Omie Holley
← All case studies

Testing Against Production in Seconds: Module-Federation Override Tooling

Built a Webpack 5 runtime-override system and browser extension for a 60-micro-frontend platform whose lower environments did not match production. Developers could test local or branch builds against production conditions in seconds.

Role
Solutions Architect
Where
Multi-billion-dollar equipment-rental company (consulting engagement)
  • TypeScript
  • Webpack 5 Module Federation
  • Browser Extension APIs
  • React

Context

A customer-facing platform consisted of 60 micro-frontends owned by four teams and composed with Webpack 5 Module Federation. Its lower environments did not match production. Production was the only environment with representative data, user behavior, and authentication flows.

The Problem

To validate a change under realistic conditions, developers spent hours assembling partial environments that still failed to reproduce production behavior. Bugs crossing micro-frontend boundaries were often discovered only after release.

The Solution

I built a runtime-override system and a custom browser extension. A developer can point an individual micro-frontend to a local or branch build while browsing the production site, without changing the experience for anyone else.

How It Works: Promise-Based Dynamic Remotes

While researching Webpack Module Federation internals, I used promise-based dynamic remotes instead of hardcoding remote URLs at build time. Each host resolves its remote container through a promise evaluated at runtime. On page load, that promise checks localStorage for an override and resolves the developer's local or branch remoteEntry.js when one is set; otherwise, it resolves the production remote.

The browser extension writes the override entries to localStorage. It is brand- and environment-aware: it identifies the current domain and scopes an override to that domain. An override for MFE #12 on one brand's production site therefore does not affect the same MFE on another brand or in another environment.

The isolation comes from the browser storage model. localStorage is scoped by browser and origin, so an override exists only in the individual developer's browser on the domain where they set it. No server-side production configuration changes are required.

flowchart LR
    Ext["Browser extension<br/>(brand & environment aware)"] -->|writes overrides| LS[("localStorage<br/>per browser, per domain")]
    LS -->|read at runtime| PR["Promise-based dynamic remotes<br/>in each host's webpack config"]
    Dev["Developer's local build<br/>serves remoteEntry.js"] --> PR
    PR --> MFE1["MFE #12 overridden ✅"]
    PR --> MFE2["59 other MFEs unchanged<br/>for every other user"]

Deployment Tooling

Release coordination had the same underlying problem: the dependency knowledge was informal. To deploy one application, an engineer opened Jenkins, started a job, inspected the app in the IDE to determine which dependencies belonged in the release and could be triggered early, entered version numbers manually, then waited for completion before repeating the process for each downstream application. Manually entered version numbers were frequently wrong.

I built a dependency-aware release orchestrator. The workflow became: pull the release plan from Confluence with the extension, paste it into the release-plan parser, save, and deploy.

Under the hood:

  • Dependency manifest: JSON declared each application's dependencies. The tool rendered the release as nodes and connecting lines across lanes, making deploy order visible.
  • Live release state: the extension cloned the developer's Jenkins session cookie and reused it with the Jenkins API. It polled job status and displayed each deployment's real-time state, including the next queued application.
  • Version awareness: the tool queried each brand and environment's version.txt to determine the deployed version. It skipped any app already at its target version, removing redundant redeploys and manual version entry.
flowchart LR
    CONF["Release plan<br/>(Confluence)"] -->|"extension pulls;<br/>parser ingests"| ORCH["Release<br/>orchestrator"]
    DEP["Dependency manifest<br/>(JSON per app)"] --> ORCH
    J["Jenkins API<br/>(session cookie via extension)"] -->|job statuses| ORCH
    V["version.txt per<br/>brand & environment"] -->|deployed versions| ORCH
    ORCH --> VIS["Pipeline view: nodes & lanes,<br/>real-time state + queue"]

Outcome

The tooling changed the set of workflows that could be tested before release:

  • End-to-end and authentication flows became testable. Previously, most auth-dependent scenarios could not be meaningfully exercised before release. They could now run against production conditions from a local build.
  • Cross-application features could be tested locally. A developer could override multiple affected micro-frontends in one browser session.
  • Shared test branches required no release. A remote override could point to a shared branch build, letting others test it against production without changing a release.
  • Single-app testing no longer required local container setup. It required only an override entry in the extension.
  • Production-condition verification went from hours to seconds, with no effect on other developers or site users. The orchestrator also prevented release mistakes through dependency and version checks rather than a manual checklist.