Problems with Monoliths: When to Move On
When the monolith starts fighting you — merge conflicts, slow CI, and team collisions — and how to read the signals before they become crises.
The monolith is the right default. But defaults have limits. I've seen the point where the monolith stops helping and starts fighting — and the signals, when they arrive, are usually unmistakable.
The Signals
Team Collisions and Merge Conflicts
The most common breaking point isn't technical, it's organizational. When multiple teams from different parts of the org — different managers, different timelines, different priorities — are all editing the same files, the monolith becomes a coordination problem.
Every PR touches shared code. Merge conflicts stack up. One team's release blocks another's. The bottleneck isn't code quality or architecture, it's the physical reality of multiple people editing the same files at the same time.
Worth noting: this is sometimes a code organization problem, not an architecture one. If you're hitting collisions because everything is genuinely interleaved, breaking into microfrontends won't fix it — you'll just move the shared code into its own package and have the same problem at a different level.
CI/CD Time
The other signal that will catch you is build time. As a codebase grows, so does the test suite. That's a good problem to have — until running the full suite takes 25 minutes and the feedback loop for "is this good enough to ship?" collapses.
Slow CI changes how people work. Developers start batching changes to amortize the wait, skipping steps, or merging on faith rather than confidence. Each of those is a quality risk. The dopamine hit of watching CI turn green disappears when it takes two hours to get there.
Here's the thing I keep coming back to: slow CI is often a build system problem, not a repo topology problem. Tools like Turborepo can give you one repo with fast, incremental builds — rebuilding only what changed. If slow CI is the main pain, fix the build system before you start splitting repos.
The Autonomy Problem
The deepest version of the monolith problem is autonomy. When teams can't deploy on their own schedule, can't make decisions about their area without coordinating with everyone else, velocity drops across the board.
This is where the spectrum becomes useful:
Monolith ←————————————————————→ Runtime Microfrontends
Simple Autonomous
One deploy Independent deploys
Low overhead High complexityMoving right gives more team autonomy. It also adds more complexity: more configuration, more coordination required at the infrastructure level even when removed at the code level. More autonomy almost always means more complexity — that's not a bug, it's the trade.
Moving the Problem Around
One thing I want to be clear about: splitting a monolith into microfrontends doesn't eliminate complexity, it relocates it. If you have a lot of shared code, that shared code doesn't disappear — it becomes its own package with its own versioning, its own release cycle, its own upgrade path.
Team collision becomes a versioning problem. Merge conflicts become API compatibility concerns. These are solvable — but they're not free. Make sure the trade is worth it before you make it.
When It's Clearly the Right Move
The case I find most compelling: when rewriting the existing codebase would be untenable. I've talked to teams sitting on 15-year-old codebases where the only realistic path forward was incremental extraction — peeling off pieces one at a time into independently deployable units, until eventually the legacy core could be replaced or retired.
In those situations, microfrontends aren't an architectural preference. They're a migration strategy. And that changes everything about how you evaluate the trade-offs.
The Buffet
The honest answer to "monolith or microfrontend?" is: take from the buffet.
Every real system has elements of both ends of the spectrum. The key is being intentional about which trade-offs you're accepting and why. A few principles I try to hold onto:
- Start with a monolith. Don't optimize for problems you don't have yet.
- Watch for the signals — team collisions, slow CI, deploy coupling.
- Match the solution to the actual problem — not to a blog post about what the industry is doing right now.
- Move incrementally. The monolith doesn't become a fully federated system overnight, and it shouldn't.
The rest of this series explores the full spectrum: runtime microfrontends with module federation, build-time composition with monorepos, Turborepo for incremental builds, TypeScript at scale, and CI/CD patterns for large teams. Each is a tool. None is the answer.
Keep reading
Enjoyed this? Get more like it.
Deep dives on system design, React, web development, and personal finance — straight to your inbox. Free, always.