Module 11 · Architectural patterns · Lesson 2 of 9
Monoliths
One deployable unit with all the features inside. Why it is the right starting point for most products, how far it scales (further than its reputation), the modular monolith that keeps internal boundaries clean.
Why it matters
The industry spent a decade treating monoliths as a mistake to be corrected and then spent several years correcting the correction. A monolith is the simplest architecture that can run a real business, and most successful products started as one and many remain one at considerable scale. Knowing how to build a good monolith, and recognising the actual signals that it is time to split, is more valuable than knowing microservices patterns you may never need.
The idea
A monolith is an application built and deployed as a single unit: one codebase, one build, one process (replicated across servers), one database. All features share memory, a transaction boundary and a release. The simplicity is the point: function calls instead of network calls, one schema with joins and transactions across everything, one thing to deploy, debug and monitor.
What it gets right
- 1
Transactions and joins across the whole domain
Creating an order, decrementing stock and recording a ledger entry is one database transaction; the entire Distribution stage of hard problems does not exist yet. - 2
Refactoring freedom
Moving code between modules is a rename, not a cross-team API negotiation. Boundaries can be discovered rather than guessed up front. - 3
Operational simplicity
One deploy pipeline, one set of logs, one process to profile, one on-call surface. A stack trace shows the whole request. - 4
Performance
In-process calls cost nanoseconds; a request that would cross ten services crosses ten functions.
How far it scales
Horizontally, a stateless monolith scales like any application tier: run more copies behind a load balancer, which works until the database is the bottleneck, and the Scaling stage's tools (indexes, caching, replicas, partitioning) push that bottleneck a long way. Shopify, Stack Overflow, Basecamp and GitHub have run or run large monoliths serving enormous traffic. The limits that bite are organisational and operational before they are technical.
The modular monolith
The failure mode of monoliths is not size but the big ball of mud: every module reaching into every other's tables and internals until nothing can change safely. A modular monolith keeps one deployable but enforces internal boundaries: each module owns its tables and exposes an interface, cross-module calls go through that interface (enforced by package visibility or linting), and modules communicate through in-process events where coupling should be loose. The result keeps all the monolith's advantages and makes a later split, if ever needed, a matter of moving a module behind a network boundary it already has.
The real reasons to split
| Pressure | What it looks like | Does splitting help? |
|---|---|---|
| Team scale | Dozens of engineers blocked on one release train; merge conflicts; fear of deploying | Yes: independent deploys per team are the strongest argument for services |
| Divergent scaling or runtime needs | One module needs GPUs or 50 times the instances, or a different language | Yes: extract that module |
| Fault isolation | A memory leak in reporting takes down checkout | Partly: bulkheads and separate processes help; services make it explicit |
| Security or compliance boundary | Payment code must be isolated for PCI scope | Yes: a clear boundary reduces audit scope |
| "It is slow" | Latency or throughput problems | Usually no: network hops add latency; fix the database and caching first |
| "Microservices are modern" | No measurable pressure | No |
Splitting adds every problem in the Distribution stage: network failures, distributed transactions, data duplication, observability across processes. It is worth paying when the pressures above are real and measured, and wasteful before.
Trade-offs
A monolith buys simplicity, transactional integrity, refactoring freedom and performance, and pays with a single release unit that couples teams, uniform scaling and runtime, and a blast radius that spans features. A modular monolith keeps the first list and softens the second. The honest default for a new product is a modular monolith until a specific pressure justifies extracting a service.
In an interview
Start there and say when you would leave: "A modular monolith with clear module boundaries and one Postgres, scaled horizontally behind a load balancer. We extract a service when a module needs independent deploys because of team size, a different runtime, or a compliance boundary; not for fashion, because each extraction adds network failure and data consistency work." Interviewers increasingly reward this answer over a reflexive microservices diagram.
Check yourself
A five-person startup proposes launching with twelve microservices 'so it scales later'. What is the strongest counter-argument?