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
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 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.
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