The Launchpad to Microservices
A well-modularized monolith is the best starting point: keep the modules until scale, team, or deployment pressure justifies extracting one into a service - then the seam is already there.
On this page
This module's closing argument ties it back to where it began. A well-modularized monolith isn't a consolation prize you settle for until you can afford "real" microservices - it's the best possible starting point, and often the best permanent home. And when a genuine need to extract a service does arrive, the modular monolith is what makes that extraction tractable instead of terrifying. Modularity first, distribution later - only if justified.
The monolith-first strategy
The strategy, argued by many senior engineers (famously "MonolithFirst" by Martin Fowler), goes:
- Start with a modular monolith. One deployable, clean feature modules, enforced boundaries, event-based communication. You get most of the design benefits of microservices with none of the distributed cost.
- Get the boundaries right where they're cheap to move. In a monolith, refining a module boundary is refactoring - move a class, adjust an API. You will get boundaries wrong at first; the monolith lets you fix them freely as the domain clarifies.
- Extract a module into a service only under real pressure - never speculatively.
The whole point: defer the hardest, most expensive, least reversible decision (a network boundary) until you have the knowledge to make it well - which you only gain by building the thing.
What actually justifies extraction
Extract a module into its own service when a concrete, present force demands it - not because microservices are fashionable:
- Independent scaling. One module (fraud scoring, image processing) has wildly different load or resource needs (GPU, memory) than the rest, and scaling the whole monolith for it is wasteful.
- Independent deployment / team autonomy. A separate team needs to release a module on its own cadence without coordinating with everyone else's deploys. (This is Conway's Law: at scale, service boundaries mirror team boundaries.)
- Different technology. A module is genuinely better in another language or runtime.
- Fault isolation. A module must fail without taking the rest down, beyond what in-process isolation gives.
If none of these apply, the module is better off staying in the monolith - simpler to develop, test, and operate. "We might need to scale someday" is not a justification; it's speculation, and you can extract when someday arrives.
Why the modular monolith makes extraction easy
Here's the payoff for all the discipline. When you do extract a module, a modular monolith has already done the hard parts:
- The boundary exists. The module has a public API and a known set of published/consumed events - that API becomes the service's network interface, and those events become messages. You're not discovering the boundary under fire; you're promoting one that's already clean.
- The data is owned. Because each module already owns its tables and no one queries across boundaries (the cardinal rule), the module's data can move with it - the single hardest part of extraction is already done.
- Communication is the right shape. In-process API calls become REST/gRPC; in-process events become a message broker. The pattern doesn't change, only the transport.
Contrast this with extracting a service from a ball of mud: you first have to find the boundary, untangle shared data, and unpick a web of direct calls - which is why big-bang monolith-to-microservices rewrites so often fail. The modular monolith turns a terrifying rewrite into a mechanical promotion.
Don't extract to escape a mess - fix the mess first
A common, doomed move: 'our monolith is a tangled mess, so let's break it into microservices to force boundaries.' You can't extract clean services from unclear boundaries - you'll get a distributed monolith, now with network calls in the tangle. Do the modularization inside the monolith first (where it's cheap and safe); only extract once the module is genuinely clean and a real pressure justifies the network. Microservices amplify whatever structure you already have - impose the structure before you distribute.
A modular monolith is a well-designed house with clearly walled rooms, each with its own door and utilities. If one room - say a home business - grows enough to need its own entrance, parking, and hours, you can convert it into a separate annex relatively cleanly, because it was already a self-contained room with its own plumbing and a door to the outside. Trying instead to carve a separate building out of a single open-plan space with no internal walls means first figuring out where walls should go while people are living there - far harder and riskier. Build the rooms first; add the separate building only when a room truly needs to be its own address, and the conversion is a renovation, not a demolition.
A company's modular monolith has a well-bounded notifications module (its own API, its own events, its own
tables). Two proposals arrive: (a) extract it to a service because 'microservices are best practice'; (b) extract
it because the notifications volume has grown 50x, it needs to scale independently on separate hardware, and a
newly-formed messaging team wants to own its release cadence. Evaluate both, and explain why the extraction would
be relatively smooth here.
Why is a well-modularized monolith the ideal starting point even if you might want microservices later?
Key takeaways
- A well-modularized monolith is the best starting point (and often permanent home) - most of microservices' design benefits without the distributed cost.
- Monolith-first defers the hardest, least reversible decision (a network boundary) until you've learned the domain by building it; boundaries are cheap to move in a monolith.
- Extract a module into a service only under concrete pressure: independent scaling, team autonomy/deployment (Conway's Law), different tech, or fault isolation - never speculatively.
- A modular monolith makes extraction tractable: the module's public API becomes the network interface, its events become messages, and its owned data moves with it.
- Never extract to escape a mess - you can't carve clean services from unclear boundaries; modularize inside the monolith first, then distribute only when justified.