Chapter 05
Monolith or Microservices?
The architecture choice every growing product faces — and what each one actually costs you.
The architecture debate that became a religious war
Picture this: a sprint planning. An engineer says "this is the third feature blocked by the checkout team's deploy schedule — should we just split this out into its own service?" The room nods. Six months later you have two services, two deploy pipelines, two on-call rotations, and the original problem — features waiting on each other — has just moved one floor up.
For most of the 2010s, "monolith vs microservices" was the loudest debate in software architecture. Conference talks, blog posts, hiring profiles. The debate has cooled, but the underlying tradeoff hasn't gone away — it just shows up under different names: "should we split this out?", "why is the team blocked on each other's deploys?", "can we move faster if we break this apart?"
The honest answer to "which one?" is: it depends on the size of your team, the shape of your load, and how much operational maturity you already have. There is no universal default. Most of the bad architecture decisions of the last decade came from teams treating one option as a default and not asking the depends-on-what questions.
What a monolith actually is (and why it's underrated)
A monolith — meaning one codebase, one deployable artifact, one type of running process (you can run many copies behind a load balancer). When you ship code, the whole thing ships together. When something goes wrong, you can follow the request from the front door to the database in a single debugger.
For most products, for most of their early life, this is the correct answer. It's fast to build in, easy to reason about, easy to refactor across, and dramatically simpler to operate. Many wildly successful products have run as monoliths for years — Stack Overflow famously ran on a small handful of servers for a long time, and plenty of nine-figure businesses still do.
PM Insight
"Monolith" is not "spaghetti." A well-structured monolith with clear internal modules is a serious, professional system. The mistake is conflating "single deployable" with "messy code" — they're independent variables.
What microservices actually solves (and what it costs)
Microservices — meaning splitting your codebase into many small, independently-deployable services, each owned by a small team, each communicating with the others over the network. The classic case for it: independent teams shipping at independent cadences without stepping on each other.
What it solves, when it works
- Team independence at scale. If you have 200 engineers, you do not want them all merging into one codebase. Splitting along service lines lets teams own their piece end-to-end.
- Independent deploy cadence. The payments team can ship 10× a day; the email team can ship once a week; nobody waits for anyone else.
- Fault isolation. If the recommendations service goes down, the rest of the product can keep running (in theory).
- Different tech per service. The ML team can use Python; the checkout team can use Go; nobody has to standardize on one stack.
What it costs, always
- Operational complexity. You now have N services to deploy, monitor, log, secure, and version. The total surface area for things that can break grows fast.
- Network unreliability. Every internal call can fail, time out, or be slow. Code that used to be a function call is now a remote call with retries and circuit breakers and timeouts. This adds real complexity to feature work.
- Distributed debugging. "Why was this request slow?" used to mean reading one stack trace. Now it means stitching together logs from five services across three regions, hopefully with proper request IDs.
- Distributed data is genuinely hard. Keeping data consistent when no single service owns the whole picture is a category of problem most teams underestimate by an order of magnitude.
- Organizational overhead. Microservices need DevOps maturity, observability infrastructure, and platform investment. A 15-person team trying to run 30 microservices is mostly running infrastructure, not building product.
The biggest, most consistently underestimated cost of microservices is organizational, not technical. They demand a level of platform investment that small teams cannot afford to maintain.
The middle option: the modular monolith
The architecture pendulum has swung back over the last few years toward a hybrid that captures most of the upside of both: the modular monolith — one deployable unit, internally structured with strong module boundaries, as if it were many services that happen to be in the same process.
You get most of the code-quality benefits of microservices (clear ownership, decoupled modules, well-defined interfaces) without the operational tax (no network between modules, no distributed debugging, one deploy). And critically, if you later need to extract a module into its own service, the boundaries you've already drawn make that extraction cheap.
The modern default for under-50-engineer teams
Start as a modular monolith. Extract services later, only when you have a specific, concrete reason to — not on a vibe, not on a conference talk, not because microservices feel grown-up.
How the answer changes by stage
Architecture is mostly a stage decision. Team size matters, but only because it reveals the deeper constraint: are you trying to learn, trying to stop a growing system from hurting you, or trying to coordinate many teams without turning every release into a negotiation?
1. Finding fit: keep one thing shippable
At finding fit, the right answer is almost always a monolith. You need fewer deploy paths, fewer ownership boundaries, fewer debugging surfaces, and fewer ways for a tiny team to spend its week operating infrastructure instead of learning from customers.
This is true even when the product feels ambitious. A payments flow, an admin panel, and a notification system can live in one codebase for a long time. The thing that kills you early is not "insufficiently grown-up architecture." It's making five engineers behave like a platform org.
2. Scaling reliability: modular monolith first
At scaling reliability, the monolith starts to hurt in real ways. Deploys get slower. Ownership gets muddy. One team's change breaks another team's flow. This is where teams reach for microservices too early.
The better default is the modular monolith: one deployable system with clear internal boundaries. Draw the payments boundary. Draw the catalog boundary. Draw the notification boundary. Make ownership visible before you pay the operational tax of network calls, distributed traces, service discovery, and multi-service incidents.
3. Operating at scale: split when ownership needs it
At operating at scale, microservices become less optional. The reason is usually organizational before it's technical: teams need to deploy independently, scale parts of the system separately, own different reliability targets, and make changes without a weekly release train becoming the company bottleneck.
Performance can matter here, but it is rarely the first reason. Most products never reach a load shape where microservices are required just to serve traffic. The stronger argument is independent ownership with enough platform maturity to support it.
4. The transition trap
The dangerous moment is the middle. A 25-engineer team with a 40-minute deploy can convince itself it has an architecture problem. It may have a pipeline problem. Fix the cheap thing first. Microservices are the most expensive way to discover your CI system was the bottleneck.
A useful question to bring to any architecture conversation: what specific problem are we solving by changing the architecture, and what evidence do we have that the change will solve it? If the answer is hand-wavy, the architecture isn't the actual problem.
AI doesn't change this calculus much. It can help engineers write either kind of system, and can take some of the boilerplate edge off microservices in particular, but it can't tell you which stage you're in. That call is still a human judgment about people, scale, and operational maturity — and it will be for a long time.
PM Playbook — Questions to ask
The next time someone proposes splitting a service out (or the inverse), try these:
- What specific problem are we solving by changing the architecture, and what evidence do we have it'll solve it? — the chapter's central diagnostic
- How many engineers do we have today, and how many in 18 months? — the team-size signal
- Do teams genuinely need to deploy independently, or is the pipeline just slow? — the most common misdiagnosis
- Have we considered modular monolith first? — forces the middle option onto the table
- What's our DevOps and observability maturity today? — the hidden organizational tax
- What's the cost — and the timeline — of switching back if we're wrong? — reversibility check
Six honest questions like these will keep your team out of the most expensive architecture mistake of the last decade — splitting services because microservices feel grown-up, then spending the next year running infrastructure instead of shipping product.