Monolith 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 lives 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 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 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 necessarily have to 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.
Does this design tax repay itself in other ways? Let’s see 👇
📈 Scalability
Scalability is often mentioned as one of the key upsides of microservice architectures.
In my experience this is not always true. To compare apples to apples, let’s separate two qualities: short-term and long-term scalability.
1) Short-term scalability
That means resilience to load peaks.
Since microservices are faster to spawn than the full monolith, you can be more responsive at acting on spikes. However, microservices spikes also tend to be more brutal than monolith ones.
Think about that: let’s say you have 10 API endpoints, and 2 of them at some point get a 5x traffic spike. If each endpoint lives in a separate service, you have to scale the capacity of the two services 5x. That might not be trivial, especially if you need to do it fast.
Vice versa, if all the endpoints live in a single monolith, the impact on the overall load is lower and can be more easily absorbed.
There isn’t a clear winner here — it depends on your load patterns.
2) Long-term scalability
Let’s say your product is successful and traffic grows consistently over time. How much mileage do you have with a monolith vs microservices?
Again, it depends.
A monolith might be straightforward to scale vertically for a long time. For example, regular web apps can go a long way by spinning off the database on a dedicated instance, scaling that vertically, and scaling the rest horizontally with jobs and queues.
How does that compare to scaling microservices?
Since each service has a different load pattern, in theory you can tune them precisely and get to a solution that is better and cheaper. In reality, though, this is hard to pull off.
Since each service has a different CPU, memory, and load profile, each also scales in a different way. Spending more engineering time on such maintenance easily eats up, cost-wise, all the other benefits you can get.
What about serverless?
It is true that serverless microservices can be truly seamless (and cheap) to scale for various orders of magnitude. However, they also need to be very small-sized, which puts additional constraints on your design.
Generally, the more services your system is split into, the more rigid the design and the harder to reconfigure in the future.
🚚 Productivity
In engineering, how fast and often you are able to ship is a good proxy for productivity. Let’s cover how monoliths vs microservices affect this: