Microfrontend Architect
Microfrontends solve an organizational problem with a technical mechanism. Most failed implementations fail because someone applied the mechanism without having the problem. The first job of this skill is therefore to check the fit, and the second is to build the thing properly if the fit is real.
Step 1 — Check whether microfrontends are the right answer
Ask about, or infer from the repo:
| Signal | Microfrontends help | Microfrontends hurt |
|---|---|---|
| Team count | 3+ teams shipping independently | 1 team, one release train |
| Deploy coupling | Release blocked waiting on other teams | Deploys are already fast and independent |
| Domain edges | Clear bounded contexts (checkout, search, admin) | Heavy shared state across every screen |
| Tech drift | Legacy Angular/React mix to strangle incrementally | Uniform stack, no migration pressure |
| Ops maturity | Can run several pipelines and CDNs | No CI budget, one deploy pipeline |
If most signals sit in the right-hand column, say so directly and propose the cheaper alternative — an Nx monorepo with enforced module boundaries and lazy-loaded routes gives most of the code-ownership benefit at a fraction of the runtime cost. Recommending against microfrontends when they do not fit is a correct outcome for this skill, not a failure to complete the task.
Step 2 — Choose the composition model
Three models, and the choice drives everything downstream:
Build-time composition — remotes published as npm packages, host installs them. Simple and type-safe, but every remote release requires a host rebuild and redeploy. Choose when independent ownership matters but independent deployment does not.
Runtime composition (Module Federation) — the default recommendation. The host fetches
remoteEntry.js at runtime, so a remote deploys by uploading files to a CDN. Choose when
teams must ship on their own cadence. Everything below assumes this model unless stated.
Server-side composition — edge or server assembles fragments (Next.js Multi-Zones, ESI, Module Federation with RSC). Choose when SEO and first-paint dominate and the shell is largely static content around independently owned regions.
Step 3 — Draw the boundaries before writing config
Boundaries follow domains, not layout. A "header remote" and a "footer remote" are a
classic anti-pattern: they split by pixel position, so a single feature change touches
every remote. Split by business capability instead — checkout, catalog, account —
each owning its own routes, state, and API calls end to end.
Record the decision in an architecture decision record before any code. Use
references/adr-template.md.
Step 4 — Scaffold
Full working configuration lives in references/module-federation.md (webpack 5 and
Rspack/Vite variants, Nx generators, TypeScript remote typing, dynamic remotes driven
by a manifest). Read it before writing config — the shared-dependency block in particular
is where most implementations go wrong, and the file explains why each field is set.
Baseline layout this skill produces:
apps/
shell/ host — routing, auth, layout, remote registry
checkout/ remote — independently deployable
catalog/ remote — independently deployable
libs/
shared/ui/ design system, published as a singleton
shared/auth/ token store + refresh, singleton
shared/event-bus/ typed cross-remote messaging
shared/types/ contracts between host and remotes
tools/
remote-manifest.json environment-specific remote URLs
Step 5 — Get the five hard parts right
These are the questions an interviewer or a staff engineer will ask, and the parts that break in production. Address each explicitly rather than leaving them implicit.
Shared dependencies. React, React DOM, and any context-carrying library must be
singleton: true with a requiredVersion pulled from the root package.json. Two React
copies produce "invalid hook call" errors that appear only after a remote upgrades
independently. Set strictVersion: false in production so a minor drift degrades to a
console warning rather than a white screen, and fail the build in CI instead — a version
conflict should be caught by a pipeline, not by a user.
Cross-remote communication. Ranked best to worst: URL state (shareable, survives
reload) → a typed custom-event bus on window → a shared singleton store. Never import
one remote's internals from another; that recreates the coupling microfrontends existed to
remove. Define the event contract in libs/shared/types so both sides break at compile
time when it changes.
Failure isolation. Every React.lazy(() => import('checkout/Cart')) gets an error
boundary with a real fallback, because a remote can be unreachable — bad deploy, CDN
outage, expired CORS. The shell must degrade gracefully to a message or a server-rendered
alternative, never a blank page. Pair this with a timeout on the remote fetch.
Versioning and contracts. Remotes expose a deliberately small public surface — a route
component and a typed props interface, nothing else. Treat exposes as a published API:
additive changes ship freely, breaking changes require a new exposed path
(./CartV2) with an overlap window while consumers migrate.
Performance budget. Federation costs an extra network round trip per remote plus duplicated non-shared dependencies. Set a budget per remote (suggest 150 KB gzipped for the entry chunk), prefetch remote entries on route hover, and audit shared-scope hits after every integration. If total JS grows more than roughly 20% versus the monolith, the sharing config is wrong.
Step 6 — Deployment and CI
Each remote deploys independently: build → upload to versioned CDN path
(/checkout/2026-03-11-a1b2c3/remoteEntry.js) → update the manifest the shell reads at
runtime. Versioned paths make rollback a manifest edit rather than a rebuild.
In an Nx workspace, nx affected -t build,test,lint gates the pipeline so untouched
remotes are not rebuilt. Add a contract test job that boots the shell against the
previous production manifest for every remote — that catches the integration break that
unit tests structurally cannot. See references/nx-monorepo.md for the affected-graph
setup, module boundary lint rules, and a full CI matrix.
Reviewing an existing implementation
When asked to review rather than build, work through this order and report findings as severity + evidence + fix, most severe first:
- Duplicated React or router in the bundle — grep the stats file for multiple copies
- Remotes importing across remote boundaries instead of through shared libs
- Missing error boundaries around lazy remote imports
strictVersion: truein production sharing config- Boundaries split by layout rather than by domain
- Remote URLs hardcoded per environment instead of manifest-driven
- No contract or integration test between host and remotes
- Shared libs importing from apps — an inverted dependency that breaks independent builds
Output format
For a design task, produce: fit assessment → composition model with rationale → boundary map → working config → the five hard parts addressed → deployment plan. For a review, produce the findings table above followed by a prioritized remediation sequence.
Keep the rationale visible. A microfrontend setup that nobody on the team can explain will be dismantled within a year, so the reasoning is part of the deliverable.