A practical lesson on using a modular monolith as a boundary-first architecture, validating internal seams before extraction, and avoiding the mistake of treating the monolith as temporary chaos.
Modular monolith first, extract later is one of the safest decomposition patterns for growing teams. Instead of paying the cost of distributed systems immediately, the team builds strong internal boundaries inside one codebase and extracts only the modules that later prove they need separate deployment, scaling, reliability isolation, or ownership. This pattern is strong because it validates the boundary before adding network cost, operational overhead, and consistency complexity.
The key is that the monolith must already be modular. If the codebase is only one big tangle, then “extract later” usually means “carry technical debt until the extraction becomes painful.” The pattern works when the monolith itself is treated as a boundary-first architecture.
flowchart LR
A["Modular monolith"] --> B["Clear internal module APIs"]
B --> C["Independent ownership and tests"]
C --> D["Observe extraction signals"]
D --> E["Extract only proven module"]
What to notice:
This pattern is valuable because it allows teams to:
Many teams want the benefits of microservices before they have enough evidence about domain seams, runtime needs, or team ownership. A modular monolith gives them a proving ground.
The pattern is only real when the monolith enforces boundaries such as:
A useful example is to make the allowed module dependencies explicit:
1// ordering/module-api.ts
2export interface OrderingModuleApi {
3 createOrder(input: CreateOrderInput): Promise<OrderId>;
4 cancelOrder(input: CancelOrderInput): Promise<void>;
5}
6
7// inventory/module-api.ts
8export interface InventoryModuleApi {
9 reserveStock(items: ReservationRequest[]): Promise<ReservationResult>;
10}
What this demonstrates:
Without interface discipline like this, the monolith is not testing anything useful.
Extraction becomes more convincing when a module shows signals such as:
This is why the pattern pairs well with the rest of this guide. Teams can use boundary review evidence, not ideology, to decide when extraction is warranted.
The most common mistake is to treat the monolith as a temporary dumping ground with the promise that “we will clean it up when we extract services.” That usually fails because:
In other words, a chaotic monolith does not become a graceful microservices architecture just because the team intends to extract later.
1module: pricing
2internal_boundary_quality: high
3team_ownership_clarity: high
4separate_scaling_need: medium
5cross_module_chattiness_if_extracted: low
6transaction_pressure_to_stay_local: low-medium
7decision_bias: candidate-for-extraction
What this demonstrates:
This is much stronger than deciding to extract just because the module feels important.
Many unhealthy microservices systems came from premature extraction. Teams distributed their code before they had:
Modular monolith first reduces that regret by delaying the hardest cost until the boundary has already proved itself internally.
A team says it is following “modular monolith first, extract later,” but modules still read each other’s tables directly, ownership is unclear, and there are no explicit interfaces between major domains. Is the pattern actually in use?
No. The stronger answer is that the team is still in a traditional monolith with weak internal boundaries. The pattern requires real modular discipline. Without explicit seams, ownership, and dependency rules, there is no trustworthy basis for later extraction.