We’ve now been using micro-frontends for two years at Beamery. In May 2018 we wrote a post announcing to the world that we’d solved our legacy application problem.
At the time, our core application was a monolith built on Angular 1.6. Four years of rapid growth meant that there hadn’t been a lot of time to pay off technical debt, and the application was becoming increasingly harder to maintain. We didn’t have time for a major refactor, so we began exploring other approaches.
We set out to find a way to upgrade or replace parts of our application over time, whilst also migrating to React. This is where micro-frontends entered the scene.
Micro-frontends offered us the best of both worlds. We built a custom orchestrator (using single-spa) and a new navigation application, which allowed us to switch between our legacy application and micro-frontends as routes changed. With that in place, we were able to migrate parts of our legacy application to smaller greenfield applications, and deliver those seamlessly (and independently) to our customers.
Despite that success, we found our move to micro-frontends also created some new challenges for us. If you’re thinking about taking this path, we hope that you can learn from our experience.
The speed at which we could create new applications meant that we delivered most new product features as new micro-frontend applications. Every new micro-frontend was delivered with React, but that was where the similarities ended.
Micro-frontends allowed teams and individuals to experiment. Our engineers loved the freedom to try new technologies regularly. However, as a rapidly growing startup, we didn’t manage this well.
From code-style and application architecture, through to user experience, our applications were diverging. We built a component library in an attempt to introduce standardisation and code sharing, but without clear ownership, that project encountered problems of its own.
We didn’t invest in dependency management upfront. As a result, each micro-frontend had its own dependency tree — with different applications having the same dependencies, at different versions.
This not only affected the amount of code that we were serving to clients, but in some cases, meant that applications broke as some libraries simply weren’t built to run alongside other instances of themselves, or alongside different versions.
Are micro-frontends bad?
Maybe you’re new to micro-frontends and you’re thinking “this seems like a bad idea”.
Dan Abramov, a React Engineer, kicked off a small storm in the micro-frontend community in 2019 with this tweet.
He argued that a solid component model can solve many of the problems micro-frontends seek to address. The new Facebook.com is a strong example of what he meant, and I’d strongly encourage you to watch the below video on how they built it.
A single application certainly solves issues around dependencies. With the right architecture, it can provide great developer and user experiences. It also ensures that teams collaborate and share code, and helps to keep code up-to-date.
With all of that being said, it’s hard to simply dismiss the benefits of micro-frontends.
At Beamery, micro-frontends have allowed us let us quickly roll out new features using new technologies, and they’ve allowed our teams to try new approaches without impacting other teams or features. We’ve also benefited from being able to roll out updates and fixes to different features (micro-frontends) at different times.
Did we just do micro-frontends wrong?
If you’re using micro-frontends today, you might have had a different experience to us. A lot of people swear by micro-frontends. Luca Mezzalira as an example, wrote this post which counters some of the points that Dan and others have made.
The short answer to the above question is: yes, we did. We could have managed dependencies better, we could have implemented processes that encouraged good architecture and code sharing, and we definitely didn’t need to build so many micro-frontends.
In fact, we started solving some of those problems last year. We now have common code-style and linting configurations, we share Webpack configurations, and we’ve set up tribes to help teams share and collaborate.
So what’s next?
We will continue working to improve our micro-frontends, but we’re actively discussing if they are the future of our front-end. Right now we see two possible paths: consolidation of our micro-frontends, or moving to a single React application.
We’re also creating a front-end platform team, with the sole purpose of delivering these changes and managing shared code. This is similar to approaches being used at Netflix and Airbnb.
Regardless of what we decide, we’ve learnt a lot about the importance of process, teamwork, and planning up-front — and we’ll be able to apply all of that knowledge to our future work.
A parting thought
In software engineering, technology decisions are amongst the hardest. The decisions we make affect costs, maintainability, performance, and even our ability to hire future engineers.
Micro-frontends help to mitigate those risks — but without careful design, they can present a host of new problems.