Architecture Stylesintermediate10 min

Monolith vs Microservices

When to keep your whole app in one deployable unit, and when to split it into small services that ship on their own.

Picture a small kitchen run by one chef. Every order — appetizer, main, dessert — flows through the same pair of hands, in the same room, with one shared set of pans. It's fast to set up and easy to reason about, because everything is right there. But as the restaurant gets busy, that single kitchen becomes the limit: one chef can only move so fast, and a spill on the dessert station holds up the steaks too.

Software architecture faces the same tension. Do you keep the whole application in one place where everything is close at hand, or split it into independent stations that can work — and fail — on their own?

The problem

Most systems start as a monolith: a single codebase that compiles and ships as one deployable unit. This is a genuinely good starting point — there's one thing to build, one thing to test, one thing to deploy, and calls between modules are just fast in-process function calls. For a small team and a young product, that simplicity is a feature, not a flaw.

The trouble shows up as the monolith grows. A one-line change to the billing logic still means rebuilding and redeploying the entire application, so releases get slow and scary. Many teams committing to the same codebase start stepping on each other, and merge conflicts and coupling pile up. Worst of all, if one feature — say, search — gets hammered with traffic, you can't scale just that part: you have to run more copies of the whole app, paying to scale everything to relieve pressure on one slice.

Step through one week of releases below: three teams, three changes, one shared release train. When Search's change fails on release day, predict what happens to Billing's urgent fix. Then flip the switch to replay the same week with each team shipping its own service.

How it works

The microservices approach decomposes the system into a set of small, independently deployable services. Each one owns a single business capability — orders, payments, inventory, notifications — along with its own database, and no other service is allowed to reach into that data directly. Services talk to each other only across well-defined boundaries: synchronous APIs for request/response, or asynchronous messaging and events when they can react in the background instead of waiting.

Clients don't call each service individually. Requests come in through an API gateway that acts as the single front door, routing each call to the service that owns that capability. Step through it below. When one service needs data that another owns, predict what it should do, then see what that choice costs.

Tip

Monolith first. A well-structured monolith — clean modules, clear internal boundaries — is the right default for almost every new system. It lets you learn where the real seams in your domain are before you turn them into network boundaries that are expensive to move later. You can always carve out services once those seams prove stable.

What you gain

The benefits of microservices are mostly about independence. Because each service deploys on its own, a team can ship a fix to payments at 2pm without coordinating a release of the entire system: independent deploys, as in the release week above. Because each service runs as its own process, you can give the hot ones more capacity and leave the quiet ones alone: independent scaling instead of scaling the whole app.

Below, a sale sends search traffic soaring. Predict how much memory the monolith needs to keep up, then flip to services and predict again.

There's an organizational payoff too. Small teams can own a service end to end and move at their own pace (team autonomy). And isolating services gives you fault isolation. Separate processes don't share memory, threads or crashes, so with a bulkhead between them, a failing recommendations service can't take checkout down with it.

Step through the same memory leak twice: first inside the monolith, then with recommendations running as its own service. Predict the outcome each time.

Check yourself

You've moved to microservices. The reviews service starts hanging for 30 seconds on every call, and now the whole product page takes 30 seconds to load. What went wrong?

What it costs

None of that is free — you're trading the simplicity of one process for the realities of a distributed system. Calls that used to be in-memory function calls are now network calls that can be slow, fail, or time out, as Cart's price lookup did above, and you have to design for that. Because each service owns its own data, there's no single database to query across, so you live with eventual consistency and reach for patterns like sagas to coordinate a transaction that spans several services.

The operational tax is real: you need an API gateway, service discovery, distributed tracing, centralized logging, and a deployment pipeline per service. Debugging a request that hops through five services is far harder than stepping through one stack trace. A handful of services is manageable; dozens are a serious investment in tooling and discipline.

Watch out

Don't adopt microservices for the hype. Splitting a system you don't yet understand into services usually produces a distributed monolith — all the operational pain of microservices with none of the independence, because the services are still tightly coupled and have to deploy together. Microservices solve organizational and scaling problems; if you don't have those problems, they mostly add cost.

When to use it

Reach for microservices when the pressure is real: you have multiple teams whose release cadences are colliding in one codebase, or specific parts of the system have wildly different scaling and reliability needs that a single deployable unit can't serve well. Those are the problems independent deploys, independent scaling, and fault isolation are designed to solve.

Until then, prefer a well-structured monolith and keep its module boundaries clean, so the seams are easy to extract later. The honest guidance is to let team size and scale — not fashion — pull you across the line, and to split one painful boundary at a time rather than rewriting everything into services at once.

Check yourself

A five-person team runs one product with steady traffic. Releases go smoothly and nobody steps on anyone's toes. They're thinking of splitting the app into eight microservices. What's the best advice?

Key takeaways

  • A monolith ships as one deployable unit — simple to build, test, and run early on, which is exactly why most systems should start there.
  • Microservices split the system into small, independently deployable services, each owning a single business capability and its own data.
  • The payoff is organizational and operational: independent deploys, independent scaling, team autonomy, and fault isolation between services.
  • The price is distributed-system complexity: network calls, eventual consistency, sagas for cross-service transactions, an API gateway, and far more to operate.
  • Adopt microservices to relieve real team or scale pressure — not because they're fashionable. A well-structured monolith beats a badly split one.

Keep going