Monolith vs Microservices
Split by team and deploy pain, not by noun. A modular monolith is usually the right first step.
The story
In 2018 Segment published an engineering post titled Goodbye Microservices. Segment sends customer data to a long list of third-party tools, which it calls destinations. Early on they gave each destination its own service, its own queue and its own repo. It sounded clean: one integration breaks, the rest carry on.
It grew to more than a hundred services. Each used a slightly different version of the shared libraries, because upgrading a hundred repos is nobody's sprint. Each needed its own scaling and alerting and its own on-call knowledge. A small team spent its time running the fleet instead of improving the product.
So they merged the destinations back into a single service in a single repo. Deploys and fixes got faster and operational load went down.
Five years later the Amazon Prime Video team wrote that moving one monitoring pipeline from distributed components into a single process cut its infrastructure cost by about 90%.
Neither post says microservices are bad. Both say the same thing: splitting a system has a price, and it's paid every day. The question is whether what you get back is worth that price for your team.
The idea: a function call becomes a network call
In a monolith, orders calling inventory is a function call. It takes nanoseconds, never gets lost, and runs inside one database transaction.
In microservices, the same call goes over the network, and the network can fail in every way it knows:
Every arrow you draw between services adds timeouts, retries, idempotency keys, a distributed transaction or saga, versioned APIs and distributed tracing. That's the bill.
What you get for it:
| You get | Worth it when… |
|---|---|
| Independent deploys | Teams keep blocking each other's releases |
| Independent scaling | One part needs 50× the compute of the rest (video encoding vs account settings) |
| Fault isolation | A crash in recommendations shouldn't take down checkout |
| Team autonomy | Several teams each own a part end to end |
| Different tech per part | One part really needs a different runtime (ML in Python, the rest in Node) |
Look at the left column. Almost everything on it is about people and deploys, not performance. A three-person team gets almost none of it.
The middle option: the modular monolith
Most of what people want from microservices is clear boundaries. You can have those without the network.
The rules that make it work:
- Modules call each other only through their public API, never by reaching into each other's internals.
- Each module owns its tables. No module joins another module's tables directly.
- Enforce both with lint rules or package boundaries, not good intentions.
If you later need to pull inventory out into its own service, the seam already exists. You swap an in-process call for a network call at one well-defined boundary instead of untangling a ball of mud. Shopify is the well-known example of running a very large business on a modular monolith.
When to split
Distributed monolith: services that must be deployed together, share a database, or call each other synchronously in long chains. You pay the full cost of microservices and get none of the benefits. It's the most common outcome of splitting too early.
How to extract safely: the strangler fig pattern. Put a router in front, move one route at a time to the new service, and delete the old code path only when traffic has fully moved.
Cheat sheet
| Situation | Choose |
|---|---|
| New product, 1–2 teams | Modular monolith |
| Teams blocking each other's releases | Extract the part with the most release contention |
| One part needs very different scaling | Extract that part only |
| Needs a different runtime (ML, video) | Separate service for that part |
| Services share one database | Not microservices yet: a distributed monolith |
| Long synchronous call chains (A→B→C→D) | Latency and failure multiply. Use async events or merge them. |
| Migrating off a monolith | Strangler fig: one route at a time behind a router |
| Rule of thumb | About one service per team, not one per noun |
Say it in the interview
"I'd start as a modular monolith: one deployable, strict module boundaries, each module owning its tables. That gives us clean seams without network calls between every part. I'd extract a service when a separate team owns it or it scales very differently, video transcoding being the obvious first candidate, using a strangler-fig route behind the gateway."
Go deeper
- SD 3 · Q47 — Why Microservices Exist: the chain of whys behind the split.
- SD 2 · Chapter 13 — Distributed Transactions: the saga and outbox patterns the arrows above need.
- API Gateway: the router the strangler fig needs.