Strava Web Microfrontends
Strava began, like many other companies, as a Ruby on Rails app. We named ours “Active”. Ruby on Rails is an outstanding framework for monolithic applications, but like other companies who grew exceptionally large, our codebase expanded and the monolithic aspect of our app began feeling suboptimal. So, we began exploring methods to manage our increasing complexity.
Now, we are migrating away from our monolithic application. We are extracting old logic out of our monolith and into a fleet of microservices. We started that process years ago and we aim to retire Active soon.
On our frontend web, we will do our part by refactoring our view layer to use microfrontends.
Microfrontends
Microfrontends are a frontend architectural strategy where you piece together independent frontend fragments into a single webpage.
Things like the main navigation, the footer, Suggested Follows and the Activity Feed, could all be refactored into microfrontends.
This image shows how we might decompose our main dashboard page into different microfrontends.
We like this approach for several reasons. Mostly, because it creates a specific strategy for extracting web pages out of Active and into a new repo. After we’ve extracted enough content into the new repo, we can retire Active.
Another reason we like microfrontends is that they help us shape our frontend engineering organization. We will ask one team to build and maintain the main shell app that hosts the microfrontends. Next, we will ask product oriented teams to build, deploy and maintain their microfrontends. They won’t need to worry about the shell app.
This is one of microfrontend’s major benefits: increasing independence among teams. In the end, we want each piece of our website to be owned and maintained by specific teams.
To implement this pattern, we’ve decided on two main techniques: module federation and using Active as a shell.
Module federation
Webpack 5 introduced module federation as a way to dynamically import JavaScript modules, at runtime, from a remote host. You can, for example, upload React components to s3 and then import them into your web server. This is how we will deploy and serve our microfrontends.
This diagram how developers build microfrontends and upload them to s3. The shell app federates them into a single webpage that it sends to the browser. The browser renders the page and it might also fetch data from our GraphQL server.
Apps can import federated modules at runtime, instead of downloading them into the app at build time. This means the app can fetch dependencies from a CDN at runtime. Consequently, we can deploy federated modules without needing to redeploy the web server. That’s one of many features we look forward to.
Each of our microfrontends will be an individual federated module. The shell app will import those federated modules and place them in the DOM along with any required data. For now, we will use Active as our shell app.
Using Active as a Shell
Active is a big app that does a lot of stuff and retiring it is a huge project. Towards that end, our engineers work in groups that each tackle different facets of the problem.
We organize engineers into Communities of Practice, otherwise known as guilds, focused on single parts of the technology stack. In the past, our backend and frontend engineers who worked with Active belonged to a single guild called “Web”. But we grew and it came time to split this group into two new guilds: Server and Frontend Web.
The frontend web guild wants to help retire Active, so we began by replacing some pages with small micro-apps, each hosted on their own web server. These apps proved that building new web features outside of Active provides many benefits. But those apps contain a lot of boilerplate code and dependencies. Now, instead of small apps, we are moving towards even smaller microfrontend components.
A single microfrontend needs a “shell” app to place it on the page. The shell app also passes along backend data like the current athlete’s identity and authorization details.
After we get further along, it will come time to replace Active with a brand new shell app. We will probably choose a JavaScript framework like NextJS because we have already built and deployed several NextJS apps. Or, we might choose a Scala web server since all of Strava’s microservices are built with Scala and we have a lot of in-house expertise with that language.
For now, Active does many of the things a shell app should do, like authentication and logging. We will continue to use it while we swap out pieces of our web pages with microfrontends on our way to building a brand new shell app.
How We Plan to Migrate
Because Active is monolithic, our frontend code is spread throughout different layers. Frontend engineers need to account for all the parts of a web page, both on the client and the server.
Also, we want to replace our website incrementally. Active contains decade-old frontend code with many dependencies, including jQuery, Backbone and React. As we change our app piece by piece, we want to prune old code and use fewer libraries.
The book Microfrontends in Action mentions several strategies to migrate from a monolith to microfrontends. One strategy in particular the author calls “Slice by Slice”. That means incrementally slicing off a piece of your monolith and replacing it with a microfrontend. You do this repeatedly until your entire monolithic frontend has been replaced by microfrontends. He refers to a blog post that names this pattern the “Strangler Fig Application” after a unique plant that slowly envelopes and replaces an old tree.
We chose this as our strategy: change our webpages piece by piece until each page comprises a collection of microfrontends. Use Active as the shell for these microfrontends for now. Then, in time, create a brand new shell app to replace Active.
Conclusion
Microfrontends increase complexity because they involve more teams taking ownership of smaller pieces of our website. Progress seems slow. But hopefully moving slowly at the beginning means we’ll end up with a well thought out platform that our developers love.