What is a distributed monolith, what symptoms reveal one, and how does carelessly combining architectural styles produce it?
answer
- separate deployables, single release train
- shared schema = no boundary
- deep sync chains: latency adds, availability multiplies
- cause: split by layer not capability; code split without data split
- cure: data ownership, async across boundaries, contract tests, or merge back
basics
~20 sA distributed monolith is a system split into separate deployable services that still must be released together, share a database, and fail together. You pay the full cost of distribution - network calls, operations, debugging - without gaining independent deployment or scaling.
solid answer
~50 sThe defining symptom is loss of independent deployability: a change requires coordinated, ordered releases of several services. Supporting symptoms are a shared database schema written by several services, one user request fanning out through a deep synchronous call chain, a shared library whose version must match everywhere, business transactions spanning services, inability to test or run a service alone, and any single service outage taking the whole system down. It usually comes from splitting by technical layer or by convenience rather than by business capability, from extracting services without extracting their data, and from adding a message broker while keeping request/reply semantics everywhere so temporal coupling survives. The cure is boundary work, not tooling: align services with bounded contexts so most changes stay inside one, give each service exclusive ownership of its data, integrate asynchronously across boundaries, version contracts and verify them with consumer-driven contract tests, and measure independent deployability as an explicit architecture metric.
go deeper
Say it means services that are separate on paper but must be deployed together and share a database, so you get the cost of distribution without the benefit.
List concrete symptoms - lockstep releases, shared schema, deep synchronous chains, shared entity libraries - and name splitting by technical layer as a common cause.
Explain root causes in terms of data ownership and temporal coupling, quantify the availability and latency effect of synchronous chains, and describe the remediation path including consumer-driven contract tests and sagas.
Discuss detection and prevention at organisation scale: independent-deployability metrics, boundaries aligned to teams and bounded contexts under Conway's law, contract governance, and the willingness to recombine services when the evidence says the seam was wrong.
## Definition A **distributed monolith** is an architecture that has been physically split into multiple deployable units but remains logically one unit: the parts cannot be built, tested, deployed, scaled or failed independently. It is the worst of both worlds - the coupling of a monolith plus the network, operations and debugging burden of a distributed system. ## Diagnostic symptoms 1. **Lockstep deployment** - releasing a feature requires deploying services A, B and C in a specific order, often behind a change freeze. This is the primary test: if you cannot deploy one service on its own, you do not have services. 2. **Shared database schema** - several services read and write the same tables. Any schema change coordinates all of them, and each service's internal state is publicly writable, so invariants cannot be enforced anywhere. 3. **Deep synchronous call chains** - one user request traverses five services. Latencies add; availabilities multiply (five hops at 99.9% is roughly 99.5%); one slow hop stalls everything. 4. **A shared 'common' library that must match everywhere** - domain entities or DTOs in a library where a version bump forces a coordinated rollout is compile-time coupling wearing a distributed costume. 5. **Business transactions spanning services** - a single operation needs atomicity across services, so teams reach for distributed transactions or ad-hoc rollback code. 6. **Cannot test in isolation** - running one service's tests requires five other services or a full shared environment. 7. **Correlated failure** - when one non-critical service is down, unrelated features stop working; there is no fallback or bulkhead. 8. **Chatty cross-service traffic for one screen** - dozens of fine-grained calls indicate a boundary that cuts through a cohesive concept. ## Root causes - **Splitting by technical layer instead of business capability.** A 'controller service', 'business-logic service' and 'data service' guarantee that every feature touches all three. Slice by capability (ordering, billing, catalogue) so most changes land inside one boundary. The general principle is high cohesion inside a boundary and low coupling across it. - **Extracting services but not their data.** Code moves out, tables stay shared. Data ownership is the actual boundary; without it there is no independence. - **Adding a broker while keeping request/reply everywhere.** Publishing a message and blocking for a reply retains temporal coupling and merely adds a hop. - **Splitting on team or repository convenience** rather than on domain seams, so seams run through cohesive concepts. - **Splitting too early**, before the domain is understood, so boundaries get drawn in the wrong places and become expensive to move once distributed. - **Sharing domain models across services** through a library or a common schema, which couples every service to one team's modelling decisions. ## Why careless style-mixing produces it Each style carries prerequisites, and hybrids fail when a style is adopted for its benefits while its prerequisites are skipped. Microservices require independently owned data, versioned contracts and per-service pipelines; adopt the deployment split without the data split and you have a distributed monolith. Event-driven integration requires accepting eventual consistency and designing idempotent consumers; adopt the broker but demand immediate consistency and synchronous replies, and you added infrastructure without removing coupling. Serverless edges require integrating through published APIs; skip that and functions become extra writers to the same schema. The pattern is constant: **take the topology of a style but not its discipline.** ## Getting out - Measure the real metric: **can each service be deployed to production alone, at any time?** Track deployment coupling as a first-class number. - Fix data ownership first - split the schema, migrate to per-service data, replicate read-only copies through events where needed. - Redraw boundaries around bounded contexts by observing which changes touch which services; merge services that always change together. - Convert cross-boundary synchronous calls to events where the caller does not need an answer now; model multi-service workflows as sagas with compensation. - Version contracts and enforce them with **consumer-driven contract tests** so a provider can deploy without a big-bang integration environment. - Consider **merging back**. A well-modularised monolith is a legitimate and often better destination; recombination is a valid architectural move, not an admission of defeat.
- What single metric best tells you whether you have real services or a distributed monolith?Independent deployability: the fraction of changes that can be released to production by deploying exactly one service, with no coordinated ordering and no shared-environment integration step. If that number is low, the boundaries are wrong regardless of how many repositories, containers or brokers exist.
- Two services always change together in the same pull request. What should you do?Treat it as evidence the boundary is in the wrong place. Either merge them into one service (or one module of a modular monolith), or find the misplaced responsibility that is forcing the co-change and move it to one side. Consistently co-changing components have high cohesion with each other and low cohesion within their nominal boundaries.
- Is merging services back into a monolith an admission of failure?No. Recombination is a legitimate architectural move when boundaries were drawn before the domain was understood. A well-modularised monolith with enforced module boundaries retains most of the design benefit and removes distribution cost; you can re-extract later along seams that are now evidence-based rather than guessed.
Three chefs are moved into three separate kitchens but still share one fridge and must plate every dish at the same second. You now have the travel time, the phone calls and the coordination overhead of three kitchens, with none of the independence.
saying these in an interview costs you the question
- Believing that having many deployables or containers automatically means microservices
- Sharing one database schema across services and calling the boundary respected
- Distributing domain entities or DTOs through a common library that must be version-matched everywhere
- Adding a message broker while keeping blocking request/reply, then claiming the services are decoupled
- Splitting services by technical layer, team convenience or repository count instead of business capability
- Treating a merge back into a modular monolith as failure rather than a legitimate correction