Architecture comparison

Microservices vs monolith

One of the most over-debated decisions in software architecture. Both can work; both can fail. The right answer depends on team size, organisational coupling, and how much operational complexity you can absorb.

TL;DR

  • Monolith — one codebase, one deployable, one database. Simple operations, fast iteration in early-stage product.
  • Microservices — many independently deployable services, each owning data. Team autonomy and targeted scaling, at the cost of distributed-systems complexity.
  • Most teams should start monolithic and split out services only when team or scale pain is concrete — not anticipated.

Side-by-side comparison

AspectMonolithMicroservices
Deploy unitOne applicationMany services, independent deploys
CodebaseOne repository (or monorepo)One repo per service (or shared monorepo)
DatabaseUsually one shared DBOne DB per service (database-per-service pattern)
CommunicationIn-process function callsHTTP/gRPC + message queues over the network
Failure modeAll-or-nothingPartial — one service down, others keep running (if designed for it)
ScalingWhole app togetherPer-service, independently
Tech stackOne language/frameworkEach service can choose its own (rarely good idea)
Team couplingHigh — shared code, shared releaseLow — teams ship independently
Operational costLow — one thing to monitorHigh — distributed tracing, service mesh, etc.
Sweet spot1–50 engineers, single product50+ engineers, many product areas

Architecture sketch

Monolith

┌─────────────────────────┐
│   Load balancer         │
└───────────┬─────────────┘
            ▼
┌─────────────────────────┐
│   App (single process)  │
│  ┌───────┬────────────┐ │
│  │ Auth  │  Orders    │ │
│  ├───────┼────────────┤ │
│  │ Users │  Payments  │ │
│  └───────┴────────────┘ │
└───────────┬─────────────┘
            ▼
        ┌────────┐
        │   DB   │
        └────────┘

Microservices

         ┌─ Auth svc ──→ Auth DB
LB ──→ API GW ──┼─ Users svc ─→ Users DB
         ├─ Orders svc → Orders DB
         └─ Payments ──→ Payments DB

         ┌────────────┐
         │  Message   │
         │  broker    │  ← events between services
         └────────────┘

When to stay monolithic

  • Small team. Under ~50 engineers, monolith communication overhead is far cheaper than service overhead.
  • Single product / domain. Clear bounded contexts haven't emerged yet — splitting too early creates wrong boundaries.
  • Operational simplicity matters. One log stream, one metrics dashboard, one deploy pipeline.
  • Tight coupling between features. When most features touch most data, a network call between them is just slow.
  • Need fast iteration. Cross-service refactors are dramatically slower than monolith refactors.

When microservices pay off

  • Many teams, parallel delivery. Independent deploys remove cross-team coordination overhead.
  • Heterogeneous scaling needs. Image processing and the customer dashboard have very different traffic patterns.
  • Clear domain boundaries. Billing, search, and inventory really are separable concerns.
  • Compliance / isolation. Payment-handling service needs PCI scope; isolation reduces audit surface.
  • Polyglot needs. ML pipeline in Python, API in Go, real-time in Elixir — but be honest about the maintenance tax.

English phrases engineers use

Monolith conversations

  • "We have a well-modularised monolith — bounded contexts are clear inside."
  • "Every PR is a shared release — coordination is killing us."
  • "The build is now 15 minutes — we need to split or shard."
  • "This is a distributed monolith — multiple services but every change touches all of them."
  • "Strangler fig: route this endpoint to the new service, keep the rest in the monolith."

Microservices conversations

  • "What's the service boundary here — by domain or by access pattern?"
  • "This is creating a chatty interface — too many round trips per request."
  • "Database per service — Orders doesn't read from the Users DB directly."
  • "The blast radius of this change is limited to one service."
  • "Saga pattern for the multi-service transaction — no distributed locks."

Quick decision tree

  • Greenfield, < 20 engineers → Monolith
  • 20–50 engineers, single product → Modular monolith
  • 50+ engineers, multiple product areas → Microservices (carefully)
  • Compliance isolation required (PCI, HIPAA) → Split out the scoped service
  • One feature has very different scaling needs → Extract that feature only
  • "We want microservices because they're modern" → Stay monolithic
  • Existing monolith is painful → Strangler fig migration
  • Migrating, unsure about boundary → Make the wrong split reversible

Frequently asked questions

What is the real difference between a monolith and microservices?

A monolith is one application that is built, deployed, and scaled as a single unit. Microservices break that application into many small services, each owning its own data, deployable on its own schedule, communicating over the network. The trade-off is operational simplicity (monolith) versus team independence and targeted scaling (microservices).

Are microservices always better?

No — they are almost always worse for small teams. Microservices add network calls, distributed tracing, schema versioning, service discovery, and deployment complexity. For a team under ~50 engineers with a single product, a well-structured "modular monolith" is usually faster to ship and easier to operate.

When does the monolith stop working?

Common pain points: deployments coordinate across many teams, the codebase becomes a merge-conflict minefield, scaling one feature requires scaling everything, technology choices are locked in, and a single bad commit can take down the whole product. When two or more of these hurt regularly, splitting parts out begins to pay off.