Contact  |  Login  |

[language-switcher]   |

Break up your monolith: give teams the freedom to scale with micro frontends

Tech blog

Your frontend codebase is growing. It started out small, but now there are multiple teams working on different parts of it and deployments are taking a little longer every month. You weren’t able to release a feature, or worse, a bug fix, because tests for code owned by a different team started failing. 

Does any of this sound familiar? 

This is precisely the challenge the Forto tech team was faced with until we collaborated to find a way to fix the problem. The answer turned out to be micro frontends and this is how we did it.

 

The challenge: A growing codebase, slower deploys

At Forto, we have business function-oriented vertical teams. Users access these business functions (let’s call them products) through a single web application. A single team cutting a release of their product would mean a release of the entire application. 

 

An example of an application using the micro frontend technique – image of vertical teams can be found here

We didn’t mind releasing the entire application for every release when it was small. The pipeline was fast, and if anything broke, everyone on the team knew enough about the entire application to be able to fix it. But as it grew, we split code ownership by domain and we no longer knew enough to fix code owned by different teams. If something broke, we wouldn’t be able to release anything until it was fixed by the team that owned it. 

These were still only occasional annoyances, so looking for a long-term fix didn’t feel urgent. But then we learned we had to embed our product in a standalone website–this turned out to be the perfect opportunity to fix our deployment challenges at the same time.

 

The solution: micro frontends were our salvation

We now had to keep the same application up-to-date in two places. We definitely didn’t want our deployments to involve more manual steps, so we wanted to be able to update both places with a single deployment. By integrating micro frontends at runtime, we achieved not only that but also decoupled our application from the rest of the codebase. This would let us deploy independently, without being blocked by other teams.

An example of an application using the micro frontend technique – image similar to this figure can be found here.

The method we used to split the micro frontend from the main application was a classic example of Conway’s Law in action. Our team would completely own the micro frontend, and the interface with the main application would be the same as the interface between teams. This ran the risk of reducing collaboration across teams, but we had confidence in the systems we have in place to prevent insular groups from forming, including regular meetings to collaborate on the shared platform, guidelines and tools, a public ADR process, and a design system for consistent UX (which increases user confidence in the application).

We wanted to understand how easily we could migrate from our monolithic React application to a micro frontend setup. To find out, we tried implementing three different approaches in one of our hack days (more about this in a future blog):

  • single-spa
  • an iframe approach, 
  • and a simple runtime script include. 

All three solutions did the job, but two of them had constraints that we wanted to avoid. Single-spa required import maps with SystemJS and a very different webpack configuration, and webpack 5’s Module Federation had not been released at the time. The iframe approach had some limitations in styling and linking back to the main application. So we went with the simple runtime script include, which required the least amount of new technology and concepts, and still did exactly what we wanted without degrading the UX too much. 

At Forto, we believe in the principle that innovation begins with engineers, especially when there are no barriers in the way to even drastic changes such as introducing micro frontends as a technique. This is especially true if the change is reversible, or it doesn’t significantly affect other teams. We could reverse our decision by just copying our code back into the monolith, and even though we’d have to make a few changes, this would not have any effect on the way the other teams work. So we just needed consensus within the team, and we were good to go! The migration took about two weeks and we were fully productive again after that. 

 

The benefits: more frequent deployments

The micro frontend approach not only fulfilled its primary purpose, which was to keep our application up to date on two different pages from a single deploy, but there were other benefits as well. We monitored the micro frontend’s deployment pipeline to measure any differences from the old approach. Below are graphs for the deployment pipeline’s duration and success rate, comparing the monolithic frontend and the micro frontend over three months.

Monolithic frontend:

Micro frontend:

Over the last three months, 95% of the micro frontend’s CI pipeline runs took less than 10 minutes, which is a 3 minute improvement over the previous pipeline. The pipeline’s success rate went from 85% with the old application to 97%. The average number of releases per day increased from just 1 to almost 5, but that was also in part because of a switch to a release toggle-based development approach, which is a story for another day.

An explicit interface between the micro frontend and the main application gave us clarity. Exactly which changes would affect the interface were clear. Only those changes need testing of both the main application and the micro frontend. This meant that we could test a vast majority of releases only on the standalone micro frontend. This saved time, since we knew that most releases wouldn’t affect anything outside the micro frontend.

Upgrading dependencies became easier since our team owned the entire micro frontend, and could change just the code we had full knowledge of. But this is where the first downside of the micro frontend approach became clear. 

 

The downsides: longer loading times

The main application was already code split, so there was no difference in initial load time. But we were now shipping a duplicate of some dependencies to the frontend. This meant longer load times for users of the micro frontend’s features. By declaring shared libraries using webpack’s module federation feature, we should be able to load dependencies that have the same version just once, but we’d still have to load dependencies having different versions again.

Sharing state across micro frontend boundaries is an anti-pattern. That would bring back the coupling that we worked so hard to sever. If there’s any data that’s needed across boundaries, the micro frontend has to fetch it again. We use events to synchronize any changes between the app and the micro frontend (e.g. changing the language of the main application should also change it in the micro frontend). We use an event bus rather than custom DOM events, because we wanted all integration points to be explicit. 

 

Conclusion

As you can see from the stats, we’re already seeing significant improvements to development workflows, even though we’re still in the early days of using micro frontends at Forto. We will continue to monitor the effects of the micro frontend approach on the quality of the product and developer experience. Module federation and import maps look like the most significant improvements to the experience of building and serving micro frontends and we look forward to seeing how they’ll help us scale.

We’d love to hear about your experiences with the micro frontend approach, or scaling frontend deploys with multiple teams. Send us an e-mail at [email protected].