The Distributed Monolith Anti-Pattern
A distributed monolith is a set of services that still depend on each other so tightly they must be deployed, changed, and scaled together — so you pay the full cost of distribution (network latency, distributed data, operational overhead) while getting none of the independence microservices are supposed to buy. It is caused by boundaries drawn along the wrong lines, a shared database, and chatty synchronous calls. The fix is to correct the boundaries and break the shared data, not to add more infrastructure.
This is the part the whole series has been steering around — and toward. Every earlier warning, about cutting along the wrong lines, sharing a database, letting the gateway swell, building chatty contracts, has the same destination: the distributed monolith. It is the most common way a microservices migration fails, and the cruelest, because from the outside it looks like success. You have services. You have a service mesh. You have a deployment pipeline per service. And you have made everything worse.
What a distributed monolith is
A distributed monolith is a system that has been split into separate services that are still so tightly coupled they behave as one. They have to be deployed together. A change to one forces changes to several others. They share data. One service’s failure cascades into the rest. The system has the topology of microservices and the coupling of a monolith.
The result is the worst trade available in architecture. You have taken on every cost of distribution — network latency on calls that used to be in-process, distributed data without the database’s free consistency, the operational overhead of running many services, the cognitive load of tracing requests across them — and collected none of the benefits that were supposed to justify those costs. No independent deployment. No independent scaling. No fault isolation. No team autonomy.
The reversal worth keeping: a monolith at least gives you simplicity for your coupling; a distributed monolith charges you for the coupling and keeps the simplicity for itself.
How to recognize it
You can diagnose a distributed monolith from a short list of symptoms. The more that are true, the deeper in you are:
- You can’t deploy one service alone. Releasing service A requires releasing B and C at the same time, or in a specific order. Deploys are choreographed across services. This is the single clearest sign — independent deployability is the definition of a real service, and you don’t have it.
- One feature, many coordinated changes. A normal feature routinely touches three or four services and three or four teams, with a change to one breaking the others until they all move together.
- The services share a database. Two or more services read and write the same tables, so a schema change ripples across services and no one can migrate data alone. (Part 6 was about exactly this.)
- Failures cascade. One service going down takes others with it, because they call it synchronously and have no fallback. There is no fault isolation.
- Chatty synchronous seams. Completing one business operation fires a long chain of back-and-forth calls across service boundaries — the boundary is cutting straight through a tightly-coupled process.
If deploys still have to be coordinated across services, nothing was actually decoupled. The system was partitioned, not decomposed.
What causes it
Distributed monoliths are not caused by bad infrastructure. They are caused by bad boundaries, and they trace to a few specific mistakes this series has already named:
- Boundaries drawn along technical layers or tables instead of bounded contexts — so a single business operation is scattered across services that must collaborate constantly to complete it.
- A shared database left in place under the split, so the services are joined at the data even though the code is separate.
- Over-splitting — chasing the smallest possible services until even one coherent capability is spread across several, guaranteeing chatter between them.
- Synchronous-everywhere communication, where every interaction is a blocking call, so the services are temporally coupled — each one only works when all its dependencies are up and fast.
Notice what is not on the list: insufficient tooling. The cause is always the boundary, not the absence of a service mesh.
How to fix it (and why more tooling won’t)
The instinct, when a distributed monolith starts hurting, is to buy your way out — a service mesh, more sophisticated tracing, smarter retries, a better orchestration platform. None of it fixes the problem. Those tools manage coupling; they do not remove it. You will have a beautifully observed, exquisitely instrumented distributed monolith.
The fix is to correct the boundaries and the data:
- Merge services that always change together. If two services are never deployed apart and every feature touches both, they are one context split in the wrong place. Merging them back into a single service is a fix, not an admission of defeat. The right number of services for a tightly-coupled domain may be fewer than you have. (This is far easier said than done once data has been split — which is precisely why Part 5 argued for coarse boundaries and Part 6 for incremental data separation. Prevention is much cheaper than this cure.)
- Break the shared database. Give each service sole ownership of its data, following the incremental path from Part 6. As long as services share tables, no boundary above them is real.
- Replace chatty synchronous chains. Swap fine-grained back-and-forth calls for coarser interfaces that return what the caller needs in one trip, or for asynchronous events that remove the temporal coupling entirely so a service can do its work without all its neighbors being up.
This is genuine rework, and it is humbling. But it is the same disciplined, incremental loop the rest of the series describes — applied to fixing boundaries instead of crossing them for the first time. Each correction is a slice, proven and shipped one at a time.
Not every coupling is the anti-pattern
Some coupling between services is normal and not a defect — services in a real system do depend on each other, and a single business process will sometimes legitimately span two of them. The distributed monolith is not “any coupling at all”; it is coupling so pervasive that independence is impossible. Don’t over-correct into the opposite failure, chasing zero coupling by splitting until every service is trivial, because that produces its own chatter and its own distributed monolith from the other direction. And be honest about the most uncomfortable possibility: sometimes the right fix for a distributed monolith is to re-consolidate it into a clean modular monolith and stop there. The pendulum swinging back — Amazon’s Prime Video team folding a distributed pipeline into a monolith for a ~90% cost reduction (Amazon Prime Video Tech blog, 2023) — was, in effect, a team recognizing a distributed monolith and choosing the honest cure. Going back is not failure. Staying in the worst-of-both-worlds out of pride is.
Where this leads
The distributed monolith is the headline risk, but it is one of several, and a team going in with eyes open should see the whole field — the organizational, operational, and consistency hazards that show up alongside the architectural one. Part 9, Microservices Migration Risks & Challenges, maps the full risk landscape and the concrete ways to de-risk each one before it bites.
Frequently asked questions
- What is a distributed monolith?
- A distributed monolith is a system split into services that are still so coupled they behave like one unit — they have to be deployed together, a change in one forces changes in others, and one service's failure cascades to the rest. It has the network latency, distributed data, and operational overhead of microservices with none of the independent deployment, scaling, or fault isolation that justifies those costs. It is the worst of both architectures.
- How do you know if you have a distributed monolith?
- The clearest signs: you cannot deploy one service without deploying others at the same time; a single feature routinely requires coordinated changes across several services; the services share a database; one service going down takes others with it; and a normal business operation makes many synchronous calls back and forth across service boundaries. If deploys still have to be choreographed across services, the split did not actually decouple anything.
- How do you fix a distributed monolith?
- By correcting the boundaries and the data, not by adding infrastructure. Find the services that always change and deploy together and consider merging them back into one — that is a fix, not a defeat. Break the shared database so each service owns its data. Replace chatty synchronous call chains with coarser interfaces or asynchronous events. More service mesh and tooling will not fix coupling; only re-drawing the boundaries along real domain lines will.