If you remember five things
The whole position in one glance. Everything below defends these five calls.
- Default to the monolith. One deployable, one database, ACID transactions. You split later for a reason, not because many boxes look impressive.
- Own-database-per-service is the real line. Not the count of services. Five services sharing one DB is a monolith in a costume, all pain, no payoff.
- Microservices trade one problem for another. You swap the pain of a big shared codebase for the pain of a distributed system. The network is the new failure surface, and it lies.
- The two winning triggers are people and scaling. Split when teams block each other on deploys, or when one piece has genuinely different scaling or hardware needs.
- Consistency is where candidates get burned. The moment checkout and inventory are two services, you lose the shared transaction and owe sagas, idempotency keys, and an outbox.
1The core idea#
Two shapes for the same application. Know exactly what each one is before you argue for either.
A monolith is one deployable unit.
- All your code (checkout, user accounts, product catalog, the email sender) lives in a single codebase.
- It ships as a single process, or a set of identical copies of that same process behind a load balancer.
- When you deploy, you deploy the whole thing.
Microservices split that same application into many small, independently deployable services.
- Checkout is its own service with its own database. Users is another. Catalog is another.
- Each team owns its service end to end.
- Services talk over the network, usually HTTP or a message queue.
The mental model I use:
- A monolith is one big house where everyone shares the kitchen, the plumbing, and the front door.
- Microservices are a street of separate houses, each with its own plumbing, that have to phone each other to coordinate dinner.
Microservices are not "the advanced version" of a monolith. They are a different set of problems. You trade the pain of a large shared codebase for the pain of a distributed system. Both are real. One of them involves the network, and the network is a liar.
2How it works#
Same checkout flow, two completely different failure stories. This is the heart of the trade.
In a monolith, a function calling another function is just a function call.
- Checkout calls
reserveInventory()directly, same process, same memory. - It is fast: roughly a 5ns in-process call versus a 1ms to 5ms network round trip, about a million times faster.
- It either works or throws, no middle state.
- You wrap the whole thing in one database transaction. Payment succeeds but inventory fails? Roll back both. Clean.
Scaling a monolith is "run more copies behind the load balancer."
- All copies hit the same database.
- Simple to reason about, right up until the database becomes the bottleneck.
In microservices, that same checkout flow turns into network calls.
- Checkout service makes an HTTP request to inventory service.
- Inventory has its own database that checkout cannot touch directly.
Two things that diagram is telling you.
- Each service has its own database. This is the rule that defines microservices. If five services share one database, you have a monolith wearing a costume: all the pain, none of the benefit.
- That call from checkout to inventory can now fail in ways a function call never could. It can time out. It can succeed but the response gets lost on the way back, so checkout thinks it failed, retries, and now you double-decrement stock. Inventory can be mid-deploy.
So you pay the distributed-systems tax.
- Add retries, timeouts, circuit breakers.
- Lose your single database transaction.
- Keeping checkout and inventory consistent is now a distributed problem you solve with sagas or outbox patterns, a whole topic on its own.
3Trade-offs#
Here is the decision laid out honestly. Nothing here is free.
| Dimension | Monolith | Microservices |
|---|---|---|
| Initial speed to build | Fast. One repo, one deploy, run it locally in one command | Slow. Service boundaries, networking, CI per service |
| Local development | Clone and run | Hard. You may need 6 services running to test one flow |
| Deploy | One artifact, all or nothing | Each service ships on its own schedule |
| Blast radius of a bug | A bad deploy can take down everything | A bad deploy ideally takes down one service |
| Scaling | Scale the whole app even if only search is hot | Scale only the hot service |
| Data consistency | Easy. One DB, ACID transactions | Hard. Distributed transactions, eventual consistency |
| Debugging a request | Read one stack trace | Trace a request across 8 services and 3 queues |
| Team autonomy | Teams step on each other in one codebase | Each team owns and ships independently |
| Operational cost | Low. One thing to monitor | High. Service mesh, tracing, per-service dashboards |
The honest one-line summary:
- A monolith optimizes for a small team shipping fast.
- Microservices optimize for many teams shipping independently at large scale, and you pay a steep operational tax for that.
A worked example with real numbers#
This is the one place you win points with arithmetic instead of adjectives. So do the arithmetic.
Set the scene.
- Your app is a monolith serving 10,000 requests per second.
- One copy (a single pod) handles about 1,000 req/s, so you run 10 copies with a little headroom.
- The traffic mix matters: 1,000 of those req/s hit a CPU-hungry image-processing endpoint, and the other 9,000 are light CRUD (product pages, cart reads) that cost almost nothing.
The monolith is forced to overscale.
- You cannot scale the image endpoint on its own, because it is baked into the same process as everything else.
- The CPU-heavy image work sets the pace, so to keep p99 healthy you end up running 20 copies.
- Nineteen of them are mostly carrying image capacity for traffic that is not image processing.
- At, say, $100 per pod per month, that is $2,000/month, and most of it is wasted.
Split the image work and the math flips.
- Image service needs enough for 1,000 req/s of heavy work: call it 15 copies.
- Everything else needs enough for 9,000 req/s of light work: 3 copies is plenty.
- That is 18 pods total, but now each group scales on its own signal.
- Image-heavy campaign spikes to 2,000 req/s? Add 15 image pods, touch nothing else. In the monolith you would have doubled all 20 copies to 40 to absorb the same spike.
Stay honest about the cost side.
- You now run two deploy pipelines, two sets of dashboards, and a network hop between them that did not exist before.
The consistency trade-off#
This is where candidates get burned, so name it before the interviewer makes you.
- In a monolith, "charge the card and decrement stock" is one transaction.
- In microservices those are two services, two databases, two network calls, no shared transaction.
- Charge succeeds and the stock update fails? You now owe the customer a refund, or you oversold the item.
- Solving this (sagas, idempotency keys, an outbox table) is genuinely harder than anything in a monolith.
When a candidate volunteers this consistency trade-off before I ask, they jump a tier in my notes. It tells me they have actually felt the moment a single transaction turns into a saga, not just read the word on a slide. Say it out loud early.
4Where it is used#
History is blunt here: nearly every successful company you can name started as a monolith.
- They built a single large application.
- They grew to hundreds of engineers who kept colliding in that one codebase.
- Only then did they peel off services.
- The order matters: they split because they hit real, measurable pain, not because microservices were fashionable.
Reach for a monolith when:
- You are a startup or small team, say under 20 engineers. You do not have the headcount to run a dozen services. Rough rule: if you cannot staff at least one owner per service, you have too many services.
- Your product is still finding its shape. Boundaries you draw today will be wrong in three months. Moving a boundary inside one codebase is a refactor. Moving it across services is a migration project.
- You want to ship this quarter.
Reach for microservices when:
- You have many teams and they are blocking each other on deploys. This is the real trigger. When "we can't ship because another team's change broke the build" happens weekly, independent deploys start paying for themselves.
- Parts of the system have genuinely different scaling or hardware needs. A GPU-heavy ML model and a lightweight CRUD API do not belong in the same deployable, the way the image service above did not belong with the product pages.
- Different pieces have different reliability requirements. You want payments isolated so a bug in the recommendations feature cannot take down checkout.
Reveal the answer
A middle path worth naming: the modular monolith.
- One deployable, but with strict internal module boundaries, so catalog code cannot reach into checkout's tables.
- You get monolith simplicity now and clean seams to split later if you ever need to.
- For most teams this is the right answer for a long time, and saying so reads as maturity, not indecision.
5Common pitfalls and gotchas#
The failure modes that tank real systems, and the ones interviewers love to bait you into.
The distributed monolith.
- You split into services but they share a database, or every request fans out to five synchronous calls to other services.
- A change to one service now forces changes to others, you still cannot deploy independently, and you added network latency and failure for nothing.
- This is the worst of both worlds, and it is extremely common.
Splitting too early.
- A three-person team with eight microservices spends its time on YAML, service discovery, and tracing instead of the product.
- You cannot find good service boundaries before you understand the domain, and you understand the domain by building it.
- Premature splitting locks in the wrong boundaries in the most expensive possible way.
Forgetting the network is unreliable.
- In a monolith, a call to another module does not time out. Across services it can, and will, at the worst moment.
- Every synchronous call needs a timeout and a plan for failure.
- A missing timeout is how one slow service cascades into a full outage: callers pile up waiting, run out of threads, and go down too.
Chatty communication.
- Rendering one page triggers 30 service-to-service calls, each a network round trip.
- Thirty calls at 5ms each is 150ms of pure network waiting before any real work, and that is the best case where every call succeeds first try.
- Monoliths do not have this problem, because those were function calls at a few nanoseconds each.
Shared data models.
- Every service imports the same
Userobject, so a change to it forces every service to redeploy in lockstep. - You have recreated tight coupling over the network, which is strictly worse than tight coupling in a single process.
Opening "design a system" by drawing a user service, an order service, a payment service, and a notification service before anyone has agreed on a single requirement. That pattern-matches buzzwords, not engineering, and it is the fastest way to tell me you have never operated a distributed system under load. Start with one service and earn each split.
6How it shows up in interviews#
Interviewers probe two things: whether you reach for complexity by default, and whether you understand what distribution actually costs.
The opening move. When asked to design something, do not open with microservices.
- Weak: "I'll use a microservices architecture. We'll have a user service, an order service, a payment service, a notification service..." (drawing boxes before anyone agreed on a requirement). This says you pattern-match to buzzwords.
- Strong: "I'll start with a single service and a single database, get the core checkout flow working, and call out where I'd split later and why. The first thing I'd split out is payments, once it needs to deploy on its own cadence." That one sentence says you have shipped real systems.
The "why split" question. Expect "when would you break this into services?"
- Weak: "Microservices scale better and are more modern." This is fashion, not engineering.
- Strong, naming a specific trigger: "When the search workload needs very different scaling from the rest, like my image service example where 10% of traffic drove the pod count, or when the payments team needs to deploy without coordinating with everyone else." Tie the split to concrete pain, with a number if you can.
The consistency trap. The classic follow-up: "You split checkout and inventory into separate services. How do you keep them consistent?"
- Weak: "They'll both write to the database and it'll be fine," or a vague wave at "eventual consistency" with no mechanism.
- Strong: "I lose the single database transaction the moment these are two services. I'd run a saga: charge the card, then publish an event, and the inventory service consumes it. I make every step idempotent with an idempotency key so a retry does not double-decrement, and I use an outbox table so the event and the local write commit together." Even naming the problem correctly beats pretending it does not exist.
The failure question. "Service B is down. What happens to A?"
- Weak: "A will throw an error," full stop.
- Strong: "A has a timeout on that call, say 500ms, retries once with backoff, and a circuit breaker that stops hammering B after a few failures so A does not exhaust its thread pool waiting. If B is non-critical, A degrades gracefully and serves a cached or default response." In a monolith this question barely exists. In microservices it is the whole game.
Microservices are not better. They are a bill you pay once the pain is real.
"When exactly would you split your monolith into services?"
"You split checkout and inventory. How do you keep them consistent without a shared transaction?"
"Service B is down. What happens to A?"
"Five of your services share one database. Is that microservices?"
"What is the modular monolith and when do you recommend it?"
What you can now explain in the room
- Why "start with one service and split for a reason" beats drawing fifteen boxes up front.
- Why own-database-per-service, not the service count, is what actually defines microservices.
- How a 10%-of-traffic CPU endpoint forces a monolith to overscale, and how splitting flips the math.
- Why a single transaction becomes a saga with idempotency keys and an outbox the moment you split.
- How one down service cascades into a full outage without timeouts and circuit breakers.
- Why the modular monolith is often the mature answer, and the two real triggers (people and scaling) for going further.
Want to see this play out end to end? Try the System Design Question walkthrough on designing an e-commerce checkout, where the monolith-to-services decision drives the whole design.
Get a prep plan built around your Monolith vs Microservices interview
Tell us the role and your interview date. We will send back a dated, day by day plan. Free.