How we approach legacy modernization
Engineering

How we approach legacy modernization
Legacy modernization is one of the most common things we're asked to do, and one of the easiest to get wrong. Here's the approach we use, and why it avoids the big-bang rewrite.
The usual failure mode is the big-bang rewrite: a long, risky transition, a system that's degraded for months, and a team that's exhausted before it's done. Our approach is different, and this post draws on a recent migration of around twenty services for a major investment bank.
The Challenge
A set of services that work but are expensive to keep current. In this case, the team owned the downstream services of a trading platform: the services that take a trade, process it and return the result to the customer. They were spread across about five repositories, and the core pain was dependency management.
Whenever a shared dependency needed upgrading or went out of date and became a security risk, the team had to update it in every service, in every repository, with a separate pull request for each. It was slow, hard to track and easy to get wrong. Left alone, the services would fall behind the firm's standards and build up security exposure.
What we did
Start with the pain, not the rewrite. We don't begin by asking how to rebuild the system. We ask what is actually expensive or risky about running it today. Here, the answer was dependency management and end-of-life security risk, not the architecture itself. That reframing keeps the work focused on the real problem instead of growing into a rewrite.
Centralize before you migrate. The single highest-impact move was bringing the services onto the firm's central platform, where dependencies are managed as shared sets. Instead of updating a dependency in twenty places, you update it once and every service picks it up. A manual, multi-repository chore became one verifiable step.
Build a repeatable runbook, not a one-off migration. The first few migrations are always fiddly. We captured what we learned as a standard runbook: copy the code, generate the dependency file, make the service compile, create the run configuration, confirm the existing tests pass, create the deployment target, and verify the service is discovered, load-balanced and logging correctly. With the runbook in place, each migration was faster and more predictable than the last.
Validate end to end against the old system. A migration isn't done when the service compiles. We deploy to non-production, sign off, then deploy to production, and compare results and timing against the old pipeline to make sure the migrated service behaves the same or better. Where a service ran slower, we optimized it.
Turn know-how into shared capability. As we migrated, we learned what typically goes wrong and how to diagnose it quickly. We turned that into knowledge-sharing sessions and helped three other teams migrate their own services, becoming the firm's go-to team for this kind of work.
The outcome
Before, a dependency upgrade meant updating, checking and re-checking every service across several repositories. After, the team updates the dependency in one place, validates the services, and is done. Releases got faster, manual work dropped, and the team could be confident its services were current and secure. As the engagement wrapped up, the client chose to extend it.
Modernization is rarely about rewriting the system. It's about finding the specific thing that makes it expensive to run, usually dependency and configuration sprawl, then centralizing it, making the migration repeatable, and checking every step against the old behaviour. The result is a system that's cheaper to run, safer and faster to release.
Facing a similar challenge? Get in touch. We'd be glad to talk it through.



