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

Common Mistakes and Trade-offs

A remarkably common mistake developers make when initially adopting microservices is treating it as an immediate fix for complexity rather than a fundamental architectural shift. Teams often declare 'we're going micro!' after a particularly thorny monolith issue, layering in services without truly rethinking the business domain or establishing clear service boundaries. This leads to a distributed system of small, tightly coupled services – essentially recreating the problems they were trying to escape. The focus shifts from solving genuine business needs to managing infrastructure and inter-service communication, often with minimal benefit gained compared to carefully refactoring and modularizing the existing monolith. It's not about technology; it's about a disciplined approach to problem decomposition.

At scale, the operational overhead of microservices can become dramatically more significant than initially anticipated. While a small monolithic application might require a single DevOps team managing deployments and monitoring, a large-scale microservice architecture necessitates multiple teams – each responsible for a specific service – all working concurrently. This creates a complex web of dependencies, requiring sophisticated orchestration tools (like Kubernetes) and robust observability solutions to track requests across services, diagnose performance bottlenecks, and handle failures. The cost of maintaining this level of infrastructure and expertise can quickly outweigh the perceived benefits if not carefully managed with strong automation and standardized practices.

There's a pervasive misconception that microservices are 'always better' for any new application – particularly those involving rapidly evolving requirements or highly dynamic teams. This is simply untrue. Monoliths, when well-structured and maintained, can be surprisingly agile. The smaller codebase makes it easier to understand, test, and deploy changes quickly, especially in environments where the business domain isn't dramatically changing. Microservices introduce significant operational complexity that requires a substantial investment in tooling, training, and process maturity; prematurely adopting them just because they're trendy is a recipe for disaster.

The decision of whether to migrate to microservices or retain a monolith – or even to adopt a hybrid approach – isn't a binary one. A full rewrite is almost always the worst option. Instead, consider 'service slicing' – identifying logical boundaries within the monolith and gradually extracting them as independent services while retaining the core application. Alternatively, carefully choose which parts of the system need to be microservices based on factors like team structure, domain decomposition, and anticipated scaling requirements. Don't try to force a monolithic architecture into a microservice paradigm; focus on applying the principles that best fit the specific context, recognizing that some areas will always benefit from tighter coupling and simpler deployment strategies.

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.