THIS EXPLANATION
THE ROOM
CDA·223 Computing, Data & AI 7 MIN · 8 STATIONS

The distributed monolith

A Socratic walk-through of the distributed monolith — reasoned out one step at a time, not lectured.

abcdefgh
a

The question we started with

THE QUESTION #

Why can a system split into services still have to be deployed all at once?

A team spends a year splitting one application into twenty services. Separate repositories, separate containers, separate pipelines. Every structural marker of a microservice architecture is present and can be pointed at in a diagram.

Then a release goes out, and it goes out as twenty simultaneous deployments in a fixed order, gated by a meeting. Roll one back and you must roll back the others. The team is doing more work to ship than they were doing before the split — because now the release is distributed too. Nothing on the diagram is wrong. So what did the diagram fail to show?

b

Reasoning it through

REASONING #

Begin by asking what independence actually consists of, because the diagram shows only separation. Separation is a property of the artifacts: distinct processes, distinct pipelines. Independence is a property of change: can this thing be modified and released while everything else stays as it is? Those come apart easily, and nothing about drawing a box around a process makes the second one true.

So look for the mechanism that couples releases. It is always the same shape: a change to A requires a simultaneous change to B, so neither can go alone. Ask which arrangements produce that, and a short list emerges.

The most common is a shared database schema. Two services reading the same table have merged their models regardless of the diagram — an added column is safe, but a renamed or dropped one breaks the other service at the instant it lands, so the two must be released together. This is the single most reliable cause, and it usually survives a split because carving the data was harder than carving the code and got deferred.

Next, shared libraries carrying domain types. A common library holding the Order class looks like sensible reuse. But now a change to that class means every service that consumes it must be rebuilt and redeployed to keep the wire compatible — so a library that was supposed to reduce duplication has recreated the single build. Reuse of technical utilities is harmless; reuse of domain model is a shared kernel with no ceremony around it.

Third, breaking contract changes without a transition period. If a producer changes a field's meaning and expects consumers to have updated by the same second, the deployments are welded. The alternative is well known — add the new field, support both shapes, migrate consumers, remove the old one later — but it takes three releases where one feels sufficient, so it is skipped under time pressure. Notice that this is a discipline failure rather than a structural one, which is why it recurs even in well-drawn architectures.

Fourth, synchronous call chains with no tolerance for absence. If a request traverses six services and every one must be up and at the matching version, effective availability is the product of theirs, and each deploy is a brief outage downstream. Independence in the source is worthless if the runtime cannot survive one participant restarting.

Fifth, and least discussed, distributed transactional expectations. If a workflow assumes several services commit or fail together, they are coupled in a way no contract versioning can dissolve, because the coupling is semantic.

Now stand back and notice what all five have in common. Each is a place where two services must agree on something at the moment of deployment. That gives a definition sharper than any structural one: a distributed monolith is a system whose components must agree at release time, whatever their runtime topology looks like. The word "monolith" is exact — the unit of release is still the whole thing.

Which explains the bitter arithmetic. Against a real monolith you have swapped in-process calls for network calls, one transaction for none, one log for twenty, and one deploy for a choreographed twenty — every cost of distribution paid, and the coordination cost the split was supposed to remove still kept. It is the worst of the two designs, and not rare: it is the default outcome when boundaries are drawn by technical layer rather than by capability, because a layered cut guarantees every feature crosses every boundary.

Is there a test that does not depend on judgement? Yes, and it is blunt: pick one service, change something in it, and try to release it alone this afternoon. Whether you can is the answer — every other measure, repository count or lines per service, is a proxy this question makes unnecessary. The diagnostic version is to look at deployment history and ask how often two services shipped within the same hour; a strong correlation is coupling made visible in data.

c

The analogy

THE ANALOGY #
THE FIGURE

Picture an orchestra where each player has been given a soundproof booth and their own door to the street. Structurally they are completely separated — no one can even hear anyone else. But they still play from one score, and every entry is timed to a shared bar count, so no player can arrive late, leave early, or change their part without the whole ensemble rehearsing again. The booths bought nothing except the loss of being able to hear each other.

WHERE IT BREAKS DOWN

an orchestra's synchrony is the entire point of the music, whereas a software system's release synchrony is pure overhead — so the analogy captures the futility of the separation but not the fact that in software the coordination is a defect to be removed rather than the goal.

d

Clarifying the model

THE MODEL #

The misconception worth naming is that this is a problem of granularity — that the services were too big, or too small, and resizing will fix it. Size is not the variable. A system of three services, each independently releasable, is not a distributed monolith; a system of three hundred that ship together is. Splitting further while the shared schema remains simply multiplies the participants in the same coordinated release.

Two refinements. First, some deployment coupling is legitimate and temporary. A planned two-step migration where a producer and consumer move within the same window is fine as an episode; it becomes the pathology when it is the steady state — when every release, not an occasional one, is choreographed.

Second, the remedy is unglamorous and sequential: separate the data first, because shared schema is the coupling no amount of contract discipline works around; then make contracts additive; then add contract tests so a breaking change fails in the producer's own pipeline. Merging services back together is also legitimate and under-used — two services that always ship together are one service made expensive to operate.

e

A picture of it

THE PICTURE #
The distributed monolith
The distributed monolith Start at the initial dot and follow the two branches out of ChangeReady -- everything is decided there. The right-hand path reaches Live in one hop, which is what independent deployability means. The left-hand path is the trap: it collects partners, imposes an ordering, and its failure edge returns the entire set to the waiting state rather than reverting one service, so the unit of both release and rollback is the whole system. {"generator":"mermaid-svg-renderer@3.2.1","source":"../Socrates/.diagram-cache/_src/the-distributed-monolith.md","sourceIndex":1,"sourceLine":4,"sourceHash":"e289eb962c8021b8d2c9754187eed6f756ecc61cb50fd6ace385c553e5cc4817","diagramType":"stateDiagram","layoutVariant":"source","repairedDuplicateIds":[],"motion":"entrance-with-reduced-motion-fallback","presentation":"editorial","attempt":1,"viewBox":{"x":0,"y":0,"width":871,"height":1168},"qa":{"passed":true,"findings":[]}} "a developer finishes achange" "shared schema or shareddomain library touched" "contract additive, dataowned locally" "every partner servicerebuilt to match" "deployed in a fixedsequence" "all versions now agree" "any one service fails" "the whole set revertstogether" "released this afternoon,alone" ChangeReady WaitingForPartners ShipAlone CoordinatedRelease OrderedRollout Live FullRollback This branch is the distributedmonolith:separate processes, one unit ofrelease

How to readStart at the initial dot and follow the two branches out of ChangeReady — everything is decided there. The right-hand path reaches Live in one hop, which is what independent deployability means. The left-hand path is the trap: it collects partners, imposes an ordering, and its failure edge returns the entire set to the waiting state rather than reverting one service, so the unit of both release and rollback is the whole system.

f

What became clearer

WHAT CLEARED #
WHAT CLEARED

Independent deployability is a property of change, not of topology, so a system can be fully separated into services and still have exactly one unit of release. The couplings that cause it — a shared schema, a shared domain library, breaking contracts, mandatory call chains, cross-service transactions — are all the same thing wearing different clothes: a point where two services must agree at the moment of deployment.

g

Where to go next

ONWARD #
  • How consumer-driven contract testing moves breakage detection into the producer's own pipeline, and what it still cannot catch.
  • The expand-and-contract migration for splitting a shared schema without a coordinated release.
h

Key terms

TERMS #
TermWhat it means
Distributed monolitha system of separately deployed components that must nonetheless be released together, paying distribution's costs while keeping the monolith's coordination.
Independent deployabilitythe ability to build, test, and release one component without changing or coordinating with any other.
Consumer-driven contract testa test written from a consumer's expectations and run in the producer's pipeline, so a breaking change fails before release.

Every term the collection defines is gathered in the glossary.

Nearby on the shelf

4