AnyLearn
All lessons
Computer Scienceintermediate

Microservices vs Monoliths

The honest case for each. When a monolith is correct, what microservices actually buy you (and what they cost), Conway's law, and how to spot a fake microservices architecture that's actually a distributed monolith.

Not signed in โ€” your progress and quiz score won't be saved.
Lesson progress1 / 8

Two ways to organise a codebase

A monolith is one deployable unit: one codebase, one process, one database. All features live together and ship together.

Microservices split features into independently-deployed services with their own data, talking over the network.

Neither is universally better. The right choice depends on your team size, deployment cadence, and whether the organisational coupling matches the technical coupling you'd get from each. Most arguments about "microservices vs monoliths" miss this โ€” they treat it as a technical decision when it's mostly an organisational one.

Full lesson text

All 8 steps on one page โ€” for reading, reference, and search.

Show

1. Two ways to organise a codebase

A monolith is one deployable unit: one codebase, one process, one database. All features live together and ship together.

Microservices split features into independently-deployed services with their own data, talking over the network.

Neither is universally better. The right choice depends on your team size, deployment cadence, and whether the organisational coupling matches the technical coupling you'd get from each. Most arguments about "microservices vs monoliths" miss this โ€” they treat it as a technical decision when it's mostly an organisational one.

2. What monoliths are good at

Reasons real teams stay on monoliths:

  • Refactoring is trivial. Rename a function across the codebase with one IDE command; no API versioning, no migration plans.
  • Transactions are free. Need to update three tables atomically? BEGIN/COMMIT. In microservices, that's a distributed transaction problem.
  • One deploy unit. Lower operational overhead; fewer moving parts; one log stream to debug.
  • Local function calls. No retries, no timeouts, no serialisation, no API mismatch โ€” just a function call that always works.

The ceiling: a monolith eventually slows down as more teams contribute. Build/test times grow, deploys become coordinated events, and ownership gets unclear. That ceiling is much higher than internet folklore suggests โ€” Shopify and GitHub run gigantic monoliths.

3. What microservices buy you

Microservices solve real problems, but specific ones:

  • Independent deployment: teams ship without coordinating. Critical when you have โ‰ฅ5 product-aligned teams.
  • Isolated failures: a bug in service A doesn't take down service B (in theory; in practice, depends on coupling).
  • Technology heterogeneity: ML service in Python, payments in Go, mobile API in TypeScript. Pragmatic at scale; overkill at small.
  • Independent scaling: scale up only the services that need it. Real savings if your services have wildly different resource profiles.
  • Bounded blast radius: a memory leak in one service doesn't OOM the whole app.

The price is everything is harder: data consistency, debugging, deployments, observability, networking. You're trading code-level coupling for network-level coupling.

4. Conway's Law decides for you

Organisations design systems that mirror their communication structure.

If you have one team of 6, a microservices architecture forces them to negotiate cross-service contracts with themselves. Pure overhead.

If you have 12 teams of 6 each, a monolith forces them to step on each other's deploys, fight over a shared database, and coordinate releases. Different pain.

The useful framing: architecture should match team boundaries. One team โ†’ monolith. Five-plus product-aligned teams โ†’ microservices, with each team owning a service. Mismatch either way creates daily friction.

5. Team count vs sane architecture

Heuristic, not law.

flowchart LR
  Solo["1-3 engineers"] --> Mono1["Monolith"]
  Small["~10 engineers"] --> Mono2["Modular monolith"]
  Med["~30 engineers"] --> Hybrid["Split a few high-friction services off"]
  Big["50+ engineers, ~5+ product teams"] --> Micro["Microservices"]

6. The distributed monolith anti-pattern

The worst of both worlds: you have many services, but they're so tightly coupled they have to be deployed together. Symptoms:

  • Service A's release notes always say "deploy in lockstep with B and C".
  • A single feature change touches 5 services.
  • Each service has its own deploy pipeline but they all break together.
  • The schema in service B is referenced by service A's code.
  • Local development requires running 12 services.

This happens when teams split services along technical lines ("the database service", "the API service") instead of business domains. The fix is to recombine and re-split along bounded contexts โ€” chunks of business logic that own their data. Or just go back to one well-modularised monolith.

7. Data: the hardest part

In a monolith, every feature shares one schema and uses transactions. In microservices, each service should own its data. The complications:

  • No cross-service JOINs. If you want "orders + users", one service has to call another. Or both project the data they need into their own store.
  • No distributed transactions. "Charge the card AND mark the order paid" is now two writes in two systems. Use outbox patterns (write to your DB and an event log atomically, then process the event asynchronously).
  • Eventual consistency leaks into the product. "User just updated their address; why is the receipt going to the old one?" โ€” because the order service hasn't synced yet.

When people say "microservices are hard", they usually mean the data part. The networking, deployment, and observability are tractable; the data consistency is foundational.

8. The pragmatic middle: modular monolith

Most teams don't actually need microservices. They need a modular monolith: one codebase and one deployment, but with strict internal boundaries:

  • Module-level imports enforced (no payments/ importing from inventory/internals/).
  • One database, but each module owns its tables and exposes a clear API to the rest of the codebase.
  • One CI pipeline, but tests can run per-module.
  • One deploy, but features can be feature-flagged for gradual rollout.

When the time does come to extract a microservice, you've already done the hardest work โ€” defined the boundaries. The extraction becomes a refactor, not an architecture rewrite. Most companies that successfully moved to microservices did so by gradually peeling services off a modular monolith, not by starting microservices from day one.

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. Per Conway's Law, what's the most reliable predictor of the right system architecture?
    • The programming language being used
    • The shape of the engineering organisation: team count, sizes, and ownership boundaries
    • Whether you're using cloud or on-prem
    • The age of the codebase
  2. Which is *NOT* a typical sign of a distributed monolith?
    • Services have to be deployed together
    • A single feature change touches multiple services
    • Each service owns its own data and database
    • Local dev requires spinning up many services
  3. Why are transactional updates harder in a microservices architecture?
    • Databases don't support transactions anymore
    • Operations spanning services can't share a single ACID transaction; you need patterns like outbox or sagas
    • Microservices use different protocols
    • All microservices run on the same machine
  4. A 7-person startup is debating microservices vs monolith. Which is almost certainly the better default?
    • Microservices โ€” they scale better
    • Modular monolith โ€” much less operational overhead until team count grows
    • One service per team member for clear ownership
    • Serverless functions for everything
  5. Your team wants to extract one service from a monolith. What's the prerequisite that makes this go well?
    • Rewriting the whole codebase in Go
    • Already having clear module boundaries that the service-to-be respects internally
    • Hiring a microservices architect
    • Switching to Kubernetes first

Related lessons