Skip to content

Trade-offs · Foundations

Monolith vs microservices

When to keep one deployable program and when to split it into services, the numbers that decide, and what breaks with each choice.

Asked at: SDE-1SDE-2

The decision

A monolith is one program that you build and deploy as one unit. It can still run on many servers. A microservice is a small program that owns one business job and its own data, and its own team can deploy it alone. A modular monolith is one deploy with clear modules inside. The choice is mostly about people and load, not technology.

Choose a monolith when

  • You have one team or a few engineers. Eight services for 5 engineers is 5 / 8 = 0.6 engineer for each service.
  • You are still learning where the boundaries are. Wrong boundaries are costly to fix across a network.
  • You need transactions across many tables. One database gives you all-or-nothing changes.
  • Your load is even across the parts, so everything can scale together.
  • Keep it modular from day one, so you can cut a piece out later.

Choose microservices when

  • Several teams block each other on every deploy.
  • One part has a very different load, for example a heavy job for two days a month.
  • One part must keep working when another part fails.
  • A part needs a different tool or language.
  • Its data is already separate, so the cut is cheap.

The numbers that change the answer

Each network call can fail. If a request needs services in a row, their availability multiplies.

  • 4 services in a row, each working 99.9% of the time (Assumption): 0.999 x 0.999 x 0.999 x 0.999 = 0.996, which is 99.6%.
  • A 30-day month has 30 x 24 x 60 = 43,200 minutes. At 99.9%, failure time is 0.001 x 43,200 = 43.2 minutes. At 99.6%, it is 0.004 x 43,200 = 172.8 minutes. That is 4 times more.

Calls also add time and can be chatty. Assumption: a call costs 7 ms (2 ms network, 5 ms work). A page that asks another service 20 times, one after another, takes 20 x 7 = 140 ms. One call for all 20 items takes 7 ms.

Worked example at Indian scale. An invoicing app for small shops. Assumptions: 5 engineers, one database. At month end, customers make 200,000 PDF invoices in 20 working hours. Each PDF needs 2 seconds of one core. Two web servers give 8 cores. Normal use is 2 cores.

  • PDF work = 200,000 x 2 = 400,000 core seconds = 111 core hours. Over 20 hours: 111 / 20 = 5.6 cores.
  • With normal use of 2 cores: 7.6 of 8 cores = 95% busy. Pages slow down.
  • Move only the PDF work to a queue and three worker servers (12 cores). Load = 5.6 / 12 = 47%. The web servers drop to 2 / 8 = 25%.

One clear reason gave two parts, not eight.

What breaks with each choice

  • Monolith: every change ships together, so one bad change can stop everyone. Builds and tests slow down as the code grows. One hot part forces you to scale the whole program. Fix: strict module edges, faster tests, and cutting out only the part that needs it.
  • Microservices: failures multiply (99.9% became 99.6%). A change that spans two services needs a saga, a series of steps with undo steps, instead of one transaction. Debugging crosses networks. The worst result is a distributed monolith: services that share tables or must always change together. You pay for the split and get no freedom.

What it costs

  • A modular monolith is the cheapest to run: one pipeline, one thing to watch.
  • Each new service needs its own build, deploy, alerts and someone on call. Assumption: a safe on-call rotation needs two people, so 5 engineers can own 5 / 2 = 2.5, which means 2 services.
  • You can move later with the strangler method. Wrap the piece in a module and put a queue in front. Run its worker as its own deploy. Send a small share of traffic to it, compare, then raise it to 100%. Plan for weeks per piece (rough guess).

How each level answers

SDE-1: A monolith is one deploy. Microservices are many small deploys that talk over a network.

SDE-2: For a team of 5 I keep a modular monolith, because splitting adds network calls and failures without removing a real problem. I split when teams block each other or one part has a very different load.

Senior: Four services in a row at 99.9% give 99.6%, which is 173 minutes of failure a month instead of 43. So I cut only for a clear reason, such as PDF work using 5.6 of 8 cores. The risk is a chatty boundary, like 20 calls of 7 ms. The fallback is a switch that sends traffic back to the old path.

Staff: I ask which cost hurts the business now: slow releases, one team blocking another, or a bill for scaling everything. I name an owner for every service and every table, and I make the cut in steps that can be undone. I check the choice by deploy time, the share of failed changes, and the hours of on-call work per service.

Say it in the interview

For five engineers I would keep a modular monolith. I would move only the PDF work to a queue and workers, because it uses 5.6 of 8 cores at month end. Each service multiplies failures: 99.9% in a chain of four becomes 99.6%. I would split further when a second team or a second clear reason appears.

Common mistakes

  • Saying "microservices scale better" for a small team. They scale teams and different loads, and a small team has neither.
  • Letting services read each other's tables. A change to one table then breaks two services.
  • Cutting by technical layer, such as "the database service". Cut by business job.
  • Blaming the monolith for a slow deploy. Check first if the tests run one after another. Faster tests may fix it with no split.

Sources

Understand the trade-off. Practise explaining your choice.

In an AI mock interview, the interviewer can push on a choice like this one: it changes a number and asks if your answer still holds.