About ten years ago, I wrote a pair of blog posts (here and here) on the early days of the microservices boom. The industry was rushing toward distributed systems like they were the answer to everything. My argument then: distributed systems offer real power, but the monolith has a tax too, and jumping to distributed systems too early introduces a massive operational burden you probably aren't ready to pay.
A decade later, the pendulum is swinging back.
We're seeing a wave of high-profile "un-migrations." Amazon Prime Video. Segment. Companies are reverting workloads from distributed systems back to monolithic structures to escape runaway cloud costs, crippling operational complexity, and performance problems nobody saw coming. A while back, I caught up with a friend of mine who's a CTO, and he admitted almost sheepishly, "You're going to be disappointed in me, but I'm having my org switch from a distributed architecture to a monolith."
I wasn't disappointed at all. He made the right choice for his organization.
A Monolith Can Be The Right Choice
In the right situation, a monolith is absolutely the correct call. If you're validating a market, just starting out, or your team and problem size aren't massive, a monolith gives you the speed and simplicity you need to survive. The industry has always known that the benefits of distributed systems come with liabilities. When you apply distributed systems to smaller teams or tightly cohesive problems, you end up with redundant infrastructure, painful dependency management, and a real drag on engineering velocity.
But here's where I want to pump the brakes. We can't overcorrect either. Yes, a monolith eliminates network latency and distributed tracing headaches. It also introduces an entirely different monster if you're not careful.
The Modular Monolith Sounds Great. Here's the Catch.
There's a popular push right now for the "Modular Monolith." The pitch is appealing: you get the deployment simplicity, cost efficiency, and straightforward testing of a single codebase, but with strict logical boundaries enforced in-process. You avoid the operational tax of distributed systems, while strict module boundaries keep the complexity tax from killing your engineering velocity. Genius! Why didn't we think of this before?
"Do not cite the Deep Magic to me. I was there when it was written." - Aslan, The Lion, the Witch, and the Wardrobe. (yeah, I'm old, and nerdy)
We did..
That was always the intent when working in a monolith...
This was how things were most often built in the 90s and early 2000s..
We intentionally moved away from it because it rarely works at scale or over long periods.
On paper, the modular monolith looks perfect. In practice, there's a massive catch.
When you rush to distributed systems, you pay an operational tax: network latency, distributed tracing, infrastructure overhead. But you get clearly defined boundaries and system independence. When you build an undisciplined monolith, you pay a complexity tax: entangled dependencies, slow deployments, blast-radius risks everywhere. The modular monolith tries to give you the best of both.
The problem is the human element.
The Real Enemy Is Entropy
The ultimate challenge with any monolith, even a beautifully structured modular one, is that maintaining it over time is almost impossible. "I can prevent that," you might say. "Stellar documentation. Clear guidance from technical leadership. Rigorous code reviews. Strict architecture oversight."
Sure. But what happens when organizational turnover hits?
Over years, you gradually lose institutional knowledge. The veterans who built the modules leave. They're replaced by engineers who haven't lived with the system long enough to deeply understand it. Under pressure to ship features, people start taking shortcuts. They pierce the boundaries. Slowly, the code devolves. Software entropy gains momentum month after month, until your clean modular monolith has quietly turned into a classic, tangled ball of mud.
"Fine," you say, "I'll hire strong technical leaders to enforce the rules."
But those new leaders arrive with their own experiences and opinions. They make different calls on how the architecture should be structured. Now you're facing a brutal dilemma: do you refactor the entire system to match the new vision, or do you start building messy translation layers between the old way and the new way? And what if the engineers already on the team don't agree with the new direction?
Your elegant middle ground has become a politically charged architectural mess.
Why microservices became so popular in the first place
A hard network boundary forces discipline when human discipline inevitably fails. You can't accidentally couple two classes across a network boundary during a late-night hotfix. The call has to go through a defined API, get serialized, hit the wire, and survive a version mismatch. The architecture enforces the rule so nobody has to remember it.
But that's only half the story. Distributed systems didn't just win on discipline. They won on organizational leverage. As companies scaled past the point where a small number of teams could safely ship one codebase, independent deployability became the real prize. A team could release its service on its own schedule, on its own stack, without waiting on a company-wide release train or a merge queue with forty other engineers. Conway's Law made this almost inevitable. Once you had dozens of autonomous teams, you needed an architecture that let org and service boundaries align. The network boundary wasn't just a technical guardrail; it was what let organizational and technical structures scale together.
So it wasn't one insight. It was three converging pressures.
- Enforced discipline where human process failed
- Independent deployability at team scale
- An architecture that mirrored how the org was actually organized.
The Takeaway
The last ten years have made one thing clear: software architecture should never be chosen based on industry hype cycles. Distributed Systems were never a silver bullet. The monolith was never obsolete. The modular monolith is a fantastic tool when your team is stable and aligned, but you have to be clear-eyed about the human tax that comes due as your organization grows and changes.
The goal isn't to avoid paying a tax entirely. That's not possible. The goal is to choose which tax your organization is best equipped to pay at its current size and maturity. The best engineering leaders aren't building the most theoretically perfect systems. They're designing architectures that can survive the messy, shifting reality of the humans building them.
Modular monolith vs. Distributed Systems — pros and cons
Modular monolith
Pros:
- Single deployment, single build, no network hop between modules, so there is lower operational overhead and infra costs
- Compiler/type system can enforce boundaries; refactoring across modules is a local, atomic change
- Easier to debug. One process, one stack trace, no distributed tracing required
- Faster to build early on; no premature investment in service mesh, orchestration, or cross-service contracts
- Transactions across modules stay simple (no sagas, no eventual consistency headaches)
Cons:
- Boundaries are a social contract, not a physical one. They erode under deadline pressure, team turnover, or lack of review discipline
- One team's bug or bad deploy can take down the whole system
- Can't scale or deploy modules independently. Every release ships the whole monolith
- Different modules can't easily use different tech stacks or scale resources independently
- As team count grows, everyone competing for the same codebase creates merge and coordination friction
Distributed Systems
Pros:
- Boundaries are physically enforced by the network. It is much harder to accidentally violate
- Independent deployability. Teams ship on their own cadence without blocking others
- Independent scaling. This lets you put more resources only where the load actually is
- Teams can choose the best stack per service; failure in one service doesn't have to take down others
- Org structure and system structure can scale together (Conway's Law working for you instead of against you)
Cons:
- Real operational tax: service discovery, orchestration, observability, distributed tracing, network reliability
- Distributed transactions and eventual consistency are genuinely hard problems
- Latency and failure modes multiply. A single user request may hop through many services
- Higher infra cost and cognitive overhead, especially below a certain team/org size
- Easy to over-fragment into a "distributed monolith". All the coupling of a monolith with none of the deployment benefits
The honest takeaway most teams land on: modular monolith is usually the right starting architecture, and the migration point to distributed systems tends to track organizational scale (multiple independent teams, need for independent release cycles) more than raw traffic or code size.