1. A Dream, Pager Duty, and Microfrontends
It's a nice, sunny day. You're gliding across the fjords. Water consumes you. You take a look at your phone and see a pager duty notification. You understand the problem, but it's caused by someone not even a frontend engineer. You fix it, but encounter issues. Eventually, you merge your pull requests. Microfrontends is a potential solution to this problem.
It's a nice, sunny day. You're gliding across the fjords. The nice sun rays fall on the lush forests and those fjords. You are dreaming to have this vacation for a long time. You are truly happy until something gets wrong.
Water consumes you. You lose control. You don't understand what's happening until you wake up. It's been a dream. It's been a bad dream and something has clearly woken you up. You take a look at your phone and you see that it is a pager duty notification. You have a duty to do.
Annoyed, you wake up. You stand up and lazily go to your computer to see what has happened. After some minutes of looking into the issue, you understand what the problem is. Nevertheless, you are extremely annoyed that it's caused not even by your team, but somebody who is not even a frontend engineer.
You hit the table because you're angry. You forget you have a kid who is woken up by your anger and now you have to explain your son why your father, Keith's father, has to fix a mobile phone validation library at night. You do the fix. You open the pull request, which takes 40 minutes to build. You make a coffee while you wait, and in the end instead of a happy green checkmark, you get a red cross.
Wakey test again. You click the restart button. You finish your cold tea to see your green checkmark. You merge your pull requests. You can go sleep for a couple of hours until you're going to wake up and do more job.
It's an example of a combination of a technological problem and an organizational challenge. It shouldn't have happened. Nevertheless, it's there. Microfrontends is one of the potential solutions to address this problem.
2. Microfrontends and Communication
Today we are going to talk about microfrontends. We will share experiences from different companies and dispel stereotypes surrounding this topic. When discussing ideas in a group, the conversation often expands and deviates from the original plan. Many people can relate to this, as it can be challenging to effectively communicate and make decisions in larger groups. This concept applies not only to software development but also to other areas, such as politics.
Thank you all for coming here. My name is Alex, and today we are going to talk about microfrontends. I was fortunate or fortunate enough to work in three companies which did microfrontends. Those were fairly different companies, and they wanted to share their experience. How did they do it? Also, dispel some stereotypes which surrounds the topic of microfrontends in general.
Let's talk about your company. You're working and you get an idea. You want to do something very straightforward, very simple, very tangible, and it's going to have very predictable and clear output. Seems easy, right? Let's do X and we're going to get Y. Very straightforward. But no, there is a guy with opinions, and now you have to convince him how you're going to do this. And at least his point makes sense, like, understand, okay, I see where you're coming from. Let's have a chat. Unless discussion expands. Expands in dimensions you didn't envision at first. And the further it goes, the more it deviates from the place it started until eventually it's gone.
Please raise your hands who can relate to the story. Many people. And people who are sitting around you, people who are part of those calls, they don't really want to be there. They also have life to live and they have kids who don't want to know about phone libraries. There is a lot of research about this. It's not a bleeding-edge subject. There was Fred Brooks. He was an engineer and engineering manager, like, many years ago, before many of us were even born. And he wrote a book. The book is called the Mythical Man Mouth, where he describes the concept of people interacting on scale. And the bigger the group is, the harder it is for people to effectively communicate and make decisions. There are similar concepts and I would say that it's credible enough to make our assumptions on this. And it also applies to different spheres of our life. If you will take a look at politics, for example, countries which are like deeply decentralized, they tend to be more effective in managing themselves.
3. Challenges with Autonomy and Boundaries
You might ask me, okay, we had five minutes discussions. You convinced us that we should divide it up. Why are we still here? We divided the teams and we gave them full autonomy. So we have different features and they have been worked by people on those features.
You might ask me, okay, we had five minutes discussions. You convinced us that we should divide it up. Why are we still here? We divided the teams and we gave them full autonomy. So we have different features and they have been worked by people on those features. And you have a small team with two people when somebody wants to use something and the other person is like, yeah, come on, we live only once. Why shouldn't we do this? It's fun. It's going to be cool to put it on our CV. Let's do it. So you're happy and you're having a day off your life. Until you don't. Until you understand that probably you crossed some boundary, which you shouldn't have crossed and you caused some problems which you don't entirely understand yet.
4. Testing Efficiency and Microfrontends
Companies need to rely on automatic tests to deploy their software frequently. However, running these tests can be time-consuming and unstable, causing frustration for engineers. Switching to microfrontends can significantly improve testing efficiency, reducing the time from 45ish minutes to 5ish minutes. This improvement allows developers to build faster and avoid global failures. Additionally, microfrontends enable companies to use new technologies and improve their pipelines, reducing costs and single point failures.
Companies and questions which we are going to be talking about are a local scale up from the Netherlands and still very big, I mean, not very big, but somewhat large software as a service companies. You can get a kind of rough idea of what the size of those companies is and the larger companies they have real CICD and they deploy a lot.
And if you want to deploy like 15 or like, for example, 77 times a day, you cannot test your stuff manually. You have to rely on automatic tests, testing it for you. And the tests take time and the bigger your app is and the more complex the business logic of your app is, the more time it takes to run your tests. And this is not a fictional example of what can it be. And that's not the worst example. And that's a result of continuous multi-year efforts to shrink this number. So it's like many beefy machines spending a lot of electricity somewhere in America running your tests on the pipeline just to fail. So another problem of end-to-end tests is that it's very difficult to make them stable, especially if you run a lot of them in parallel, you actually also create some kind of concurrency and whatever you use as a backend for your end-to-end test needs to be reliable as well. So yeah, imagine that you have to redo this operation often and often, again and again, and it makes your engineers somewhat unhappy.
This is an example of how massive of an improvement you can get from switching to microfrontends. Since every microfrontend can have a test that is dedicated to this specific microfrontend, you can have a good guess of what it takes to test the specific microfrontend. These are the real numbers for us. We went from 45ish minutes on average to 5ish minutes on average, which was a really nice improvement of life. Usually it was building quicker than the developer was writing in the description for the tickets, so we were incredibly happy. Eventually, we had to extend those with a couple of platform-wide end-to-end tests just to make sure that we didn't screw up globally. But besides that, no massive changes were made. If it's not clear what is.
On top of that, we can see that there were additional improvements. In the case of FreshBooks and 3Way 42, it enabled us to use new technology. 3Way was stuck with the first version of Angular. FreshBooks was using Ember. And I'm not sure if anybody still uses Ember here. It's actually a functional framework which is being maintained. So it's not exactly deprecated. Nevertheless, it's difficult to find people who want to do Ember. It's difficult to find modern technologies which support Ember and whatnot. And in the case of FreshBooks and Personio, it enabled us to improve our pipelines, run them faster, reduce the cost of pipelines because we run them faster, and also reduce the amount of single point failures so people wake up less. And of course, people autonomy.
5. Misconceptions and Split Approaches
There are misconceptions about micro-frontends, particularly regarding the horizontal split approach where multiple front-end frameworks are used on the same page. Another approach is the vertical split, where one micro-frontend is displayed at a time, divided by domain.
You can make decisions and be somewhat happy about them, to some extent. There are a couple of misconceptions when it comes to micro-frontends. Often people think about this image from Google images request for micro-frontends where you have a tractor and multiple micro-frontends on the same page. I can't recall how many times I've been sent this image. And so you have like menu in Angular, something else in React, and the rest of the page in Vue.js. Whatever you see there is called horizontal split. It's the approach when you have multiple front-end frameworks on the same page. But also, there is another approach which is called vertical split, where you have one micro-frontend up on the screen at a time where you divide your apps by domain.
6. Challenges with Split Architecture
Horizontal split is functional and helpful, but not suitable for companies building microfrontends on the UI. FreshBooks and Persona use vertical split microfrontend architecture. Challenges include defining migration strategy, dividing domains, and managing dependencies and pipelines.
Since while horizontal split is functional and helpful, I didn't work in the companies which could benefit from it to full extent. And normally, it's e-commerce companies because they can render their micro-fountains on the back-ends. They can ship the resulting bundle as HTML. Since we are building our micro-fountains on the UI, we cannot afford ourselves to have 30 megabytes bundle size. It was important for us to be more reasonable about it.
In both FreshBooks and Persona, we opted to use vertical split micro-fountain architecture. Now, let's talk about challenges. First challenge is defining migration strategy. If you have a large product, you cannot suddenly decide that you have a new thing. You have to restart the previous thing and get three years of development efforts out of somewhere and build the thing. If you are reasonable about that, there is a pattern which is called Strongest Pattern, which basically suggests you to wrap the old thing into Facade framework and start growing the new thing next to it until you finally eliminate the old thing. Be realistic, invest in tooling because chances are that you're going to stuck in step 2 forever, so you need to be comfortable to be in this situation.
Another challenge is dividing domains. Domain-driven design isn't extremely simple topics. There are plenty of books about it. It's a challenge on the back-ends, it's also a challenge on the front-ends, and those challenges aren't necessarily answering the same question. It's okay if your front-end domains are going to be different from your back-end domains. If you opt into vertical split architecture, you're going to have pages which have multiple back-end domains displaying data. Nevertheless, you want it to keep as one single micro-fountain. So it's okay to have differences in domain boundaries. Very small domains might cause you building those multiple micro-fountains together just to test them, because one test is going to go through multiple micro-fountains to check the business flow, which is also not perfect, and large quantity of small domains might lead you to the same problem which we started with, with somebody fixing code which nobody exactly owns.
Third challenge is managing dependencies and pipelines. I hate it so much. It's really difficult, and we did it wrong, entirely wrong. So the intuitive approach to building things is like, okay, I have a shelf for this, I have a shelf for this, and we're going to separate things. Separate npm package for our components, design system, separate package for helpers, until you start putting it together and debugging, and until you want to debug your component inside the app, and then you have the process of, okay, I need to update the component. So you go to the component repository, you build, you change the components, you build the components, you deploy it to artifactory or npm, whatever you use. Then you update the package version in your application, and then you reinstall dependencies, and then you can see how did it work. And if you want to debug some simple CSS, it becomes quite painful. There fortunately are solutions, neither of which we found helpful enough for us and convenient enough for us to use.
7. Monorepos and Their Benefits
After starting with separate repos, we used npm link and sub-repositories. However, we eventually ended up with a monorepo in both companies. Monorepos have proven to be highly recommended, and Google's Software Engineering book supports this idea.
So after starting with separate repos, we decided to use npm link, which we didn't like. And we started using sub-repositories, which was already better. It helped us to avoid versioning and make sure that we can do bigger changes, and we can reduce the frustration around different microphones having different versions of the same package. And yeah, finally we just ended up with monorepo in both of the companies. Both companies had very different path to it, but now we use monorepo in both. I cannot recommend you monorepos enough, and if you're interested in them, there is a nice book which is called Software Engineering in Google, and they claim that the whole Google thing, including YouTube and whatnot, is monorepo.
8. Managing Data and Side Effects
The best way to manage data is not manage it at all, but if you get to manage data, you can have different approaches. First, you can initiate some data when you initialize the framework itself. If you want to exchange data, especially if you're using horizontal split micro-frontends, then you can use PubSub. Side effects are easy to overlook but straightforward. You can use iframe or web components to isolate inner frontends from outer frontends.
Managing data. The best way to manage data is not manage it at all, but if you get to manage data, you can have different approaches. First, you can initiate some data when you initialize the framework itself. You can render your microphone with some starting data. For example, it can be your authentication token, it can be your translation strings, it can be your object's configuration settings, like what color scheme they are using, what mod. You can pass it once when you instantiate the app, and then you don't care if this data changed.
If you want to exchange data, especially if you're using horizontal split micro-frontends, where you have multiple frontends on the same page and they need to impact each other, then you can use PubSub. Any implementation is going to do. You can use even RxStreams, whatever you like. Just have something to subscribe to and listen from. You can also put some state machine in there. You can wrap Redux into PubSub and publish changes into Redux if you want. All those things do. See what works for you.
Final Thoughts and Q&A
Before we wrap up, a couple of general takeaways. Make sure to build a good example application. Let's be honest with ourselves. People copy-paste code if they can. And if you have a good example of what a good micro-frontend could look like. It's better if people copy-paste your good example than if they copy-paste something that is not exactly good.
Invest in education. If you spend a week teaching people now, you'll save many weeks, maybe a month, in refactoring their code later. Automate the boilerplate. Especially if you don't have the full agreement in your company to move with micro-frontends, it might be some proof of concept. And depending on how other teams perceive the micro-frontends you build. If they like the experience of working with micro-frontends, if the new approach is better than the old approach, they might be having different opinions on if they want to convince their manager to opt into this or not. And if you have a good tooling, a good boilerplating, and their developer experience is good enough, you have better odds of this idea sticking to it.
Lifting restrictions is easy, but making new restrictions is hard. So if you start in the wild west when everything is possible, you will have a much harder time having restrictions later and standardizing your code base. So if you can afford to start in more restricted modes, you'll have a better time. But I also want to acknowledge that it's not always possible. And please take a look at your dependencies. There are so many rabbit holes to jump into. You can have a hundred React applications with a hundred different React versions. If you get into this situation, you cannot share the React bundle between those micro-frontends. You'll have basically a hundred Reacts being loaded for your website. Make sure that you can easily update packages. If there is critical vulnerability and you have outdated versions, different versions of the same package in different repositories, it's going to be painful. So it's technical depth which you need to keep in mind and you need to make sure that you made it easy for yourself in the future to maintain that.
On this note, I want to say thank you. I really appreciate your coming. Thank you and we'll gladly answer your questions.
Building Micro-frontends Without a Mono-repo
First question: How viable is this approach without using a mono-repo? It depends on the scale of your app. If it's a small local community website, you can have as many repositories as you want. However, Persona faced challenges with discoverability and technical depth when using a multi-repo approach. Having one repository with multiple micro-frontends improves discoverability and makes it easier to get eyes on PRs. For small greenfield applications, micro-frontends may not be necessary if the complexity outweighs the benefits.
Firstly, what a deep story to start with. I was getting stressed and I woke up. It was a nightmare. I was somewhat relieved. We'll jump straight in. We've got about seven minutes to answer some questions. Just a reminder, there's a speaker Q&A section near the entrance or those remotely in the timeline. The ones we don't get round to, if you still want to ask them, pop that after this.
Okay, cool. First question is how do you think this approach is viable without using a mono-repo? Absolutely. What would be the implications of not using a mono-repo? I know you said they're very nice. They're nice to work with. In my day job, I work with a mono-repo. How much more difficult is it without? It really depends on how big potential of scale you have in your app because some apps are never supposed to be scaled. If you are building the website for your local community, it's never going to have millions of customers and it's never going to be big. In this use case, you can have as many repositories as you want. On the other hand, Persona started as a multi-repo and eventually found that we have a problem of not knowing how many micro-frontends exactly we have. That we need to put continuous efforts in just discovering the micro-frontends being built. Technical depth became more difficult because you need to update something that is unknown. You cannot update something that you don't know exists. And that makes it quite tricky in general. Definitely possible. You can definitely live with that. The last bit is discoverability. If you want people to take a look into your PRs, it's much more difficult to get eyes across 100 repositories over one repository with 100 micro-frontends.
I think there's quite a few questions in here which stem from this, and it's actually about how does a team work and communicate while building micro-frontends? I think there's a few. We may come to others like it in a moment. This question got a ton of upvotes, thumbs up, plus ones. What point does the complexity outweigh the benefits of micro-frontends? If you have a small new greenfield application, I would say you don't need micro-frontends. I would say it's not points when it starts outweighing.
Benefits and Avoiding Duplication
Micro-frontends become useful for big apps when the pain of not having them is felt. Avoiding duplication across micro-frontends can be challenging, but trust in people is key. Implementing a registry of externally built components helps manage duplication. Starting with an abstracted component library is recommended, as demonstrated by FreshBooks.
I think that micro-frontends outweigh the benefits when you start. And eventually, they start having more sense once you move forward. So I would say for big apps, it makes sense. Not really for small ones. Yeah.
I think in my mind, it's almost the inverse question. At what point do micro-frontends become something that is beneficial to your project? Exactly. That's the question. At what point do you think they become useful? At the point you feel pain of not having them. I mean, yes. Yes. Okay.
You know what? Yes. We have so many questions. Oh my God. They're coming in so fast. I've not had any talk with so many questions coming in. This is wonderful. How do you avoid duplication across micro-frontends? Remember I said trust people. So to some extent, it's this. Clearly it's not perfect, and it can be better. We are looking into different means of finding duplicated code, but it's not very straightforward because you can implement the same thing differently. Currently, we have a registry of components built externally, and we are working on socializing it across the organization. So if you build some very domain-specific component, which does not belong to the design system, at least there is a centralized place where you can go and look that this component exists. But yes, the real problem, we have helpers which are duplicated across multiple micro-frontends, and that's just the price you pay, I would say.
This was one of the questions that came in actually while you were answering this question, but just to be explicit, is it therefore recommended to start with this kind of abstracted component library that can then be used by all the micro-frontends? Is that a common approach? It's an approach we've chosen in FreshBooks because we had a head start. So basically we built an example of how it's going to work. I wouldn't say proof of concept, I would say full-fledged MVP. We had a couple of teams which adopted the approach later, and it was pretty good. But in Prestonio, for example, we had different circumstances.
Demands, Tools, and Shared State Management
We had demands for the new design system and had to move in parallel with other teams. Micro-frontend tools and setups that worked for us include Module Federation and NX for monorepo management. In FreshBooks, we used VIT for building micro-frontends. Shared state management was handled easily with a global object, without much overhead. The problem of not being able to share state is more exaggerated than it actually is.
We had demands for the new design system, and we had to move in parallel with other teams who are going to consume that. It really depends on what state your organisation is in, and if you can afford yourself to spend two, three, four months of head start just preparing for... Yeah, it's one of those go-slow-to-go-quick things, and yet not every team has the luxury or the privilege of being able to take that initial hit.
This next question had so many people who were interested. So what micro-frontend tools or setups have worked for you? And which ones, I suppose, did not? It's worth talking about as well. Yeah, I would say that pretty much everything that we considered worked good enough. I can tell what we settled with. We used Module Federation, and it's pretty good. We used NX for monorepo management, which was also good. In FreshBooks, we have tried Module Federation. It didn't justify downsides, which we had in the moment. So we were just building micro-frontends with VIT. We were dividing bundle, like, shared bundle size of files separately, like React and other large chunks, which we used across micro-frontends so it can be shared. And we had 500 lines bash script, which was doing what Module Federation is supposed to do. I just head to the room and go, whoa, that was a large bash script.
I think we have time really quickly for this one, which I was curious about in your talk as well, because you mentioned using PubSub or other techniques to keep micro-frontends in sync. But how do you handle shared state management? We had this in Rayway 42 because we had Angular application communicating with React App living inside of it. And it was very, very easy, actually. We didn't experience much of the overhead around it. We had a global object, which was communicating between Angular services and our React states. It wasn't Redux, so it was kind of full and dataful. And yeah, I would say the problem is more exaggerated than it actually is.
That's a really good perspective, too, because I would assume it's absolutely huge, not being able to share state. But as you've done it a few times at quite large scale, knowing that actually it's not been that much of an issue is a really useful insight. We are out of time. Once again, there's a speaker Q&A section, a speaker Q&A area, which is near the reception.