Micro-Frontend Architecture at Enterprise Scale - Lessons from the Trenches
What actually works in enterprise micro-frontend migrations - seam design, Module Federation vs iframes, design system prerequisites, and the failure modes nobody writes about.
Micro-frontend architecture - a pattern for composing independently deployable frontend applications into a unified user experience - promises team autonomy and faster release cycles. At Fortune 500 scale, micro-frontend architecture works - but only when seams follow domain boundaries, not org-chart boundaries, and a shared design system is in place before migration begins.
After leading two such migrations at Fortune 500 organisations in financial services and enterprise SaaS, these are the patterns that held up and the ones that quietly fell apart. I am not going to summarize Martin Fowler’s article on the topic - you can read that yourself. What I can tell you is what the decision looks like when you are three months into a migration and discovering that the seams you drew in month one are wrong.
How Do You Design a Micro-Frontend Architecture? Starting with Seams
The hardest part is not splitting the application - it is deciding where the seams go. This single decision determines most of what follows. Draw seams correctly and the teams that inherit those boundaries can work independently for years. Draw them wrong and every release cycle involves coordination between teams that were supposed to be autonomous.
Domain boundaries are the right basis for seams. User journeys, not org chart boundaries. These two things are not the same thing, and at enterprise scale they are almost never aligned.
The org chart says: Team A owns the dashboard, Team B owns reporting, Team C owns settings. The user journeys say: a user initiating a complex workflow touches the dashboard, a reporting view, and account settings in a single session. If your seams follow the org chart, that user journey is split across three applications that need to share state, synchronize routing, and coordinate loading states. Every integration point is a coordination cost.
The domain boundary approach asks instead: what are the natural consistency requirements? Which features must always render together because they share state? Which features can genuinely be deployed independently without the user noticing? The answers to these questions define the seams, and they rarely match the org chart. In one migration I led for a financial services platform, the initial seam proposal had five applications that roughly mirrored the engineering org. After the domain analysis, we ended up with three applications that crossed team boundaries - and those seams remained stable for two years without significant renegotiation.
A bad seam produces specific, recognizable symptoms: shared navigation state that requires a cross-application event bus, loading states that must be coordinated between applications, and “simple” features that require PRs in three repositories. If you are in a migration and seeing these symptoms, the seams are wrong. Refactoring seams mid-migration is painful, but it is less painful than maintaining misaligned seams for the next three years.
Martin Fowler’s team has written well about the principles behind service boundary design (martinfowler.com/articles/micro-frontends.html) - the core insight that domain coherence should drive boundary decisions applies directly to frontend seams.
Module Federation vs iframes: Which Should You Use?
Webpack Module Federation gives you runtime code sharing with low integration overhead. iframes are simpler and provide true isolation but create significant UX friction - scroll position, focus management, and cross-frame accessibility all become your responsibility. For customer-facing surfaces, Module Federation is almost always the right choice.
The iframe case is more specific than it might appear. iframes make sense when: (1) the embedded content comes from a genuinely untrusted third party, (2) complete style isolation is a hard requirement that cannot be met any other way, or (3) the embedded application has a technology stack so different from the host that runtime integration is not feasible. A legacy Silverlight or Flash application being progressively replaced falls into this category. A modern React application owned by the same engineering organization does not.
The UX friction of iframes is not theoretical. In one project that initially used iframes for an embedded analytics module, these were the specific problems we encountered: scroll position resetting when focus moved between the host frame and the iframe; keyboard focus becoming invisible when it crossed the frame boundary; screen readers losing their position in the document when the iframe loaded; and modal dialogs inside the iframe being clipped by the iframe boundary and appearing visually broken. Each of these required a custom JavaScript bridge to address. By the time we had built the four bridges needed to make the iframe feel like a native part of the application, we had spent more engineering time than Module Federation would have required.
Module Federation requires a build tooling investment - Webpack 5 or Rspack - and careful management of shared dependencies. The shared dependency problem is real: if the host application and a remote both bundle React, the user downloads React twice. Module Federation’s shared configuration handles this, but it requires explicit decisions about which dependencies are shared and which version constraints are acceptable. From experience, the most important rule is: always share React and ReactDOM as singletons. Everything else can be evaluated case by case.
Why Does a Shared Design System Matter for Micro-Frontends?
A versioned design system component library is what keeps micro-frontends from fragmenting the user experience. Invest heavily in the system before the migration, not after.
Teams that skip this step spend the next two years discovering that independent deployability also means independently broken UIs - inconsistent spacing, duplicate components with diverging behaviour, and no clear owner for the cross-cutting problems that nobody planned for. Two specific examples from migrations I have been part of: two teams independently implemented a modal dialog component, resulting in two different focus trap behaviours in the same application - one team’s modal trapped focus correctly, the other allowed focus to escape to the host page. To users with keyboard-only navigation, these were two different applications wearing the same visual style. The second example: three teams each built a date picker, each with slightly different keyboard interaction patterns, each with slightly different visual styling despite notionally using the same design language. A user navigating between sections of the application was constantly re-learning which date picker they were using.
The design system has to be more than a visual component library. It needs to own: the keyboard interaction patterns for complex widgets, the focus management patterns for overlays and dialogs, the token layer that ensures visual consistency across independently deployed applications, and the versioning strategy that allows teams to upgrade independently without falling permanently behind. Without all four of these, the design system is a partial solution and the fragmentation problems persist at a lower level.
The sequencing matters. Build and stabilize the design system before the migration begins. Not “mostly done” - stabilized, with a clear API contract and a versioning policy. Teams migrating to micro-frontend architecture while the design system is still in flux will make decisions based on an unstable foundation and will need to revisit those decisions as the design system stabilizes. The migration and the design system development compound each other’s scope in ways that are very difficult to manage.
When Should You Not Use Micro-Frontend Architecture?
This is the question that most micro-frontend content avoids, and it is the one that prevents the most expensive mistakes.
Micro-frontend architecture adds complexity. It requires build tooling investment, deployment pipeline sophistication, shared dependency management, cross-application communication patterns, and ongoing governance of the seam boundaries. That complexity is worthwhile when the problems it solves - team autonomy at scale, independent deployability, technology heterogeneity - are real problems the organization is experiencing. It is not worthwhile when those problems are hypothetical.
The specific signals that indicate micro-frontend architecture is the wrong choice: a single product team with fewer than 15 engineers; a monolithic application with no clear domain boundaries in the data model; a team still in product discovery phase where the application architecture is subject to significant change; and an organization without a mature CI/CD pipeline capable of managing independent deployments across multiple applications. These are not excuses to avoid the pattern - they are genuine prerequisite conditions. Attempting micro-frontend architecture without meeting them does not prevent the complexity; it just means the team encounters that complexity without the scale benefits to justify it.
The scale threshold where micro-frontend architecture starts to make sense, in my experience: multiple product teams (more than three) working on the same frontend application, each with independent deployment requirements, and a shared design system that is stable enough to serve as a contract between teams. Below that threshold, a well-structured monorepo with clear module boundaries gives most of the team-autonomy benefits with a fraction of the operational complexity.
Frequently Asked Questions
What is the difference between micro-frontends and Module Federation?
Micro-frontends is the architectural pattern - composing independently deployable frontend applications into a unified user experience. Module Federation is a Webpack (and Rspack) feature that is one mechanism for implementing that pattern at runtime. Module Federation handles the runtime code sharing and dependency deduplication that micro-frontend composition requires. You can implement micro-frontends without Module Federation (using iframes, or build-time composition via npm packages), but for most modern web applications, Module Federation is the most practical runtime composition mechanism.
How do you share state between micro-frontends?
The simplest and most reliable approach is to minimize shared state. Each micro-frontend should own its state and expose a clean API for the data other applications need. When cross-application state is unavoidable - authentication state, global notification state, shared navigation state - use a lightweight publish-subscribe event bus rather than a shared store. A shared Redux or Zustand store across micro-frontends creates tight coupling that eliminates the independence that justifies the pattern in the first place.
When should you not use micro-frontends?
Avoid micro-frontend architecture when: you have fewer than three product teams working on the same frontend; your application lacks clear domain boundaries in the data model; your CI/CD pipeline is not mature enough to support independent deployments; or your design system is not stable enough to serve as a reliable contract between teams. The pattern adds significant operational complexity - it is only justified when the scale of team coordination problems makes that complexity worthwhile.
How do you prevent UX fragmentation in a micro-frontend system?
The design system is the primary defense against UX fragmentation. It must cover: visual components, keyboard interaction patterns for complex widgets, focus management patterns for overlays and dialogs, and a token layer that ensures visual consistency across independently deployed applications. Beyond the design system, establish a cross-team frontend platform team that owns the shared infrastructure - the Module Federation configuration, the design system, the deployment pipeline, and the seam boundary governance. Without a clear owner for these cross-cutting concerns, fragmentation accumulates incrementally until it is too expensive to fix.
Keep reading
More publications
AI Code Review for Frontend Teams - Integrating Without Losing Engineering Judgment
AI code review for frontend catches pattern violations fast but risks crowding out the design conversations that build teams. Here is how to integrate it without losing what matters.
Web Accessibility Best Practices for Modern Applications
Web accessibility best practices mean embedding WCAG compliance into your token layer, component library, and definition of done - not treating it as an audit phase after the product ships.
CSS Container Queries in Production - A Year In
CSS container queries shipped across a 200-component design system in 2023. Here is what the compositional win looks like in practice, the naming overhead nobody warns you about, and the style query gap.
About the author
Sandeep Upadhyay
Principal Frontend Engineer & UI/UX Director
I architect accessibility-first enterprise design systems adopted by Fortune 500 financial, insurance, and technology organizations, reducing regulatory risk and long-term development cost at scale.


