الدرس 1 من 2
A Monolith Is a Deployment Model, Not a Quality Judgement
One unit built, released and run together. That says nothing about whether the inside is well organised — and confusing the two is the most expensive mistake in this chapter.
A monolith is a system built, deployed and run as a single unit. That is the entire definition, and it is a statement about packaging. It says nothing about whether the code inside is a tangle or a clean set of modules, which is why "monolith" and "big ball of mud" being used as synonyms causes so much damage: teams conclude that their tangle requires distribution to fix, and end up with a tangle spread across a network.
| Deployment shape | Well-structured inside | Tangled inside |
|---|---|---|
| One deployable | Modular monolith — a legitimate end state for most systems | Big ball of mud — the common complaint, fixable without distributing |
| Many deployables | Microservices — independence bought with real operational cost | Distributed monolith — every cost, no benefit, hardest to escape |
What one deployable actually gives you
It is easy to list what monoliths lack. It is more useful to list what they provide, because every item here is something a distributed system has to rebuild deliberately, at cost, and usually less well.
- **Atomic transactions across the whole domain.** Any set of changes can commit or roll back together with no sagas, no compensating actions and no eventual consistency to explain to the business.
- **Refactoring across boundaries in one commit.** Moving a responsibility from one module to another is a rename, verified by the compiler, not a coordinated release of two services with a deprecation period.
- **One place to look during an incident.** A single log stream, one process, one stack trace that reaches from the request to the query.
- **No partial failure between components.** A function call cannot time out, arrive twice, or succeed on one side of a network and fail on the other.
- **One thing to secure, configure and upgrade.** One set of credentials, one runtime version, one dependency audit, one deployment to patch.
- **Straightforward local development.** The whole system runs on a laptop, which shortens every feedback loop the team has.
The same call, before and after distribution
Everything in the right-hand column is work that did not exist when this was a function call.
On the left, an order module calls an inventory module in process: one stack, one transaction, failures are exceptions. On the right, the same call over a network requires serialisation, a timeout, a retry policy, idempotency, a circuit breaker, distributed tracing and a compensating action when the caller later fails.
In-process call
orders → inventory
one stack frame
One transaction
commit or roll back
Failure = exception
immediate and local
Network call
Serialisation and schema
versioned contract
Timeout, retry, idempotency
at-least-once delivery
Circuit breaker, tracing
new operational surface
Compensation
no shared transaction
In practice
Fixing the tangle instead of splitting it
A team of fourteen maintained a five-year-old monolith. Deployments took 50 minutes, one in three releases had to be rolled back, and any change risked breaking something unrelated. Leadership approved a two-year microservices programme.
Constraints
- Fourteen engineers in two teams
- No independent scaling requirement — the system runs on four machines
- Roughly 4,000 files with no enforced internal boundaries
- Two-year programme already funded
Decision
Spend one quarter on three things instead: enforce module boundaries in the build, cut the deployment pipeline from 50 to 6 minutes, and adopt feature flags with trunk-based development. Revisit the microservices decision afterwards with measurements.
Why
Every symptom described was a symptom of a tangle and a slow pipeline, not of single-unit deployment. Rollback rate was driven by hidden coupling between modules, which distribution would have converted into runtime failures rather than removing. None of the properties microservices buy — independent scaling, independent release, isolated failure domains — was on the list of complaints.
What it cost
The quarter delivered no features, which was politically expensive and had to be defended repeatedly. Rollback rate fell to about one in twenty and deployment frequency rose from weekly to several times a day. Eighteen months later they extracted exactly one service — a bursty document-rendering component with a genuinely different resource profile — and the rest of the programme was cancelled. The team had spent one quarter to avoid two years.
Leadership asks "why are we not on microservices?"
As a developer
Explains that the team is too small and lacks the operational experience. Accurate and defensive, and it frames the monolith as a limitation the team has not yet outgrown.
As an architect
Answers in terms of what is being bought. Microservices purchase independent deployment, independent scaling and isolated failure domains, and charge for them in operational complexity, eventual consistency and coordination overhead. Here is which of those three we need today, here is what we currently pay, and here are the two measurements — release blocking incidents and per-component load profile — that will tell us when the trade changes. That reframes the conversation from maturity to economics, which is the only ground on which it can actually be settled.
A team says their monolith "cannot scale". What should you ask?RevealHide
Which resource is exhausted, and for which part of the system. Monoliths scale horizontally perfectly well if they are stateless — many organisations run hundreds of instances of one deployable behind a load balancer. Three quite different problems hide behind that sentence. If the database is the bottleneck, splitting the application changes nothing, because the same queries hit the same database. If one component consumes resources out of proportion to the rest — video encoding, report generation — that is a genuine argument for extracting *that component*, not for splitting everything. And if the application holds session state in memory, the fix is to stop doing that, which is a day of work rather than a migration. "Cannot scale" is almost never about the deployment model on its own.
Key takeaways
- A monolith is a packaging decision; a big ball of mud is an internal quality problem. They are independent.
- Distribution converts compile-time coupling into runtime coupling; it does not remove it.
- One deployable buys atomic transactions, cross-boundary refactoring, one incident surface and simple local development.
- Those are quality attributes with names, and the choice is a trade among them rather than a question of fashion.
- "Cannot scale" usually means the database, one component, or in-memory state — none of which distribution fixes by itself.