Modernize without rewriting: how we separated a module from a monolith
"We need to rewrite it" is the most expensive sentence in a system's life. Here's how we renewed a critical piece without switching anything off along the way.
“We need to rewrite it” is probably the most expensive sentence in a system’s life. It sounds like a solution and is almost always the start of a project that takes twice as long, lives alongside the old system for years and ends up repeating its mistakes.
The alternative is to separate it piece by piece, with the original system running. This is the method we follow, with examples from when we took user management out of our own monolith —the origin of Ailita.
1. Find the real boundary, not the diagram’s
The architecture diagram says where a module ends. The code says something else. We start by listing what the module uses from the rest of the system and what the rest uses from it: shared code, data read from elsewhere, common utilities, configuration.
In our case, identity shared more pieces with the rest of the system than it seemed. We decided to take them along, even if it meant repeating a bit of work. Duplicating a little is cheaper than tying two new systems together.
2. Pin down the behavior before moving it
Before changing code that works, we write tests that describe what it does today, even what it does wrong. They’re called characterization tests: they don’t check that the behavior is correct, they check that it doesn’t change by accident.
They’re the safety net that lets you move things with confidence. When one fails, the question isn’t “what did we break?” but “was this change in behavior intentional?”.
3. Put an interface in the middle
The piece being separated gets a clear front door. The original system stops reaching into its details and starts using that door: first pointing at its own code, then at the new piece. That change of destination is the most important step, and it must be possible to undo it in minutes.
4. Live together for a while
During the migration, the old and new systems run at the same time. Data moves with checks: nothing missing, everything matching, samples reviewed by hand. If something doesn’t match, we stop and understand it before going on.
5. Switch off when there’s no traffic left
The old code is removed when the metrics show nobody uses it, not when the calendar says the project is over. Switching off earlier is the most common way to discover, in production, a dependency nobody had listed.
Why we do it this way
- Value at every stage. Each step leaves something working, instead of one big launch at the end.
- Contained risk. Each step can be rolled back without coordinating half the team.
- Improvements that weren’t in the plan. In our case, separating identity forced us to rethink company isolation and the shared sign-in from scratch, instead of inheriting decisions from another era. Today Alfred uses that service too.
If you have a system that holds the business together and nobody wants to touch it, this is the kind of work we do in modernization.