Architecture
Intermediate 15 min read Architecture

Monolith vs Microservices

A monolith is a single deployable unit that contains every feature; microservices decompose an application into independently deployable services that collaborate over the network. The right choice is a trade-off between operational simplicity and organizational scale, not a matter of fashion.

monolithmicroservicesdecompositionbounded contextdistributed systemsstrangler fig
Introduction

What is this?

A monolith ships all features — UI, business logic, data access — as one process and one artifact. Microservices split the system by business capability, giving each service its own codebase, data store and deploy pipeline that talk over HTTP, gRPC or events. Both are architectural strategies for managing change, coupling and scaling, and the decision is reversible.

Motivation

Why do you need it?

  • Monoliths have the lowest operational overhead: one build, one deploy, one place to trace a request.
  • Microservices let each service deploy, scale and fail independently, so a hot path scales without replicating the whole application.
  • Independent services map cleanly to team ownership and bounded contexts, reducing cross-team merge conflict and coordination.
  • A well-drawn service boundary isolates blast radius: a crash in fraud detection does not take down checkout.
  • The strangler-fig pattern lets you modernize a legacy system incrementally instead of doing a big-bang rewrite.
Think about it

Mental model

Imagine

A single restaurant kitchen

Think of

A fleet of specialty food trucks

One kitchen serves every table under one roof — simple, but every new menu item touches the same stove. Trucks each own one menu and can be added, scaled or retired independently, but coordinating orders between trucks introduces delivery logistics the kitchen never needed.

Mechanics

How it works

  • A monolith is one codebase and one artifact (JAR, bundle, container image) where every request is handled in a single process.
  • Microservices are split by business capability; each service owns its data and exposes an API rather than sharing a database.
  • Services communicate over the network, so the request path spans multiple hops and latency, retries and timeouts become first-class concerns.
  • Decomposition is driven by team boundaries, ownership and change frequency — split what changes together and is owned together, not by file count.
  • An API gateway or service mesh fronts the services to handle routing, auth, rate limiting and request tracing.

Request path in a monolith versus microservices: an in-process call becomes a distributed fan-out.

User request
Router / gateway
Monolith path
Service fan-out
Data ownership
Response
Reference

Key commands

node server.js

Start the monolith locally on port 3000.

curl -s localhost:3000/api/orders/1

Call an endpoint inside the single process to verify the monolith.

{"id":1,"customer":"ada","total":49.99}
docker build -t store-api .

Package the monolith into a single container image.

docker-compose up -d

Bring up the full stack (api, db, cache) in one command.

npm test

Run the test suite once for the whole monolith.

git tag v1.0.0

Tag a deployable version of the monolith for release tracking.

Hands-on

Examples

A two-route monolith before decomposition

A two-route monolith before decomposition
// monolith/server.js - one process handles every feature
const express = require("express")
const app = express()
app.get("/api/users/:id", (req, res) => {
const user = db.users.findById(req.params.id) // shared database
res.json(user)
})
app.get("/api/orders/:id", (req, res) => {
const order = db.orders.findById(req.params.id) // same shared database
res.json(order)
})
app.listen(3000)

Users and orders live in the same process and share one database and one deployment unit. Extracting the orders route into its own service means moving the data access into the service, exposing it over HTTP, and having the gateway forward /api/orders there — that new network hop is the cost you take on.

Real world

In production

  • A startup ships a well-modularized monolith first to reach product-market fit with the smallest operational surface.
  • A payments platform splits fraud detection, ledger and notifications so each scales and fails independently.
  • An e-commerce platform splits catalog (read-heavy, cacheable) from checkout (write-heavy, transactional) to tune each path.
  • Teams use the strangler-fig migration, standing up new capabilities as services and cutting over old monolith routes incrementally.
Guidelines

Best practices

  • Start with a modular monolith; extract a service only when a stable seam, team boundary and data boundary all exist.
  • Enforce database-per-service: never let a service reach into another service's tables.
  • Define explicit, versioned contracts (OpenAPI or protobuf) so producers and consumers evolve independently.
  • Keep every service independently deployable and feature-flagged so you can roll back without a coordinated release.
  • Measure before splitting — latency, deploy cadence and team friction — so the move is driven by data, not dogma.
Watch out

Common mistakes

  • Building a distributed monolith: services that must deploy together or share a database.
  • Splitting by technical layer (front, back, db) instead of by business capability, which maximizes chatty coupling.
  • Ignoring data coupling and designing synchronous cross-service transactions that need distributed locking.
  • Over-engineering: spinning up dozens of services for a five-person team that ships one CRUD app.
Root cause analysis

Common issues

Error
ECONNREFUSED calling the orders service
Root cause

The downstream service is down or registered under the wrong address.

Fix

Check the service's health endpoint, confirm service discovery / DNS, then look at its logs on startup.

Error
End-to-end p95 jumped from 50ms to 500ms
Root cause

Chatty request graphs where one user action triggers many sequential calls.

Fix

Batch or fan-in calls, add read-model caching, and move non-critical steps to events.

Error
Order and payment data are inconsistent
Root cause

Two services writing to the same logical transaction without coordination.

Fix

Introduce a saga with compensating actions or an outbox pattern so each step is eventually consistent.

When things break

Debugging tips

  • Start from the tracing system (Jaeger, Zipkin, Datadog) to see the full request path across services.
  • Check every service's health endpoint before assuming the problem is in the application logic.
  • Thread a correlation ID through headers so log lines across hops can be joined into one trace.
  • Verify deployed versions — a stale or mismatched service version is the classic source of 'works on my machine'.
Interviews

Interview checkpoint

Practice

Hands-on lab

From Monolith to Service
45 min · intermediate
In progress
Objectives
  • Stand up a two-route monolith API with a shared data store
  • Identify a bounded capability worth extracting
  • Extract the orders capability into its own HTTP service
  • Route the gateway to the extracted service and verify the split

From Monolith to Service

45 min · intermediate

0/5 steps
0%
sandbox · Linux sandbox
$
Try these commands
Test yourself

Quick quiz

0/3
1

What is the primary cost you accept when you move from a monolith to microservices?

2

In a well-designed microservice system, services commonly share a single database.

3

A four-person team must ship a CRUD application in six weeks with no SLA beyond availability. Which starting architecture is most defensible?

Quick reference

Cheat sheet

Decomposition signals
Same deploy cadence?Keep it together; do not split
Different scale profileCandidate for its own service
Team boundary existsService per team ownership
Data accessed across servicesIntroduce an API or event, never shared tables
New capability in legacy appStrangler-fig pattern
Migration tools
API gatewayRoute, auth, rate-limit external calls
Service meshmTLS, retries, canary traffic, tracing
Feature flagsSafe cutover and instant rollback
Correlation IDsJoin logs across hops
Go deeper

Official documentation