Nicolás Duque

Micro-frontends with Module Federation: what we gained and what it cost

Jan 14, 2026 · 3 min read

From 2020 to 2022 I was a senior frontend engineer at Doktor24 in Stockholm, a digital healthcare platform used by patients, practitioners and partners. One of the bigger pieces of work I was part of was co-designing a new micro-frontend architecture with Webpack Module Federation.

This isn't a tutorial. It's the reasoning behind the decision, and what I'd tell a team considering the same move.

The problem we were solving

Several teams worked on the same web product. When everything lives in one frontend application, every team shares one build, one release and one set of dependencies. A change in one area waits for, or breaks, another. The code isn't the bottleneck; the release process is.

The goal was simple to state: let each team deliver its part independently, without giving up a single, coherent product for the user.

Why Module Federation

Module Federation lets separately built applications load code from each other at runtime. A host application pulls in "remote" modules that other teams build and deploy on their own. That gave us what we wanted:

  • Each team owns a remote, with its own repository boundaries, build and deploy.
  • The host composes them into one experience.
  • Shared libraries, like React, can be declared as shared so they aren't loaded several times.

The trade-offs nobody puts on the slide

Micro-frontends move complexity; they don't remove it. These are the areas where you pay:

Shared dependencies

If two remotes need different major versions of a shared library, you have a problem at runtime, not at build time. You need an explicit policy: which libraries are shared, who is allowed to upgrade them, and how upgrades are coordinated. "Everyone just uses the latest" works until it doesn't.

Contracts and versioning

A remote is effectively a public API for the host. Props, events and routes become contracts. Changing them casually breaks another team's deploy. Treat them like an API: document them, version them, and change them in a backwards-compatible way.

UX consistency

Independent teams drift. Buttons, spacing, loading states and error messages start to look slightly different in each area. A shared design system and shared UI components are not optional in this model; they're what keeps the product feeling like one product.

Local development and debugging

Running the whole product locally now means running several applications, or pointing at deployed remotes. Errors can come from code your team didn't build. Invest early in a good local setup and clear ownership of each remote.

Performance

More independently built bundles means more to download if you aren't careful. Shared dependencies and sensible loading boundaries matter.

When I wouldn't do it

Micro-frontends are an organizational tool first. I'd avoid them when:

  • One team owns the frontend. You pay all the costs and get none of the independence.
  • The product boundaries aren't clear yet. Splitting early freezes the wrong seams.
  • Your release pain is really a testing or CI problem. Fix the pipeline before you split the app.
  • You don't have a design system. You'll ship inconsistency at scale.

A well-structured single application, with clear module boundaries inside it, gets you surprisingly far.

What I'd recommend

If you do go this way:

  1. Split along team and domain boundaries, not technical layers.
  2. Decide up front which dependencies are shared and who owns upgrades.
  3. Treat every remote's interface as a versioned contract.
  4. Build the design system first, or at least in parallel.
  5. Make the local developer experience a first-class deliverable.

The architecture worked for us because the organizational problem was real: several teams, one product. That's the question to ask before anything else. The next piece of that platform, the backend-for-frontend layer, is in Backend for frontend: one API per kind of client.