For most of a decade I worked on a dispute platform that runs in a regulated, multi-region environment, at a scale you can describe as roughly 300,000 disputes a day against tens of billions of records. I’m deliberately vague about the employer; the architecture story is the part I can tell, and it’s the interesting part anyway.
When I joined the frontend, it was one big React application. One codebase, one build, one deploy, five teams. If you’ve worked in that situation you already know the failure mode: nobody ships on their own schedule. Your tested, ready feature sits behind someone else’s half-finished migration because you both ride the same deploy train. A regression in a corner you’ve never touched can block your release. At small scale that’s annoying. At this scale it was the actual bottleneck, and it had nothing to do with how fast anyone could write a component.
So the real question was never “how do we write better React.” It was “how do five teams ship independently without the app turning into five apps wearing a trench coat.”
The answer was Module Federation: five micro-frontends, each built and deployed on its own cadence, composed at runtime by a host shell. That part is easy to say and there are a hundred blog posts about it. What those posts undersell is that federation doesn’t remove the coordination problem, it moves it. You stop coordinating deploys and start coordinating contracts, and contracts at runtime are harder to reason about than a shared build, because the failures don’t show up until two independently-shipped things meet in a real browser.
Three things had to be true or the whole thing fell over:
One copy of the important singletons. React, the router, and the design system are shared as singletons. If two micro-frontends each drag in their own React, you get two React trees fighting over the same DOM and a class of bug that will eat a whole afternoon before you realize what you’re looking at. The singleton contract is non-negotiable, and enforcing it across teams who each “just want to bump their version” is a political problem as much as a technical one.
A design system that’s genuinely the source of truth. We ran a shared component library of 60-plus components across 190-something source files, published once and consumed everywhere. That’s what kept five independently-deployed surfaces looking like one product instead of five slightly-different shades of “close enough.” When this slips, users notice before you do.
Versioned boundaries between host and remotes. A remote needed to evolve its internals without forcing a lockstep release of the shell, which would have reintroduced the exact coupling we were trying to kill. So the interface between host and remote was versioned and treated like an API, because that’s what it is.
The part that genuinely sucked, and the part I’d want to talk through in an interview, was debugging the runtime. A micro-frontend could be green in its own CI, green in isolation, and still detonate in the composed app because a shared dependency drifted or a remote shipped a contract the host didn’t expect. There’s no single repo to bisect. You’re debugging the space between the deployments. Getting good at that, and building the guardrails so other people didn’t have to be, was most of the actual work.
I’m keeping the scale numbers as context about the environment, not as a “look what I personally did” stat, because that’s the honest version and because anyone senior can smell an inflated metric from across the room.