Best Practices

AI Rules for Micro-Frontends

AI builds monolithic frontends or splits them incorrectly at component boundaries. Rules for vertical slice architecture, Module Federation, shared dependencies, cross-app communication, and deployment independence.

8 min read·February 15, 2025

HeaderApp + SidebarApp + FooterApp — a distributed monolith with triple the bundle size

Vertical domain slices, Module Federation, shared deps, event communication, independent deployment

AI Splits Frontends at the Wrong Boundaries

AI generates micro-frontends with: horizontal splits (header app, sidebar app, footer app — every page load assembles from 5 micro-apps), shared state across apps (global Redux store that couples everything), duplicate dependencies (each micro-app bundles its own React, tripling the page weight), synchronous coupling (App A imports components from App B at build time), and no deployment independence (changing one app requires redeploying all of them). The result is a distributed monolith — all the complexity of micro-frontends with none of the benefits.

Modern micro-frontends are: vertically sliced (each micro-app owns a complete domain: checkout, product catalog, user account), runtime-composed (Module Federation loads apps at runtime, not build time), dependency-shared (one copy of React shared across all apps), event-communicated (custom events or event bus for cross-app messaging, no shared state), and independently deployable (each app has its own CI/CD pipeline and can deploy without affecting others). AI generates none of these.

These rules cover: vertical domain slicing, Module Federation for runtime composition, shared dependency management, cross-app communication patterns, and independent deployment pipelines.

Rule 1: Vertical Domain Slices, Not Horizontal Layers

The rule: 'Split by business domain, not by UI layer. Each micro-frontend owns a complete vertical slice: its routes, components, state, API calls, and data models. Checkout micro-app: /checkout/*, cart components, payment logic, order API. Product micro-app: /products/*, catalog components, search, product API. User micro-app: /account/*, profile, settings, auth API. Each team owns a full feature, not a UI fragment.'

For the boundary test: 'A good micro-frontend boundary passes the deployment test: can this app be deployed independently without breaking other apps? If deploying checkout requires redeploying product catalog, the boundary is wrong — they are coupled. Good boundaries align with: team ownership (one team per micro-app), domain boundaries (bounded context from DDD), and release cadence (apps that change together should be one app).'

AI generates: a HeaderApp, SidebarApp, MainContentApp, and FooterApp — horizontal slices that must all be present on every page, share routing state, and coordinate layout. Changing the header navigation requires testing with every main content variant. Vertical slices: each app is self-contained. The checkout team deploys independently of the product team. True team autonomy.

  • Split by domain: checkout, products, account — not header, sidebar, footer
  • Each micro-app: routes + components + state + API calls — complete vertical slice
  • Deployment test: can this app deploy without breaking other apps?
  • Align with: team ownership, domain boundaries, release cadence
  • One team per micro-app — full ownership from UI to API integration
💡 The Deployment Test

Can this micro-app deploy independently without breaking other apps? If deploying checkout requires redeploying product catalog, the boundary is wrong — they are coupled. Good boundaries align with team ownership, domain boundaries, and release cadence.

Rule 2: Module Federation for Runtime Composition

The rule: 'Use Module Federation (webpack 5 / Rspack) or import maps for runtime composition. The host app loads micro-apps at runtime — no build-time dependency between apps. Module Federation: each micro-app exposes components or routes; the host consumes them via dynamic import. Deployment of one micro-app does not require rebuilding the host or other apps.'

For the host/remote pattern: 'Host (shell app): provides the layout, routing, and shared context (auth, theme). Remotes (micro-apps): expose page components or route modules. Configuration: new ModuleFederationPlugin({ name: "checkout", exposes: { "./CheckoutPage": "./src/pages/Checkout" }, shared: { react: { singleton: true }, "react-dom": { singleton: true } } }). The host dynamically imports CheckoutPage at runtime from the checkout app deployment URL.'

AI generates: import CheckoutPage from '@checkout-app/pages' — a build-time dependency. Every change to checkout requires rebuilding the host. With Module Federation: the host loads checkout at runtime from its deployed URL. Checkout deploys independently; the host picks up the new version on next page load. Zero coordination between teams.

Rule 3: Shared Dependency Management

The rule: 'Share framework dependencies (React, React DOM, router) as singletons across all micro-apps. Module Federation shared config: shared: { react: { singleton: true, requiredVersion: "^19.0.0" }, "react-dom": { singleton: true } }. Singleton ensures: one copy of React loaded (not one per micro-app), consistent behavior (no multiple React instance bugs), and reduced bundle size (React loaded once, shared across all apps).'

For version alignment: 'All micro-apps must agree on the major version of shared dependencies. React 19 in one app and React 18 in another: singleton fails, two copies loaded, hooks break across app boundaries. Strategy: maintain a shared package that pins major versions: @org/shared-deps exports the version constraints. Renovate/Dependabot updates this package, and all apps consume it.'

AI generates: each micro-app with its own React bundled — three copies of React (600KB), three copies of React DOM (1.2MB), and hooks that break when components cross app boundaries (multiple React instances error). Singleton sharing: one React, one React DOM, consistent hooks, 1.8MB saved. The shared config is 5 lines; the savings are enormous.

  • Singleton sharing: react, react-dom, router — one copy across all apps
  • requiredVersion for compatible range — ^19.0.0 allows minor updates
  • Shared deps package: @org/shared-deps pins major versions for all apps
  • Three React copies = 1.8MB wasted + hooks breakage across boundaries
  • Non-shared: app-specific libs are bundled per app — only frameworks are shared
⚠️ Three Reacts = 1.8MB Wasted

Each micro-app bundling its own React: three copies (600KB), three React DOMs (1.2MB), and hooks break across app boundaries. Singleton sharing: one React, one React DOM, consistent hooks. Five lines of config, 1.8MB saved.

Rule 4: Event-Based Cross-App Communication

The rule: 'Micro-apps communicate via custom events or an event bus — never via shared state or direct imports. Pattern: checkout dispatches window.dispatchEvent(new CustomEvent("cart:updated", { detail: { itemCount: 3 } })). Product catalog listens: window.addEventListener("cart:updated", (e) => updateBadge(e.detail.itemCount)). The contract is the event name and payload shape — apps are decoupled.'

For what to communicate: 'Cross-app events should be: infrequent (navigation events, auth state changes, cart updates — not per-keystroke), coarse-grained ("user logged in" not "user typed character in login form"), and documented (event catalog with name, payload type, and source app). If two apps need frequent fine-grained communication, they should probably be one app — the boundary is wrong.'

AI generates: a global Redux store shared across micro-apps — coupling every app to a single state tree. Changing the checkout state shape breaks the product app reducer. With events: checkout emits cart:updated, product listens for it. Checkout can change its internal state freely — the event payload is the only contract. Internal state is private; events are the public API.

Rule 5: Independent Deployment Pipelines

The rule: 'Each micro-app has its own: repository (or monorepo workspace), CI/CD pipeline, deployment URL, and release schedule. Deploying checkout does not trigger a rebuild of products. The host app loads micro-apps from their deployment URLs — updating a micro-app means deploying it to its URL. The host discovers the new version on the next page load (or via a manifest file that maps app names to URLs).'

For the manifest pattern: 'The host reads a manifest at runtime: { "checkout": "https://cdn.example.com/checkout/v2.3.1/remoteEntry.js", "products": "https://cdn.example.com/products/v1.8.0/remoteEntry.js" }. Deploying checkout v2.3.2: update the manifest entry, host loads the new version. Rollback: revert the manifest entry to v2.3.1. The manifest is the deployment contract — a JSON file that maps app names to versioned URLs.'

AI generates: all micro-apps in one build pipeline — changing one line in checkout triggers a rebuild of everything. Deployment takes 30 minutes (build all apps) instead of 3 minutes (build one app). Independent pipelines: the checkout team ships 10 times a day without waiting for, coordinating with, or blocking any other team.

ℹ️ 3 Minutes, Not 30

All micro-apps in one pipeline: changing one line triggers a 30-minute build of everything. Independent pipelines: the checkout team builds and deploys in 3 minutes without waiting for, coordinating with, or blocking any other team. Ship 10 times a day.

Complete Micro-Frontend Rules Template

Consolidated rules for micro-frontends.

  • Vertical domain slices: checkout, products, account — not header, sidebar, footer
  • Module Federation for runtime composition — no build-time dependencies between apps
  • Singleton shared deps: one React across all apps — consistent hooks, less bundle
  • Event-based communication: CustomEvent — never shared Redux store across apps
  • Events are coarse-grained and infrequent — if fine-grained, merge the apps
  • Independent deployment: each app has its own CI/CD, URL, and release schedule
  • Manifest pattern: JSON maps app names to versioned deployment URLs
  • Do not start with micro-frontends — split a monolith only when team scaling demands it