Distributed monolith anti-pattern
Understand why microservices that share a database are worse than a monolith, how to detect a distributed monolith, and how to fix service boundaries without a rewrite.
TL;DR
- A distributed monolith has the operational complexity of microservices (network calls, partial failures, versioning) but the tight coupling of a monolith (shared database, shared schema, lock-step deployments).
- It is the worst of both worlds: you cannot deploy services independently because a schema change in one breaks another, yet you still pay the network overhead of inter-service communication.
- The signature: services that point at the same database tables, or services that must be deployed in a specific order.
- The fix requires splitting ownership of data first. Each service owns its tables. This is harder and more disruptive than any code change.
The Problem
Your team decomposed the monolith into eight services over six months. Deployments are still coordinated events requiring a 4-hour Saturday night window. A schema change in the Users table requires touching the User, Auth, Notification, and Analytics services. Service B can't start until Service A has finished its migration because they both write to the same orders table.
You split the code into services but never split ownership of data. Every service still reads and writes the same shared Postgres database. The database is your secret monolith, the part you didn't decompose.
I've seen this happen on three different teams. Each time the conversation was the same: "We have microservices." No, you have a monolith with extra network hops.
This is common because database decomposition is the hard part. Code decomposition is refactoring. Data decomposition requires migrating tables, duplicating data, accepting eventual consistency, and changing all the places that relied on ACID transactions spanning service boundaries.
Every service reaches into every table. A column rename on users breaks four services simultaneously. That is the distributed monolith in one picture.
What deployment coupling looks like
Here's the deployment ceremony for a simple feature: adding a preferred_name column to the users table.
Adding a single column requires four coordinated deployments. In a properly decomposed system, only the User Service would need to change.
Why It Happens
Nobody sets out to build a distributed monolith. It emerges from a series of individually reasonable decisions.
"We'll share the database for now." The team plans to split data later. Later never comes because the codebase grows around the shared access pattern.
"It's faster to query directly." Building an API for internal data feels like overhead when the table is right there. Every shortcut compounds into coupling.
"We followed the microservices playbook." Most tutorials focus on code decomposition (separate repos, separate containers) and barely mention data ownership. Teams split code because that's what the guides show.
"The domain boundaries aren't clear yet." When you don't know which service owns which data, sharing a database feels like the safe default. It isn't. It's the default that locks you in.
I've been guilty of the "just query the table directly" shortcut myself. It saves a day in week one and costs a quarter in year two.
The coupling progression
Distributed monoliths don't appear overnight. They grow through a predictable progression:
- Week 1: Service B needs user data. "Let's just query the users table directly for now."
- Month 2: Service C also needs user data. "Service B already does it this way, so we'll do the same."
- Month 6: Five services query the users table. Nobody remembers which columns each service actually needs.
- Year 1: Someone renames a column. Three services break in production. The post-mortem reveals nobody knew about the cross-service dependencies.
- Year 2: The team wants to shard the users table for performance. They can't, because five services have hardcoded queries against the table schema.
Each step was reasonable in isolation. The compound effect is a system that's harder to change than the monolith it replaced.
How to Detect It
Ask these questions about your "microservices" architecture:
Continue Reading with Premium
Unlock this article and every other in-depth system design guide on the platform with NotesFromSDE Premium.