System Design ·18 Jun 2024 ·9 min

Micro Frontends: When Independent Deployment Is Worth It

Micro frontends solve an organisational problem, not a technical one: separate teams deploying into one interface without blocking each other. This covers the real costs, the integration options, deployment and rollback, and when a modular monolith is the better answer.

Pranav Begade By Pranav Begade
Micro Frontends: When Independent Deployment Is Worth It

Micro frontends solve an organisational problem, not a technical one, and most teams that adopt them do not have that problem. The single thing the architecture buys you is the ability for separate teams to deploy separate parts of one interface on separate schedules. If you do not have separate teams blocked on each other's release cycles, you are paying a large bill for a benefit you cannot spend.

That framing is unpopular because the usual list of benefits is much longer. Technology freedom. Fault isolation. Smaller bundles. Incremental migration. Independent scaling. Almost every item on that list is either untrue, or true but available far more cheaply inside a single well structured application. Fault isolation is an error boundary. Smaller initial payload is route level code splitting. Incremental migration off an old framework is a reverse proxy and a route table, and it is temporary by design. None of those require you to split your build.

What follows is the honest version: what the architecture actually does, what it costs, how the integration options differ, and how deployment works in practice, which is the part people search for and the part most articles cover last. There is also a table for deciding whether you need any of this, and a lot of its rows say no.

The only benefit that is genuinely hard to get another way

Picture a single interface with five teams shipping into it. Search, catalogue, checkout, account, and a merchandising team that owns the homepage. In a single application with a single pipeline, they share a release. That means they share a merge queue, a failing test, a rollback, and a calendar. When checkout finds a bug at 4pm, the merchandising team's finished work sits unreleased until it clears.

Micro frontends break that coupling. Checkout builds and deploys its own bundle, on its own pipeline, at its own cadence, and the shell composes it at run time. Nobody waits. That is the product you are buying, and it is real. Coordination cost in a shared codebase grows faster than headcount, and past a certain size the release calendar becomes the bottleneck rather than the engineering.

The question is whether you are past that size. Five teams is roughly where the pain starts to justify the cure. Two teams is not. One team with a large codebase is definitely not, and a large codebase is a modularity problem, not a deployment problem.

The signals that you actually have the problem

You have the organisational problem micro frontends address if several of these are true, not one:

  • There is a recurring meeting whose only purpose is agreeing what ships this week.
  • Teams routinely delay finished work because another team's change is not ready.
  • A rollback by one team reverts work belonging to three others.
  • You acquired a product and now run two frameworks in one interface, with no plan to converge for a year or more.
  • A long migration, Angular to React or an equivalent, needs both stacks live in production side by side for many months.
  • Teams are in different time zones and cannot share a release window without someone working nights.

If your honest answer is that the codebase is messy and builds are slow, that is a different problem with a much cheaper fix. Module boundaries, an enforced dependency graph, and a faster bundler will do more for you than a distributed frontend, and they will not add a runtime failure mode.

What independent deployment actually costs

This is where most explainers get quiet, so here is the bill in detail. None of these are unsolvable. All of them are ongoing work that a single application does not require.

  • Bundle duplication. Each piece ships its own dependencies unless you carefully share them. Three teams on React means either three copies of React on the wire, or a shared singleton that every team must upgrade together, which reintroduces the coupling you were escaping.
  • Cross application communication. The header needs to know the cart count. The cart lives in another deployable. You now need a contract, usually a custom event bus or a shared store, and that contract is untyped across a bundle boundary unless you invest in generating types from it.
  • Design consistency. Two buttons rendered by two teams must look identical. That means a versioned component library, a release process for it, and a policy for how long old versions stay supported.
  • Routing across boundaries. Someone owns the URL space. Deep links, back button behaviour, and route transitions between two independently loaded applications are a real source of bugs, especially where one piece renders inside another.
  • Authentication and session. A token refresh must not race between three pieces that each think they own it. Silent session expiry that logs out one region of the page and not another is a genuinely bad user experience, and it is easy to build by accident.
  • Debugging across a boundary. A stack trace that crosses from the shell into a remote bundle is harder to read, source maps need to resolve per remote, and error reporting needs to attribute the failure to the right team.
  • Observability. You need per piece error rates, per piece performance, and a way to correlate a single user session across all of them. This is not free tooling work.
  • Local development. A developer changing the header may need the shell plus two remotes running locally to see their change in context. Teams solve this with proxying to deployed remotes, which works until the deployed remote is broken.

The worst of them is version skew. Two regions of the same screen can be running different builds of a shared dependency at the same moment, because they deployed at different times. That is not a hypothetical. It is the normal steady state of the architecture, and it produces bugs that only appear in production, only for some users, and only for the window between two deployments.

Then there is the cost the user pays. More JavaScript, more requests, more parse time, and a run time composition step before anything renders. A shell that fetches remote entry points before it can paint has added a network round trip to the critical path. That shows up in Largest Contentful Paint, and it shows up on mid range Android phones long before it shows up on your laptop.

Four ways to integrate, and when each one fits

Build time integration

Each piece is published as a versioned package and the shell installs them. Simple, well understood, and it gives you exactly none of the independent deployment benefit: shipping a change means bumping a version and redeploying the shell. Useful as a shared component library. Not useful as a micro frontend strategy, because the coupling is still there.

Run time integration through module federation

The shell loads remote bundles at run time, and dependencies can be declared as shared singletons. This is what most people mean when they say micro frontends today. It is the only mainstream option that delivers genuine independent deployment with a shared dependency graph. It is also where version skew, shared singleton negotiation, and remote load failure handling all live. Choose it when independent deployment is the actual goal and you have the engineering capacity to own the failure modes.

Iframes

Unfashionable and honestly underrated for a narrow case. You get real isolation of CSS, JavaScript, and crashes, with almost no shared machinery. You pay for it with awkward routing, awkward resizing, focus and accessibility problems, duplicated dependencies, and communication limited to message passing. If the embedded thing is a genuinely separate application, a legacy admin tool or a third party module, an iframe is often the correct and cheapest answer. If it is one region of a shared page that needs to feel native, it is not.

Server side or edge composition

Fragments are assembled into one HTML document before the browser sees it, at the origin or at the CDN edge. The user gets a single document and good first paint, which is the strongest option for content heavy pages that need to perform well in search. The cost moves to your infrastructure: fragment timeouts, caching per fragment, and a composition layer that becomes a shared dependency of its own. Worth it when page performance is a business metric and the pieces are largely static per request.

Micro frontend deployment in practice

Deployment is the whole point, so it deserves more than a paragraph about CI tooling.

Independent pipelines mean independent artefacts. Each piece builds to an immutable, content hashed bundle at a stable URL, and the shell resolves which version to load from a manifest rather than from a hardcoded path. Deploying is then two steps: publish the new bundle, then update the manifest entry. Keep those steps separate. It makes rollback trivial, because rolling back is just pointing the manifest at the previous hash, with no rebuild and no pipeline run.

Contract testing replaces the integration test you no longer have. Once teams deploy independently, nobody runs a build that contains all the pieces. So the interfaces between them need to be pinned: the props the shell passes to a remote, the events a remote emits, the shape of the shared auth context. Each team tests its side against the recorded contract in its own pipeline, and a contract change becomes an explicit, versioned event rather than a surprise at run time. Without this, the architecture quietly degrades into a distributed system with no schema.

Rolling back one piece must not require rolling back the others. This works only if the pieces are backward compatible across at least one version. Practically: additive changes only on shared contracts, deprecate before you remove, and keep the previous bundle live and reachable rather than deleting it on deploy.

Shared dependency upgrades are the failure case to design against. If upgrading the shared framework or the design system forces every team to release together on the same day, you have rebuilt the release train inside a more complicated architecture. Avoid it by allowing a version range rather than one exact singleton, by supporting two adjacent major versions of the component library at once, and by treating shared dependency upgrades as a migration with a deprecation window rather than a coordinated cutover. If you cannot do that, you do not really have independent deployment, and it is worth asking what the split is still buying.

Should you use micro frontends? A decision table

Your situationWhat to do
One team, any codebase sizeModular monolith. Enforce module boundaries and split routes lazily. There is no deployment coupling to break.
Two or three teams, one framework, shared release is occasionally annoyingModular monolith with clear code ownership and feature flags. Cheaper than the alternative by a wide margin.
Five or more teams, release calendar is the bottleneckMicro frontends via run time integration. This is the case the architecture was designed for.
Post acquisition, two frameworks, must coexist for a year or moreRun time integration, or iframes if the products stay genuinely separate. Set a convergence date anyway.
Long framework migration, both stacks live for monthsRoute level split behind a proxy. Temporary by design, removed when the migration finishes.
Embedding a legacy admin tool or a third party applicationAn iframe. Isolation is the requirement, and you get it almost for free.
Content heavy pages where first paint is a business metricEdge or server side composition, not client side composition.
Builds are slow and the codebase is tangledFix the build and the module graph. Splitting deployments will not fix either, and will add new failure modes.
Startup pre product market fitOne application. Optimise for changing your mind cheaply, which a single codebase does best.

The cheaper architecture that gets you most of the way

Before splitting deployments, exhaust the options inside one application. A well modularised codebase with enforced boundaries gets you most of the isolation. Enforced means a lint rule or a dependency checker that fails the build when the checkout module imports from the search module's internals, not a wiki page saying please do not. Boundaries that are only social do not survive a deadline.

A shared component library gets you visual consistency without any run time composition, and it is worth building even if you never split anything. Writing the design rules down early is what makes work distributable: on Nestlet, our in house marketplace for short term residential leases, a full design guideline document was written up front so the build could be handed to different developers or coding agents and come back consistent. That is the same problem a micro frontend estate has, solved with a document instead of a distributed system.

Feature flags give you the independent release timing that most teams actually want. Merge continuously, deploy continuously, and let each team decide when its work becomes visible. That covers a surprising share of the pain people attribute to shared releases, at a fraction of the cost. Add clear code ownership so reviews route to the right people automatically, and you have removed most of the coordination overhead without changing your architecture at all.

Our published position on this is unchanged: a well structured monolith with a proper deployment pipeline carries a product a very long way. Most teams reach for distribution well before the coupling actually hurts, and then spend the following year paying for it. If you are weighing the build effort either way, the web and web app cost guide sets out what each level of complexity realistically costs, and our approach to web application development starts from the smallest architecture that fits the team you have, not the one that fits the team you might have in three years.

Part of this decision is about people rather than code. If the real constraint is parallel throughput, adding dedicated developers to a well modularised codebase is usually faster and less risky than distributing the frontend for teams you have not hired yet. Denti360, our dental practice management SaaS, is multi branch, in daily production use, and still a single application.


Not sure whether your frontend pain is an architecture problem or an org chart problem? A Scoping Sprint ($2,300, two weeks) ends with a frontend architecture recommendation made for your team and release process, a prototype, and a fixed quote. Or just start a conversation.

Frequently asked

How does micro frontend deployment actually work?
Each piece builds to an immutable, content hashed bundle published at a stable URL. The shell reads a manifest to decide which version of each remote to load, so deploying is two separate steps: publish the bundle, then update the manifest. Keeping those steps apart makes rollback instant, because reverting means pointing the manifest back at the previous hash with no rebuild and no pipeline run.
What is a micro frontend architecture, in plain terms?
It is a way of composing one user interface from several separately built and separately deployed applications, usually assembled at run time by a shell. It extends the microservices idea to the browser. The defining property is not that the code is split, it is that each piece ships on its own schedule without waiting for a shared release.
What are the real micro frontend benefits?
One benefit is genuinely hard to get another way: separate teams deploying separate parts of one interface without blocking each other. The rest of the usual list, smaller bundles, fault isolation, incremental migration, is available more cheaply inside a single application through code splitting, error boundaries, and a route level proxy. Buy the architecture for the deployment independence or do not buy it.
When should you not use micro frontends?
When you have one to three teams, one framework, and no recurring release coordination pain. Also when the actual complaint is slow builds or a tangled codebase, since splitting deployments fixes neither and adds run time failure modes. A modular monolith with enforced boundaries, feature flags, and clear code ownership solves most of what teams are reaching for.
How do you build a scalable frontend without micro frontends?
Modularise the codebase and enforce the boundaries mechanically, with a lint rule or dependency checker that fails the build on a forbidden import. Add route level code splitting, a versioned shared component library, feature flags so teams release on their own timing, and code ownership rules that route reviews automatically. That covers most coordination pain at a fraction of the cost.
What is the biggest hidden cost of a micro frontend application?
Version skew. Two regions of the same screen can run different builds of a shared dependency at once, because they deployed at different times. That is the normal steady state, not an edge case, and it produces bugs that appear only in production, only for some users, and only during the window between two deployments.
Does splitting into micro web apps hurt performance?
Usually yes, at least a little. Client side composition adds a network round trip before anything renders, duplicated dependencies add payload, and run time assembly adds parse and execution time. It shows up in Largest Contentful Paint and on mid range phones first. Edge or server side composition avoids most of it if first paint matters to the business.
How do you stop a shared dependency upgrade turning into one coordinated release?
Allow a version range instead of a single exact singleton, support two adjacent major versions of the shared component library at once, and treat upgrades as migrations with a deprecation window rather than a same day cutover. If every team must ship together to upgrade, you have rebuilt the release train inside a more complicated architecture.
Fixed price · $2,3002-week sprint

Building something in this space?

We turn ideas into buildable plans in 2 weeks: clickable prototype, technical plan, fixed quote. Fixed price, credited against the build.

See the Scoping Sprint

Start your journey towards building scalable web applications with micro-frontends today!

Start a project →
Book a 15-min scoping call