In a microfrontend architecture, a 'shell' application loads several independently built and deployed fragments (e.g. a header team's app, a checkout team's app) at runtime. What is a 'stable integration contract' between these teams, and why do they need one instead of just having each fragment reach into the others' code or DOM directly?
answer
- props/events/tokens = the three contract channels
- no cross-repo compiler
- independence needs a stable interface
- Conway's Law → team boundaries = code boundaries
- explicit > implicit surface
basics
~20 sA contract is the agreed set of inputs (props), outputs (events), and shared visual rules (design tokens) two teams promise not to change without warning, so each team can update its own code without breaking the others.
solid answer
~40 sThe integration contract is the explicit surface area a microfrontend exposes to the rest of the system: the props/config it accepts, the custom DOM events or messages it emits, and the design tokens it consumes to stay visually consistent. Because microfrontends are built, tested, and deployed by different teams on independent timelines, there's no shared compiler or build step to catch a breaking change across team boundaries — a checkout team renaming an event field can silently break the shell in production with no build error anywhere. Treating that surface as an explicit, documented, versioned artifact — not 'whatever the code happens to do today' — is what lets teams ship independently without a cross-team release-coordination meeting every time.
go deeper
Can state that microfrontends need an agreed-upon interface (props/events) and give a rough reason ('so teams don't break each other'), even without full precision on why a compiler can't catch it.
Names all three contract channels (props, events, design tokens) and explains that separate deploy pipelines mean no shared compiler catches violations.
Connects the contract explicitly to independent deployability and Conway's Law, and can describe concrete failure modes (payload drift, token drift) from experience.
Frames the contract as the mechanism that converts organizational independence into technical independence, and can discuss how to formalize and enforce it (shared typed packages, contract tests) at multi-team scale.
## What the shell is holding together A microfrontend architecture splits a single web application into several independently deployable pieces, each owned end-to-end by a different team, and stitched together at runtime by a **shell** (also called a container or host app). The shell might mount a checkout fragment inside a route, alongside a header fragment and a recommendations fragment, using a mechanism like Webpack Module Federation, single-spa, iframes, or web components. Because these pieces are compiled and deployed separately, there is no compiler pass that spans team boundaries — TypeScript in the checkout repo cannot type-check against the shell's expectations, and vice versa. The only thing standing between "this works" and "this silently breaks in production" is a contract everyone has agreed to honor. ## The three channels That contract has (at minimum) three channels. 1. **First, the props/config API:** whatever data the shell passes into a fragment at mount time — a `locale` string, a `cartId`, a callback for navigation — forms an implicit function signature that the fragment's code depends on. 2. **Second, custom events (or a pub/sub bus):** fragments communicate sideways and upward not by calling each other's internal functions but by emitting named events with a defined payload shape, e.g. a checkout fragment dispatching a `checkout:completed` CustomEvent with `{ orderId, total }` that the shell listens for to show a confirmation banner. 3. **Third, shared design tokens:** CSS custom properties or a tokens package (colors, spacing units, typography scale, breakpoints) that every fragment consumes so the seams between teams' UIs are invisible to the end user, even though the code was written by different people in different repos. ## Why it must be explicit rather than emergent The reason this needs to be explicit rather than emergent is **Conway's Law** in action: teams are organized around business capabilities (checkout, search, account) precisely so each can ship on its own cadence, without waiting for a company-wide release train. That independence is the entire point of the architecture. - But independence at the deploy layer only works if the *interface* between teams is stable even while the *implementation* behind it changes freely. - If the checkout team is free to refactor its internals but not free to silently rename an event field or add a required prop, then the shell team can build against a known shape and never has to redeploy in lockstep. - The moment a team treats their fragment's runtime behavior as an implicit, undocumented contract, they've reintroduced tight coupling through the back door — the systems are still deployed separately, but they are not actually independent, because nobody can safely change anything without grepping every consumer's source code (which may live in a repo they can't even see). ## The trade-off The trade-off is upfront cost: designing a props API, naming and documenting events, publishing a tokens package, and writing down what "breaking" means takes real design effort that a team moving fast might be tempted to skip in favor of "we'll just call into their global object." That shortcut is fast on day one and catastrophic on day ninety, when the other team ships an unrelated refactor and a production incident traces back to a coupling nobody remembered existed. ## Where it goes wrong Common failure modes include: | Failure mode | What it looks like | |---|---| | **Payload drift** | a field silently renamed or its type changed, e.g. `total: number` becoming `total: { amount, currency }` | | **Design-token drift** | a fragment hardcodes a hex color instead of consuming the token, so a rebrand only updates part of the page | | **Event-name collisions** | two teams independently choose the same custom event name for different purposes, and a listener fires on the wrong payload shape | ## How mature platforms formalize it A concrete, widely-cited real-world pattern: teams building large multi-team frontends (Zalando's Interface Framework and IKEA's microfrontend platform are both public examples) formalize fragment-to-shell communication as custom browser events with a documented, versioned payload schema, plus a shared, independently-versioned design-tokens npm package that every fragment installs as a dependency — so a rebrand or spacing-scale change propagates by a version bump rather than a coordinated multi-repo edit. The contract is the thing that makes "independently deployable" actually true rather than aspirational.
- Should the props API and event schema be typed and published as a shared package, or just documented in a wiki?A shared, versioned package (TypeScript types, JSON Schema, or similar) is strongly preferred because it turns contract violations into compile-time or CI-time errors for consumers who pull the latest version, rather than relying on someone remembering to check a wiki page. A wiki-only contract still works in principle but degrades fast in practice because nothing enforces it — teams drift and only discover breakage in production.
- What happens if the shell needs data that isn't in the current contract — should it reach into the fragment's internal state as a stopgap?No — that reintroduces the tight coupling the contract exists to prevent, even if it's 'just this once.' The correct move is to extend the contract (add a new prop, or a new event field with a default) through the same versioning/negotiation process as any other change, so the dependency stays visible and documented rather than becoming a hidden landmine for the next refactor.
It's like separate contractors renovating different rooms of a house who never talk directly to each other, but all agree on where the load-bearing walls, plumbing risers, and electrical panel are. As long as nobody moves those without telling everyone, each contractor can redesign their own room freely.
saying these in an interview costs you the question
- Describes fragments calling each other's internal functions or reaching into shared global mutable state directly
- Treats the contract as 'whatever the current code does' rather than an explicit, versioned artifact
- Doesn't mention that independent deployability is the actual goal the contract serves
- Ignores design tokens entirely and only talks about data/events
- Assumes a shared build step or monorepo type-checking will catch contract breaks across teams