Scaling Up with Remix and Micro Frontends

Bookmark

Do you have a large product built by many teams? Are you struggling to release often? Did your frontend turn into a massive unmaintainable monolith? If, like me, you’ve answered yes to any of those questions, this talk is for you! I’ll show you exactly how you can build a micro frontend architecture with Remix to solve those challenges.

by



Transcription


Hey, hi everybody. And welcome to this talk, Scaling Up with Remix and Microphone Lens, in which we will talk about how to use microphone lens in Remix. A little bit about me. My name is Adrien Baron. I'm a senior software engineer at Tractable, which is a cool company that does AI and machine learning for insurance. So we take cool tech in quite a boring market and we try to make it more modern. Before that, I was a principal software engineer at Kizoo. And at that company, I kind of led the microphone and effort. So I'm going to talk mostly about my work there. So first, you're probably wondering, what's a Kizoo? I probably thought about that. That's not the kind of Kizoo we're going to talk about. Going to talk about this Kizoo. Kizoo is the better way to buy a used car. It's basically for people in the US, it's a clone of Carvana, but in the UK. And what it does is they basically buy used cars and then sell them online. They deliver it to your door and you get seven days to try it. If you don't like it, you just return the car. No problem. No question asked. So it's just your traditional e-commerce, like for people that build site like us, you look at this like, oh, it's an e-commerce website. Yes, it's an e-commerce website. So except every item is like 10,000 pounds or something, right? Cool. So what does front end at Kizoo looks like? From the beginning, Kizoo have been following a domain driven design approach, meaning each vertical slice of the problem is owned by a specific team and they own the whole stack for that specific problem. So they own the front end, the back end, the deployment, everything. So what's some examples of sites you might ask? For example, you might have search and browse, which is that you search for a vehicle, see the detail of a vehicle, all this kind of things. You would have my account that lets you see your account information and past orders or consumer finance, which is the loan application online. So when you buy the vehicle, you can like apply for a loan. And there's lots of forms that ask you lots of really fun things like where have you lived in the past three years? And that's a separate app. That's a slice of the problem of like selling your car online. So from an implementation perspective, you basically have the domain Kizoo Code UK and different separate application, which are deployed independently and all own a set of pages on the domain. So if you look at that from a remix perspective, that would be basically multiple remix apps deployed separately, like independently by teams, but all pointing on the same, like all being like rooted on same domain, if that makes sense. At Kizoo, they didn't use remix, they used Next because it was built before, but potentially could migrate to remix. So that approach works great. And it's called like a vertical slicing in the microfinance architecture, like this kind of each team on a set of pages. But we saw that sometimes it wasn't granular enough. So let's imagine a scenario we actually had at Kizoo, which is a consumer finance team, which provides this loan application thing is like, you know what, to boost conversion, because we want to sell more loans, we need to display a finance calculator on the detail of a given vehicle, the detail page of the vehicle. Or for example, when you like placed an order and you want to rescindle your delivery, in my account, you would want to see some kind of thing that lets you select an available slots. And there's a team that owns like all the logistics of moving cars. And there's a one that knows about available slots and what the UI and UX should look like for people to be able to book slots. So they want to deploy their thing on my account. Finally, that one is something that almost everybody has a header and a footer, which need to be consistent across the whole website. So if you're in a company that deploys a few apps in production, and you have a consistent branding, like if you do this kind of vertical slicing already, then you probably have that problem already. Like you probably have a header and footer that everybody needs to like depend on. And that problem of like providing a slice of the page like this, an horizontal slice of the page is called horizontal slicing in the microfinance world. Yeah. So first thing that comes to mind, and as a front-end engineer, you're like, oh yeah, that can be an NPM package, right? Like I'm just going to publish a package on NPM, like in our internal private repository or something that contain a React component, that's a header and a footer, and everybody will just install that in their app and just render it done, right? Yes, but we've just introduced a build time dependency between two teams by doing this. Meaning any change to the header by the team that owned the header needs updating and redeploying of every application that depends on the header. So that means now our team that manages that content basically needs to, each time they do a change, need to go see every single team and say, please, can you update and deploy your application again? Which is really painful and kind of like hinders like team ability to deploy fast. So we kind of want that pattern of NPM package work quite well, but we would want independent deploys. Introducing tiny front-end. So to solve that exact problem at Kazoo, I built a library that's called tiny front-end and basically aimed to solve that specific kind of microfinance problem. What is a tiny front-end? It's an NPM package that fetches its latest deployed implementation at runtime. So imagine NPM package that you install in your project, but doesn't contain the actual source of the React component, but fetches the latest one at runtime. It's one opinionated implementation of a micro front-end architecture and it's aimed at solving the horizontal slicing problem. It's not the one true way to micro front-end. Like micro front-end is just an architecture. There's a lot of different solutions out there that are different trade-offs. So this is just one of them. And it's not aimed at solving the vertical slicing problem. So it's not, for example, it doesn't care about routing or anything like this. It's just here to provide a component at runtime. Okay. A few guiding principle that tiny front-end follows. Use the framework as a runtime glue. So the idea is like, for example, you might have heard of web components or a thing like this. And there's quite a few solution that lets you use theme cross framework, like let an Angular app consume a React component or a thing like this. This is not what tiny front-end wants to do. It wants to just say, cool, you're going to deploy a React component and your host is going to be a React app. And just, you know, it's like a library. You expect your host to have React. Could be Vue, could be something else. But it's an assumption we're making. Why do you want to do that? Because having multiple frameworks running in the same page is not great for performance. So generally having a consistent framework for your whole company makes for a better user experience. So second principle. Don't need anything special to consume a tiny front-end. Yet again, it's some microcontent implementation will require you to have a big host application that knows a lot of things. Basically it's more like a framework than a library. And tiny front-end is more like a library. So the host should be just a vanilla remix app or a vanilla whichever app that uses React, for example. We don't want the host to have anything complicated. For them, they should be really easy. Should be basically the same as consuming any regular NPM package. Third one, be type safe. We like type safety. TypeScript is our friend. So if we can provide the types of a component to the team that consume it so they can make sure that their code is going to run against it at runtime, that would be great. Ensure shared dependencies are compatible at build time. Because we're providing that component to the other team, our component might be using a version of React. The host application might have a different version of React. We would want some way of checking that those dependencies are compatible at build time. Then automatic opt-in for non-breaking changes. This is the idea of like I have a header. It doesn't need any information. It's just a React component. And the type is just like cool. It's a header. Render it. It's going to show the header. Whenever someone changes the header, the color, whatever, the contract of that component doesn't change. So team that use the header should not have to do anything. If you refresh the page, you should see the new header when you push a new one. Manual opt-in for breaking change mean whenever a team actually that own the header actually need to do something new to do their job. For example, we just translated like made our website internationalized. And other component need to know which language it need to display. Otherwise, it can't do the job anymore. Well, in that case, we'll need to communicate to the team that choose that component to be like actually, no, please, you need to provide language as part of the contract to that component for me to do my job. And in that case, you do basically a new package version and ask teams to update manually and change the code base because they actually need to change something in the code base for the component to be able to work. What does the architecture look like? So we have this amazing diagram that's like everybody's like, oh, my God, what is happening here? There's so many pink boxes. We'll go through them one by one. The first one, the host. So the thing that's going to display that microphone. So a remix app in this case. The second one is a tiny front end. It's a piece of front end, a header, a footer, some kind of component that's going to be consumed by the host. So displayed inside the host application and it's composed of two folders, an app folder that contain the actual source of your React component and a contract, which is an NPM package that you're going to publish for people that want to consume that component at runtime to be able to do so. And finally, because we're talking about like deploying those bundles from this tiny front end because we need to be able to change them because they're not in the NPM package, they can be changed somewhere. They need to be somewhere to push those bundles to. And in this case, it's a small API built on Cloudflare and deployed on Cloudflare as a Cloudflare worker. Cool. So our example host, in this case, Remix, it's going to install the NPM package, which provides a method, which is asynchronous. When you call that method, it returns to you a React component that you can use in your application. When you call that method, what does it do? Well, it uses a tiny client library to call an API to get the latest version of the bundle. So all the complexity of how do I get a React component at runtime from a bundle that's deployed somewhere and everything, all of that is abstracted away in the library. So when you build a new tiny front end, you don't need to reinvent the wheel all the time and copy paste a lot of code. You can just have, you can just, like basically everything is just abstracted away in this library that you use. So whenever the team that has the tiny front end, for example, the header team, have a new version of the header, they'll just like basically push to their repository. They'll have some kind of, for example, a GitHub action that's going to bundle the new component and then deploy that bundle to this Cloudflare API. And it will like basically update the state there to say, this is now the new bundle. Someone will then, like a user, for example, will come to our remix app and refresh the page because the bundle has changed. We'll get the new bundle and we'll just render the app with the new component. Nice. Okay. So now we're going to go to a small demo because it makes much more sense when you see it for yourself. Cool. So here we have a tiny front end app. That tiny front end app is this like box here in pink. Everything inside that is a component that's deployed independently and it's this code base. And everything outside is just a regular remix app. So we can see that this code base has two things, as we saw, the contract, which is NPM package that the remix app installs and the app, which is a source for that component. Cool. You can see that I can press and there's communication between the two. This is because this is just a regular React component at runtime. So the communication is just literally the remix app can pass callbacks and you do the communication the way you usually do communication in a React app. Right? So some microcontent framework have like buses and things like this. But here because we use the framework as a glue, all we have to do is pass callbacks, for example, if we want to. Another nice thing that we can see is if I'm disabling JavaScript and refreshing the page, you can see that I can still see that component. So tiny front end work on service side rendering. So the remix app actually load the component like the latest version of the component from the network at runtime on the server and use that component to render the page on the server. It then also loads it on the client and use it for rehydration. So if I reenable JavaScript and I reload, I think it's still working. And to prove I'm not lying about the client, here's a component being loaded on the client. We can see it's loaded from this Cloudflare URL because it's a Cloudflare API that serves this bundle. If I open it, you can see that it has like this text like hello and the links and I was deployed and everything. So you can see this is the actual source for that component. Yeah. Let's go now see let's now go on the remix side and see how we consume that component. So on the remix side, if we go to our server side rendered page that we just saw, here's the text and everything. We can see that we get this component and it's just a regular component. We can also see it as a type. So for example, it expects a name property. If you remove it, it's going to complain and say you need to provide a name. And how do we get it? Well, we get it either from client or server, depending on whether we're on the client or the server. And for example, on the server, how is it loaded? We have a function called load tiny frontend server, which is provided by that NPM package we installed on this remix app. And it returns a promise of basically something that contains our component. And we just store it in the global context. That means on the server, we call this function initially. And once we we awaited that function, we know that the component exists on the server and we can actually render on the client. We do the same thing, but we call the client one and we do that before we hide reaching the remix app. There's probably some optimization you could do around page and everything, but yeah. Cool. So now let's see it actually change. So if we go to the source of this app and we go to our component, we are able to change, for example, the text here, say, hello, remix Europe. And let's change also the color. So if we go here and we change this to be Rebecca purple, then we'll be able to commit this up. So I've committed this change and I'm pushing yet again. Now I'm not doing a change to remix application. I'm just changing that component only. So I just pushed on the repository that contained the source for this component here. Right. So the remix app doesn't get redeployed. Nothing changes there. It's still like the same, the same app. If we go to the actions, we can see that there's an action that started on GitHub action. It's currently running the CI, so it's installing the dependencies to build the latest bundle of this like component. It's deploying to and it is now deployed successfully. So now if we go here and we refresh, we see that it's not a new component. So press me remix Europe and the bottle is now purple. And we didn't have to touch our remix app. Pretty cool, right? Okay. So that's horizontal slicing. But what about vertical slicing? You ask? With remix, can we do something about that? So I don't know if you remembered, but we had this very independent remix application. And ideally what we would want, like one of the problems we have with this is when people cross boundaries between applications, you will actually have to download react again. Right. Like each app will ship its own react and remix and react router and all those dependencies. So ideally we kind of want people to be able to like teams to be able to deploy their own app independently, but at runtime is one federated like remix app that just works across boundaries. Basically, so we would want some kind of remix host running that could save pages from both from a remote A or remote B and like handle everything for us. So I build a very little, very basic proof of concept that shows that it's rather easy to achieve that in remix. You mostly need to change some stuff in the remix source. Although, like if you really feel adventurous, you can use my demo to actually like try to do it yourself. But if you wait, I've heard it's possible it might module federation, like webpack module federation, might come to remix and allow you to do that in a like more framework supported way. Okay. Demo time for that thing. Cool. So here is my remix federation playground app. And we have two apps. We have a host and a remote. The host is the one currently running. And you can see in its app, in its roots, it only has an index with a link to a slash remote page, which doesn't exist in this code base. We also have another code base called remix remote. And that one contains a remote page, which is there. What happens if I click on this link? Hey, I navigated from one app to the other one. Now, the really cool thing is this is actually a regular, like there's no page reload, nothing. It's client side. This is a regular remix navigation. So it behaves just as if the remote page is part, was part of the remix host app. Even better than this, we have things like, for example, live reload works. So if I change the remix remote, it actually reloads inside the host app. And we have stuff, everything you expect from remix, like action function works also. So, for example, I submit this form, it's actually going to call this action. And that function is actually going to run inside the host app. I'm not going to get in details on how that works, because I don't have the time. But if you search for, if you search in the discussion on the remix GitHub, you might find how this works. And that was all. Now we're going to switch to Q&A. Thank you, everybody, for listening to my talk. Make sure to follow me on Twitter at Baron Adry. And yeah, thanks, everybody. Cheers. First, we're going to go over the Slido results that we asked at the beginning of your talk. So we're going to look at the results for are you using micro front ends at your company? I'm going to switch over to that view and see what the results are. So no, 75% of people said no to that. That's crazy. And then 25% of people did say yes. So is that surprising to you? I guess it depends what company you work for, right? Like, I think not everybody needs to use micro front end. It's like, use micro front end if it actually solves, like, a pain point in your company. So, you know, it's totally fine not to use them. So I guess it depends on the audience and where they're from and where they work, I guess. I guess that is very true. I currently work for Netlify and we don't use micro front ends, but I worked for Grainger before and we did use them there. So it is very dependent on the company that you're working for and what their architecture is. So that makes total sense. So we have another question that we asked the audience in the beginning and we've been asking our speakers all day also. So would you like, we would like to know what is your favorite Remix feature? Okay, I'm going to be super original here and reply what almost everybody did, which is Formant Mutations. Yay. Wow, such originality. Yeah, for most of the reason I think people have said. It's really like, I think when you use Remix the first time, you know, you write the loader, you render a page, you see the data. Well, if you use competitor, I don't want to say the word, but like Next.js or like other similar frameworks, you're like, hey, there's nothing new here. But then you look into how do I change data? And then you're like, oh, wait, this is like much easier. I just like literally use a form. And then you turn off JavaScript and you're like, oh my God, it works even in that case. And it does so much things under the hood by itself, which is really, really nice. Like I think it's definitely my favorite feature. I think it's one of the big strength of the framework. I agree with that too. I think if, especially if you're working with forms day in and day out and you see that that needs to happen in your framework and then you don't have that available or it's hard to implement, then Remix just makes it so easy. So that's a great feature. We have our first audience question from Baymax. Doesn't Remix's architecture with file-based routing reduce the need for micro frontends? Well, I would say it kind of depends. So one of the pain points, so in the example I was giving you, which was Kazoo, we're talking like a code base where a lot of engineers are working on at the same time. Kazoo is probably, I don't want to say, on the public website is probably at least 10 teams of seven people, eight people. So that's a lot of people working on same code base. And the only way you would be able to do that with one single Remix application would be a monorepo, which come with its own set of challenges in terms of deployments and PR, queue and everything. So if you don't want to go through all of that and you want to keep independent small repo that you can deploy independently for slice of the application, then you can't choose just the file-based routing because it's just different black code base. So I think that's when micro frontend can be useful still with Remix. Yeah, that makes a lot of sense because if you are working across multiple teams, you have to have that monorepo set up, like you said, and you have to have a pipeline of getting the code out there. So you have to navigate that. And that's a lot of processes and stuff. So Chris asked, you mentioned towards the end of the talk that Remix might support some of this going forwards. Do you have any more specifics on that? So sadly, I'm not part of the Remix team, so I can't say for sure. But I think people in the Webpack Module Federation ecosystem, for example, Zach, I think I've been hinting that they would have been working with the Remix team to maybe add support for Module Federation in Remix, the same way there is a plugin that he made to support Next Module Federation, Next.js Module Federation. So if that comes out of the box, then you'll get basically this kind of ability of deploying independent Remix app, but they behave as just one single federated app inside of framework. But I don't think there's a timeline for it. OK. Yeah. I wish that we had you on before we did the fireside chat with Chance, because I could have asked Chance. But I think that they have their RFC and their pipeline that they're coming out with new features public now. So we were talking about that earlier. So maybe just keep an eye on that, Chris, if you're looking for things that are coming in the future. And you can always maybe file an issue or start a discussion on their repo about that too. Yeah. One small thing to add also is the tiny front-end stuff, though, doesn't require any framework support. So that works already in Remix and Next and other frameworks out of the box. It's just like federated pages, like this vertical slicing thing that you need to change Remix for. Yeah. I actually think another good point of using Remix is that something we talked about earlier is that it's taking this step to being platform agnostic, where you may be able to use other frameworks. And if you're using a micro front-end architecture, being able to write other languages may be useful for your company, right? Yeah. So that's a good point. Although I think I generally advocate to not choose micro-front-ends to mix frameworks, unless you're doing some kind of migration thing, where you're deciding to explicitly migrate from React to Zelda, or you say this is not the language you want to use anymore. And in the meantime, while we're migrating, we're going to have both at the same time. The reason is, I think it is like allowing multiple framework does allow for developer freedom, basically. But it does come as a performance cost for your users, but then they have to download multiple frameworks on the client to make everything work together. So I think generally I'm like, try to use one framework so you can deduct the framework cost only once. I think that does make sense. The reason I brought it up is because when I was at Granger, we did have like, there were React, there was like jQuery, Hybris components, there was Felt components. So we had like everything kind of working together and things had been either done by contractor companies or other companies. And then our team was working on the Felt piece, and then they ended up switching to React. So we were kind of working with all of these like within the same app, and that might be like a future. So I was just wondering what your thoughts were on that. But I do agree if you have the choice to just stick with one framework, it's much less overhead for your company. So would you suggest that people use a micro front end at their company? So I think Ruben Casas, who is a person that also talks a lot about micro front end, also has some talks on like, should you use micro front end in your company? And I think he's, I kind of agree with his opinion, which is use micro front end if it solves a problem for your company. So if you have like just a few teams and they can share monorepo, and everybody can contribute and you don't have pain points there, just do monorepo, do this and it's fine. If you start feeling the pain of like, we need to be able to deploy independently because we keep like stepping on each other's feet. It's not manageable, like the PRQ is getting crazy and everything we want really to have this independence of deployment, then go for the like then micro front end is a tool that can help you, you know, like remove those dependency or make them more like looser. So yeah. It's like every other developer question, it depends. Yeah, exactly. Senior developers. Right. Okay. So we have one more question for you. This is kind of going back to Baymax's question earlier with the Webpack Modules Federation, is Tiny Front End using Webpack Modules Federation? Good question. So no, it's not. It could, like the underlying implementation could use Webpack Modules Federation. When I built it, I decided not to. The main reason is then it's not using Webpack to bundle, it's actually using, I think, ES build, I mean, it's using ES build to build it. And the main reason is in Tiny Front End, you specify each dependency you're sharing, like in your, like SPA dependencies. And basically what it does is just do a tiny bit of glue to like expose those on the global context and then like plug them in for you. And Webpack Modules Federation can do a lot of things for you. It can do like dynamic solution and all these kind of things. And that wasn't really needed for Tiny Front End because you cannot just have this one-on-one relationship between the host and the package that comes in, which is why I didn't go with it. It's just a bit lighter. But to be fair, you know, like if, for example, Webpack Modules Federation becomes a bundle agnostic and like can as a standard and everybody starts using it, then it would be rather trivial to change internals of Tiny Front End to like use that in the hood instead. Yeah, that is. Okay. Well, I think that is all the time we have for today. Thank you so much, Adrian, for joining us for the Q&A and thank you for the talk. Yeah, thank you. Thank you for having me. Cheers.
23 min
18 Nov, 2022

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Workshops on related topic