Skip to content

Monolith vs Modular Monolith vs Microservices#

Three architectural styles for the same application. Each makes a different bet on team scale, operational maturity, and rate of change.

Side-by-side#

Monolith Modular monolith Microservices
Deployment unit One artifact One artifact, many modules Many services, deployed independently
Process boundary One process One process One process per service
Database One shared DB One DB, sometimes split per module One DB per service (typically)
Inter-module calls In-process function call In-process function call across modules Network call (gRPC / REST / events)
Team coordination Tight (merge conflicts, release train) Module owners, shared deploy Loose (independent CI/CD)
Cross-cutting changes One PR One PR Many PRs, often versioned
Operational complexity Low Low High (mesh, observability, deploys × N)
Latency between modules nanoseconds nanoseconds milliseconds (network + serialize)
Failure isolation None (one crash = all down) Limited High (cell + circuit-breaker)
Best for Startups, MVPs, small teams Most companies < 100 engineers Many teams, independent deploy cadence, polyglot

Decision tree#

flowchart TB
  Q[Building a service]
  Q --> A{Team size > 50<br/>engineers?}
  A -->|no| MM[Modular monolith<br/>cleanest start]
  A -->|yes| B{Polyglot or<br/>independent deploys mandatory?}
  B -->|yes| MS[Microservices]
  B -->|no| MM
  Q --> C{Pure MVP<br/>1-3 engineers?}
  C -->|yes| MO[Monolith<br/>ship today]

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class Q,A,B,C service;
    class MO,MM,MS datastore;

What goes wrong#

Premature microservices#

  • Distributed monolith: services that must deploy together — gain all the network cost, none of the isolation.
  • No platform team → every service team reinvents observability / CI / mesh.
  • Latency blows up: a request becomes 10 RPC hops.
  • "Microservices because resume" — the most common bad reason.

Big-ball-of-mud monolith#

  • One repo, no module boundaries.
  • Long release cycles; releases batch dozens of changes.
  • Adding an engineer slows the team down rather than speeding it up.
  • The fix isn't always microservices; it's modular monolith first.

Modular monolith — the sweet spot#

flowchart TB
  subgraph App[Single deployable app]
    subgraph OrderModule[Order module]
      OAPI[Service]
      ODB[(Schema: orders)]
    end
    subgraph CatalogModule[Catalog module]
      CAPI[Service]
      CDB[(Schema: catalog)]
    end
    subgraph UserModule[User module]
      UAPI[Service]
      UDB[(Schema: users)]
    end
  end
  OAPI -. in-process API call .-> CAPI
  OAPI -. in-process .-> UAPI

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class OAPI,CAPI,UAPI service;
    class ODB,CDB,UDB datastore;
  • Strict module boundaries (separate schemas, no shared models).
  • In-process API call, not network.
  • When you outgrow it, peel a module into its own service — the boundary already exists.

Migration paths#

  • Monolith → Modular monolith: introduce module boundaries, restrict cross-module access to APIs only, separate DB schemas.
  • Modular monolith → Microservices: peel one module out; introduce mesh, observability, service registry; repeat.
  • Microservices → Modular monolith: re-merge low-traffic services into a single deployable to cut operational cost.

What you'll get asked#

  • "When do you go to microservices?" When team coordination overhead exceeds operational overhead — usually past 50-100 engineers, or earlier if independent deploys matter.
  • "What about the strangler fig?" Standard migration: route new functionality to a new service, gradually peel from the monolith.
  • "How to share data?" Each service owns its DB. Cross-service queries via APIs or events. CDC for derived stores.

FAQ#

When should I use microservices instead of a monolith?#

Use microservices when team count exceeds the coordination capacity of one codebase, parts of the system scale very differently, or you need independent deployments. Otherwise a modular monolith is faster and cheaper.

What is a modular monolith?#

A modular monolith is one deployable artifact internally split into clear modules with stable interfaces. You get most of the boundaries microservices provide without the network, deploy, and observability tax.

Are microservices always faster than monoliths?#

No. Microservices add network hops, serialisation, and distributed failure modes that often make request latency higher than a monolith. They scale teams and deployments, not raw per-request speed.

How do I migrate a monolith to microservices?#

Identify high-churn, high-isolation modules, expose them behind APIs inside the monolith, then extract them one at a time using the strangler fig pattern. Move data ownership last, not first.

What is the biggest hidden cost of microservices?#

Operational complexity. You need solid CI, distributed tracing, service discovery, and on-call coverage per service. Many teams underestimate this and end up with a distributed monolith that combines both downsides.

Video walkthrough

Top 5 Most Used Architecture Patterns : via ByteByteGo