From frontend monolith to micro-frontends, step by step
You can move from one large frontend to micro-frontends without a freeze and without a big-bang release. The monolith keeps working while areas leave it one at a time.
Before you start
Check that micro-frontends are the right answer. They help when several teams block each other in one codebase and one release process. If one team owns the whole frontend, better module boundaries inside the monolith are cheaper. The decision is covered in when to use micro-frontends and in rewrite, hybrid or micro-frontends.
If the answer is yes, the method is the strangler approach. New structure grows around the old application and takes over one piece at a time, until the old application is small enough to retire or to keep as one part among others.
Step 1: Draw the boundaries inside the monolith
Do this before any infrastructure work. List the business areas, the routes each one owns and the team responsible for it. Then look at the imports between areas.
- Move each area's code under its own folder or library.
- Remove direct imports between areas. Replace them with a small public API or an event.
- Separate what is truly shared (session, layout, design system) from what is shared by accident.
This step has value even if you stop here. It also shows which areas are easy to extract and which are tangled.
Step 2: Let the monolith be the first host
You do not need a new shell on day one. The simplest start is to add federation to the existing monolith so that it can load a remote at runtime. The monolith keeps the login, the layout and the router. One route now points to code that is built and deployed elsewhere.
Later, when most areas have left, what remains of the monolith is the shell. Some teams prefer to build a clean shell first and mount the monolith inside it. That is the usual choice when the monolith runs on an old framework. See running a legacy app inside a micro-frontend host.
Step 3: Build the minimum platform
Before the first area moves, a few pieces must exist:
- A way to load remotes at runtime and a manifest that says where each one lives.
- The session, shared from host to remote.
- Routing rules: the host owns the top level, each remote owns its prefix.
- A design system package, even a small one, so the extracted area looks the same.
- A pipeline that builds and deploys one remote on its own, with rollback.
Step 4: Carve out the first area
The choice of the first area matters more than the technology. Look for:
- A clear business boundary and a single owning team.
- Few dependencies on other areas.
- Real usage, so the result means something, without being the most critical flow of the product.
- A team that wants to do it.
Move the code to its own project, expose its routes and point the host's route to the remote. Release it behind a feature flag or to a small group of users first. Keep the old code in the monolith until the remote has been stable in production, then delete it.
Delete the old code. If the monolith keeps a copy of every extracted area "just in case", you maintain two versions and the monolith never shrinks.
Step 5: Repeat, area by area
After the first area, the path is known and each extraction should be cheaper. A few rules keep delivery going:
- No freeze. Feature work continues in the monolith for the areas that have not moved. An area is frozen only for the short period of its own extraction.
- One extraction at a time per team. Parallel extractions by different teams are fine. Two at once in the same team are too much.
- Fix the platform as you learn. Each extraction finds gaps in the host, the design system or the pipeline. Fix them in the platform and avoid local workarounds.
- Measure progress by routes. Compare the routes served by remotes with the routes still in the monolith. Everyone understands that number.
Step 6: Decide when to stop
Full extraction is optional. Stop when the remaining cost is higher than the remaining benefit.
Good reasons to leave code in the monolith:
- The area rarely changes.
- No team owns it, and creating a remote would not create an owner.
- It is small and closely tied to the shell, such as settings or the user profile.
In the end state the old monolith is gone, reduced to the shell, or kept as one more remote that happens to be larger than the others.
What usually goes wrong
- Splitting by technical layer. A remote for components and another for services creates coupling without independence. Split by business area.
- Mixing the migration with a redesign. Move first, redesign later. If both change together, you cannot compare old and new behavior.
- No owner for the platform. The host, the pipeline and the design system need a team. See team ownership and Conway's law.
More in this section
- Running a legacy AngularJS app inside a micro-frontend host
- Team ownership, boundaries and Conway's law
- The real cost of micro-frontends
- Common micro-frontend mistakes and how to avoid them
Planning a migration or a micro-frontend platform?
In a 20-minute call you describe your situation and I tell you how I'd approach it. If it isn't worth doing yet, I'll say so.
Message me on LinkedIn