Micro-Frontend

Shipping a micro-frontend shell on Module Federation. The contracts that kept it sane.

Why the registry and design-system packages matter more than the remotes themselves, the runtime errors that nearly killed the rollout, and the team-topology pattern that made independent deploys actually safe.

7 remotes
composed behind one shell host
1 registry
single source of truth for routing & versions
Singleton DS
one design system & framework copy, shared

The problem

A single front-end owned by many teams becomes a queue: every change waits behind every other change, and a release is only as fast as the slowest contributor in it. The goal was to let teams build and deploy independently behind one coherent shell — without the UI fracturing into a set of visually inconsistent islands that happen to share a domain.

That's the real tension in micro-frontends. Independence and coherence pull in opposite directions: total independence gives you a Frankenstein UI, total coherence gives you the monolith back. The architecture has to buy autonomy without spending consistency.

The contracts that mattered

The remotes were the easy part — Module Federation makes loading remote code almost trivial. What kept the system sane were the shared contracts the shell enforced around them:

  • The registry. A single source of truth mapping each route to the remote and version that serves it, owned by the platform team rather than negotiated ad hoc between feature teams. Promotion goes through the registry, so "what is live where" is always answerable.
  • The design-system package. Shared as a versioned singleton so the whole surface speaks one visual language. Declaring it (and the framework runtime) singleton: true in the Module Federation shared config is what stops two copies loading and fighting at runtime.
  • Shared dependency strategy. The framework runtime, the design system, and cross-cutting libraries are singletons with an agreed required-version range; everything else stays local to each remote. The discipline is keeping that singleton set small and deliberate.

The runtime errors that nearly killed the rollout

The failure mode that hurts most in Module Federation is the one you can't see in any single repo: version skew between independently built remotes. When two remotes resolve different copies of the framework runtime or the design system, you get the classic duplicate-instance failures — broken hooks, context that silently doesn't match, styling that drifts — and they only appear once the remotes are composed together at runtime, not in any team's local build.

A micro-frontend that can crash the shell isn't independent — it's a shared liability with extra steps.

Two things contained it. First, the singleton + required-version contract above, so shared dependencies resolve to one copy instead of many. Second, treating every remote as untrusted at load time: the shell wraps each one in an error boundary with a fallback, so a remote that fails to load or throws on mount degrades to a contained, recoverable state instead of taking the whole page down with it.

The team topology that made it safe

The architecture only works if it mirrors the org. Each feature team owns its remote end to end; a platform team owns the shell, the registry, and the design system — the shared contracts everyone depends on. That boundary is the point: feature teams move fast inside their remote without coordination, and the one set of things that must stay consistent has a single, clear owner. Most micro-frontend efforts that fail get the technology right and this part wrong.

Outcome

The win is organizational, delivered through architecture: teams ship on their own cadence instead of queuing behind a shared release, while the registry and singleton design system keep the composed product coherent. The contracts — not the remotes — are what make that safe.

The honest caveat: micro-frontends are the right call when independent team velocity is the actual bottleneck. If you don't have multiple teams genuinely blocked on each other, the coordination overhead of remotes, a registry, and a shared-dependency contract usually costs more than the monolith it replaces.