Microservices vs Monolith: Complexity and Mitigation

There are 2 ways to look at complexity; Inherent complexity and what I'll call "aggregate" complexity.

Inherent (or perhaps "initial") complexity is rather simple to define. It is how complicated a solution is up front. How complex it is "necessarily". Aggregate complexity on the other hand is how complicated a solution becomes over time. Neither are particularly easy to measure however, and therein lies the problem. 

I watched a Tim Corey video the other night on EF vs. Dapper. In it, he made a point which sort of stuck with me; Dapper doesn't hide the underlying DBMS so, developers using Dapper tend to get a better understanding of the persistence layer of their application. As a result, they tend to be better equipped to deal with issues in that layer.

I would fire back and suggest his data may be skewed. I suspect that a large base of Dapper users are also those with more pre-existing database understanding rather than people who were made better developers by virtue of choosing one over the other. 

But, there is nonetheless a valid point in there somewhere. I also thought back over a lot of problems I've seen or been a part of over the years. And it has its roots in why I recommend architecting with microservices in mind, even if you think you will build and stick with a monolith.

Dapper is inherently more complicated than EF. If you start both with zero knowledge, you have to learn both Dapper and some amount of the underlying DBMS you want to put it on top of. EF you need much less DB specific knowledge. 

But, EF tends to come with more aggregate complexity. Which is to say, while it allows you to get started more quickly, when things go wrong, and they eventually will (or you should at least that assume they will), then you will then be burdened with an even bigger complexity. Now you'll need both a better understanding of the underlying database and EF on a much more granular scale. 

If you take Entity Framework *BECAUSE* it is easier and do not bother with the depth of learning/training then it will *BECOME* harder to deal with later. 

As with my other topics; if you're skilled developer on a skilled team and you understand the tools thoroughly enough... then why are you here reading this? You wouldn't take this advice even if it did apply to you, and it perhaps it doesn't. 

While this wasn't the exact point Tim was making, it IS the a point that I make on the microservices front. I assume that if you're looking for guidance that you don't fully understand the domain of the problem and/or the scope of where the project will go. So, I assume (as you should) that it will become something bigger and more complex than planned. 

This, in turn, will undoubtedly drive you to investigate microservices if you went monolith or other possible broad architecture changes. But the aggregate complexity of pivoting architectures by the time things start to go sideways is often much bigger than any organization can deal with. And they instead fall into a cycle of rapid-fire maintenance cycles.

By starting with a multi-pronged approach you're biting off as a TON of inherent complexity. I won't lie. But, you're also hedging your bets against a world of aggregate complexity later. The more architectures and approaches you understand up front, the more you know how to integrate them, the less guess work and foundational work you have when things go sideways.

However, I want to add one more feather in the cap of microservices; mitigation.

When things go sideways, they almost always go sideways in very acute ways. One or two areas of the system are bringing everything down. When you are all in on a monolith there is no separating the festering wound from the rest of the healthy body of your app.

If you already have a microservices or hybrid architecture there are 3 possible scenarios:

  1. You got lucky and anticipated this and the problem code IS the entirety of a single microservice. In this case you can maybe scale resources for that one service while you work on things and you can address the code in isolation as well.
  2. You had a hybrid architecture, but this code was in the more monolithic part. In this case, you already have a microservices architecture. You have patterns you can follow. If it hurts bad enough you can amputate the service into a new microservice. And depending on where the issues are you may even be able to then use the same sorts of remedies.
  3. You have a hybrid architecture and the problem is a part of another Microservice. The situation here is probably the same or easier than #2. 
Aside from #1 I'm not suggesting that the others are GOOD solutions. When things go sideways there often aren't nice, elegant answers. BUT you already know how to make your existing services talk to a microservice because you already have that architecture. You already know how to deploy and so on. You have a blueprint for that alternate solution. It isn't a gigantic risk filled enigma. Developers won't have heart attacks playing "estimation poker" or give you GTFO quotes. 

In a purely monolithic arch though, these are risk factors which would be seen as INSANE to try and tackle in the midst of a crisis. "The whole world is on fire, maybe now we should start investigating the virtues of interstellar travel?".

And THAT is the essence of aggregate complexity. With a single solution, the only solutions which aren't massive, high-risk unknowns are the ones you're already using. The very ones which are currently not serving your best interests.

By separating something like authentication, localization or settings into a microservice you don't commit to a potential Amazon scale fiasco of making every call in a process involving dozens of steps serverless function, while also ensuring that you have at least one Ace in the hat later.

VERY few problems in computer science which are profitable are also minimally complex enough that there is one best answer.

Stop forgetting; There is no silver bullet. If you put all of your eggs in one basket, you'll soon discover that you weren't able to stop the monster in your code.

Comments

Popular Posts