ADHD Dev Journal: Module Federation Thoughts

8:52 AM

Using Task management software consistently for more than a couple weeks is hard.

9:02 AM

9:55 AM

Webpack Module Federation creates a really interesting opportunity to evaluate code ownership and maintenance in a web application. If we break up any application into small pieces then there has to be tools and processes in place that reliably reassemble those pieces together back into the running application that the user interacts with. The reasons for breaking the code apart into different modules in the first place is important. There’s several reasons:

  1. Understanding. It’s easier to understand and comprehend the scope of a function when it’s been isolated and designed with a specific interface.

  2. Engineer ownership. The actual people that do the work may not want to be slowed down by understanding and running tests for a larger code-base, they may want to write, test and deploy a piece of code that is smaller in scope so they can move faster.

  3. Isolation from side-effects. Depending on the technology and interface definition, the two different code-bases may be less impacted by bugs in each other when they are isolated.

Most of the other reasons I can think of depend on the method of isolation/separation. For instance, in module federation, JS components are loaded from an http endpoint and injected into the host application in the browser at runtime. This means the developer of the host application isn’t able to see and predict the effects of every change that happens in a federated module they are loading. Whereas if the module was loaded a build time, using webpack on the CI/CD server, then the JS code that is loaded will always be knowable and testable by developers before it is run by the user’s browser.

The big drawback of build-time integration is that fixes and updates to the module must be pulled, tested, and approved by a developer before they are deployed to any users. So, runtime integration through webpack module federation gives us back some of the freedom we had on the web before we all started using bundlers and NPM for our dependencies. Back in the day we could choose which dependencies were loaded as always the latest version, or which ones were pinned to a specific version or “fingerprint”.

<!-- This component will be cached by the browser, but can be updated by whoever can push changes to the CDN, no change to this html file is needed. -->
<script type="text/javascript" src=""></script>
<!-- This component has a fingerprint which means if a new version is pushed, then this HTML file must be updated too -->
<script type="text/javascript" src=""></script>

That’s a simplified example of what we’re talking about. It wasn’t an issue when the html file and JS file were maintained and owned by the same team. But, as organizations grow and ownership is divided further and further, the repo and build process responsible for the html file may not be the same as the JS file. This can cause terrible slowdowns when a component developer can’t see their changes in production until the upstream team adopts them and deploys them.

The adoption of webpack and NPM set the default for modern applications to always have every dependency pinned and fingerprinted at build time. This type of bundling and compilation step made the end result more predictable for web developers by simplifying the mental model of where your code comes from.

Changing that model to include runtime dependencies through webpack module federation, modern ES Modules in the browser, or a script tag with a src that wasn’t compiled in webpack will complicate the maintenance and debugging story for an application, while offering certain advantages in letting different developers update and maintain different parts of an application independently.