← All posts

How to roll back a micro frontend in 30 seconds (without rebuilding the host)

A bad release is live and the clock is running. Most teams need fifteen minutes to undo it, because the version is baked into the host build. It need not be.

Lorenzo De Francesco· Maintainer7 min read
A production environment panel listing versions v1.4.2, v1.4.1 and v1.4.0, with an arrow moving the selection down one row from v1.4.2 to v1.4.1, beside a stopwatch reading 00:30 and a web application still running.

The release went out twenty minutes ago. Checkout throws on Safari, the error rate is climbing, and someone has already asked the question everybody dreads: how fast can we undo this?

If your micro frontends load through Module Federation and the version each environment serves lives in the host's source, the honest answer is ten to fifteen minutes — on a good day, with CI free and nobody else's pipeline ahead of yours. This is about why that number is what it is, and how to make it thirty seconds.

Why is rolling back a micro frontend so slow?

Because in most setups the rollback is not a rollback. It is a redeploy. The host holds a list of remotes — a file, usually committed — mapping each micro frontend to a URL. Change which version you serve and you have changed the host's source. Changing the host's source means building the host again.

So the fix for a broken checkout widget is a full rebuild and redeploy of the shell containing it. The blast radius of the recovery is larger than the blast radius of the bug.

Where the fifteen minutes actually go

Worth breaking down, because the instinct is to blame the build, and the build is rarely the biggest share.

  1. Someone decides what to revert. Not mechanical: the last deploy may have carried three micro frontends, and only one is broken.
  2. Revert the commit, open a PR. If the branch is protected — and on a host application it should be — that needs a review.
  3. Wait for a CI runner. On a shared plan at three in the afternoon, this is dead time you do not control.
  4. Install, build, test. The host's build is the whole shell: every dependency, every micro frontend reference.
  5. Deploy, invalidate the CDN, wait for the edge to actually drop the old bundle.

Only step four is engineering. The rest is queueing and process, which is why a faster build machine barely moves the number.

What does Module Federation actually give you?

Runtime loading, and that is genuinely a lot: the host fetches a remote's bundle when it needs it, and shared dependencies get negotiated rather than duplicated.

What it does not give you is a decision. Module Federation loads whatever URL you point it at. It holds no opinion about which version staging should serve versus production, keeps no record of what was live yesterday, and offers no way to change the answer without changing the code that contains it. Composition is solved. Version resolution is left as an exercise.

What makes a thirty-second rollback possible?

One change: the version an environment serves stops being a build-time constant and becomes runtime data.

Concretely, the host stops importing a remotes map and starts fetching one. On startup it asks a single question — which versions should I load right now? — and gets back a small JSON document. The bundles do not move; they were uploaded already, immutable already, sitting behind a CDN already. The only thing a rollback changes is which of them the answer points to.

That is why it is fast. Nothing is rebuilt because nothing needs building: the version you are rolling back to was built weeks ago and never deleted.

How do you roll back in thirty seconds?

  1. Find the environment serving the broken version.
  2. Pick the previous version from its history. It is still there — immutable artifacts are not garbage collected on deploy.
  3. Point the environment at it, and confirm.
  4. The next page load fetches the new configuration and mounts the old bundle.

No PR, no CI, no host deploy. The rollback touches one record, and that record is the only thing that was ever environment-specific.

Does this work with both Webpack and Vite?

Yes, and it is the same shape in both. Module Federation is a first-party plugin in Webpack and Rspack, and reaches Vite through @module-federation/vite. What differs is where the remotes map is declared, not that it is declared.

The change is identical either way: instead of writing the remotes object literally into the bundler config, you generate it from configuration fetched at runtime. The bundler still needs the remote names at build time — that part is genuinely static — but the URLs behind those names do not have to be.

What can still go wrong?

Plenty. A piece that says otherwise is selling you something.

  • Shared dependency drift. If the broken version bumped a shared library and the host negotiated around it, rolling back one remote can leave the singleton at a version the older code never saw.
  • In-flight sessions. Users mid-session keep the bundle their browser already holds until they reload. The rollback is instant for new page loads and eventual for everyone else.
  • Configuration caching. If the runtime config sits at the edge with a long TTL, your fast path is only as fast as that TTL. Keep it short, or make invalidation part of the rollback.
  • Contract changes. If the broken release also changed an API contract, the backend has moved on. Rolling the frontend back does not roll the contract back.

None of this makes the approach worse than rebuilding the host. The rebuild has every one of these problems too — plus the fifteen minutes.

And if you would rather not need the rollback?

A rollback is the blunt instrument: something is broken for everyone, and you are undoing it. The considered version of the same mechanism is a canary release. Once the version an environment serves is runtime data, it can also be a different answer for different users — which is how you find out a release is bad while it is still reaching two percent of traffic. We wrote about [how that works, and what it costs](post:a86a0236-46b1-47c8-9715-763f2f8ca97d) separately.

Do you need a control plane for this?

No. The mechanism is not complicated, and you can build it: a JSON document in object storage, a fetch on host startup, something to write the file.

What you end up maintaining is everything around it. Somewhere to see which version each environment serves without opening a bucket. A history, so “the previous version” is a fact rather than a memory. Permissions, because whoever can write that file can change production. An audit trail, for when someone asks who rolled back and when. Validation, so a typo in a version string fails loudly instead of serving a blank page. A path for CI to publish a build and register it in one step.

That is the part that takes the time, and it is the part every team rebuilds. MFE Orchestrator is our answer to it — open source, self-hostable — but the idea outlives the tool: as long as the version lives in the host's build, your rollback time is your host's deploy time. Move the decision out of the build and the number collapses to whatever it takes to write one record.

Thirty seconds is not a marketing figure. It is what remains once you stop rebuilding an entire application in order to change your mind about which version of one widget to serve.