Monoliths vs Microservices 🗿
All the technical and organizational tradeoffs and how to choose between them, or anything in between.
In the book Sapiens, historian Yuval Noah Harari spends some initial chapters explaining the transition of humans from the hunter-gatherer lifestyle to the farmer one.
In doing so, he debunks several myths about the life of prehistoric men.
Hunter-gatherers lived a simple, happy life: they had a diverse diet, famine was rare, they were physically fit, and, contrary to popular belief, they only had to work a few hours a day.
The transition to farming, instead, brought some unexpected setbacks. Farmers had to work long hours, sometimes twice as much as their ancestors did. Their health got worse because of intense work, bad diet (mainly carbs), and diseases brought on by the poor hygiene of primordial villages.
The farmer lifestyle had one main upside, though: it produced more food, which would sustain the creation of larger communities.
In other words, life sucked at pretty much everything, but hey — it was scalable now!
Does it remind you of anything?
The first farmers were pretty much the engineers of the prehistoric world. To their defense, nobody chose to make their own life worse on purpose — on paper, odds where overwhelmingly in favor of farming life. Most downsides were not obvious (e.g. diseases, diet problems) and wouldn’t be so until generations later.
Many big engineering choices are similar. Implications are far reaching, and we may fail to account for the second and third-order consequences.
One of the most divisive of such choices is about monoliths vs microservices, as a strategy for designing our systems.
This article tries to shed light into the tradeoffs, upsides and downsides of both (or anything in between), both from the technical and organizational point of view.
We will go through:
📖 Definitions — Let’s get everyone on the same page.
✏️ System Design — How is design effort different? And when is it worth it?
📈 Scalability — Do microservices scale better than monoliths? Spoiler, it’s complicated.
🚚 Productivity — Does your dev process change based on your architecture? You bet it!
🔍 Case Studies — Three real-world stories of evolving monoliths and microservices.
⚖️ How to decide — Let’s bottom-line all of this and figure out what’s the best for you.
Let’s go!
📖 Definitions
In system design, a monolith is a configuration where all functionality lives in a single service. Such a service is tested and deployed as a single unit.
With microservices, instead, features are delivered by combining many single-purpose, independent network services. Each service can be tested, deployed, and scaled in isolation.
These are of course, two extremes.
Real world architectures often live on a spectrum between them. You may have monoliths that cover all features but a few, provided by independent services, or compositions of microservices where some of them are anything but micro.
Unfortunately, it is easier to give names to extremes than to anything more nuanced — but the best solutions often lie in between.
How do people choose one vs the other? Let’s start with system design 👇
✏️ System Design
Most systems start as monoliths, because they are arguably easier to design.
In a monolith, you don’t have to necessarily think about how to factor things precisely, because all the code lives in a single place and can be referenced easily.
Microservices, instead, interact via network calls. Interaction via the network is expensive and hard to reconfigure — so you better be sure about how you split functionality.
Grug said it best:
grug wonder why big brain take hardest problem, factoring system correctly, and introduce network call too.
Design complexity makes microservices less than ideal in situations where the problem scope is unclear or fast moving — like a startup. Monoliths, instead, pay a lesser price for the wrong abstraction, because they can be more easily reconfigured.
Brendan Mulholland, CTO at Recital, describes it well:
The more complex the boundary, the harder it is to move — and the o11y, security, decoupling, etc, you describe is all complexity on top of a network call. That's also why microservices are so dangerous for a startup: if the architecture of your system (indeed, even your business domain) are still emerging, how likely are the boundaries to be correct?
Does this design tax repay itself in other ways? Let’s see 👇