← All Articles

Micro-frontends won't fix your monolith

I keep seeing the same pitch. A team is frustrated with their frontend monolith. Changes take forever. Every PR touches too many files. Deployments are nerve-wracking. So someone proposes the fix: split it up into micro-frontends. Smaller, independent pieces. Each team owns their slice.

A LinkedIn post I saw recently made this exact argument. "Big systems rarely grind to a halt because they're big. They grind to a halt because they're tangled." And I agree with that diagnosis. But the prescription of splitting into independently deployable frontends? That's where I think people jump too far, too fast.

The pattern I keep seeing

A team has a React or Angular app that's grown over a few years. It started clean. Probably had good structure early on. But over time, components got coupled. Shared state leaked across features. The design system became suggestions rather than constraints.

Now every feature change requires coordinating with three other teams. The PR review queue is a bottleneck. Someone does a spike on micro-frontends and presents it at an architecture review. The slides look great. Independent deployments. Team autonomy. Faster iteration.

I watched this happen at an e-commerce company I worked with. Their product catalog, cart, and checkout were all in one Next.js app. They felt the pain of slow releases and proposed splitting each into its own micro-frontend. The plan was Module Federation, separate repos, independent CI pipelines.

Six months into the migration, they had two of the three extracted. And they were spending more time debugging cross-app state issues than they'd ever spent on slow PRs. The irony wasn't lost on anyone.

It's usually a modularity problem

When I dig into these situations, the actual problem is almost never that the code needs separate deployment pipelines. The problem is that the code doesn't have clear boundaries inside the existing app.

Components import from each other freely. There's no enforced interface between features. Shared state is a global soup that anyone can reach into. The monolith being one deployable unit isn't what makes it slow. The tangled modules are.

And that's a fixable problem without rearchitecting your entire frontend infrastructure.

Modern tooling already gives you most of what you need. Monorepo tools like Nx or Turborepo let you set up independent build targets per feature. CODEOWNERS files give you clear team ownership. ESLint boundaries or module federation within a single build can enforce import rules. Design systems and component libraries create shared contracts between teams.

These aren't glamorous solutions. They don't make for exciting architecture slides. But they solve the actual problem: untangling modules so that teams can move independently within a shared codebase.

The catch is they require discipline. You have to enforce the boundaries. You have to actually say "no, you can't import directly from the cart module, use the published interface." And that's a people problem as much as a technical one. Splitting into micro-frontends doesn't fix that. It just moves the arguments to a different layer.

When micro-frontends actually make sense

I don't think micro-frontends are always wrong. But the legitimate use cases are narrower than most people think.

The first is genuine organizational scale. If you have 15+ teams all contributing to a single frontend, and you've already invested in modular design, and teams still can't release independently because of build and deployment coupling... then micro-frontends solve a real coordination problem. This is the IKEA, Spotify, Zalando situation. Hundreds of developers. Multiple product lines. The deployment unit itself becomes the bottleneck.

The second is legacy migration. The strangler fig pattern, where you're gradually replacing an old frontend by wrapping new micro-frontends around it. You're not choosing micro-frontends because they're better. You're choosing them because you can't rewrite everything at once and you need the old and new to coexist.

Both of these are specific, contextual decisions. Not general best practices.

The tradeoff nobody wants to talk about

What gets glossed over in the "just split it up" pitch is the operational cost.

Shared state across micro-frontends is genuinely hard. Your cart and product pages need to agree on what's in the cart. In a monolith, that's a shared store. Across micro-frontends, that's a coordination problem. Custom events, shared state buses, or duplicated API calls. Each approach has tradeoffs and failure modes.

Routing gets weird. You need something to orchestrate which micro-frontend handles which URL. That orchestrator becomes its own piece of infrastructure to maintain. And when two teams disagree about routing, you've got a coordination problem that's arguably harder than what you started with.

Dependencies multiply. If three micro-frontends all use React, are they sharing one copy or bundling three? Shared dependencies create coupling at the infrastructure level. Separate copies mean your users download React three times. Pick your problem.

And then there's version skew. Team A ships a breaking change to the shared component library. Team B hasn't updated yet. Now your users see inconsistent UI depending on which part of the page they're looking at. I've seen this happen in production. It's not fun to debug.

A team I talked to last year went all-in on micro-frontends for a B2B dashboard. Five teams, five micro-frontends, Module Federation. Within a year, they'd built an internal platform team just to manage the integration layer. Three engineers, full-time, maintaining the shell app and dealing with cross-cutting concerns that no individual team wanted to own. They essentially traded their monolith coordination problems for platform coordination problems.

Fix the modularity first

If your frontend feels slow to change, start with the boring stuff.

Draw the module boundaries. Figure out which features should be isolated from each other and enforce it. Nx has project boundaries. ESLint has import restrictions. Use them.

Set up CODEOWNERS so teams know what they're responsible for. Make it clear who approves what.

Invest in your component library and design system. These are the shared contracts between teams. They should be versioned, documented, and treated as infrastructure.

Set up independent build and test targets. Most monorepo tools let you build and test only what changed. Your CI shouldn't run the entire test suite when someone changes a button in the settings page.

If you do all of that and you still can't release fast enough, then micro-frontends become a reasonable conversation. But I'd bet that in most cases, you won't get there. The modularity fixes solve the actual pain.

I think there's a tendency in frontend engineering to reach for architectural solutions to what are really design and discipline problems. Micro-frontends have their place, but it's a narrow one. And most teams aren't in it, even when it feels like they are.

What's your experience been? Have you worked on a micro-frontend setup that actually improved things? Or did it introduce problems you didn't expect? I'd genuinely like to hear from teams who've been through this.