Leading a Major Framework Migration Without Stopping Feature Work
By Carlos Diaz · March 5, 2024 · 3 min read
We ran a major framework migration across a production platform with real daily traffic for the better part of a year, without a single sprint where feature work stopped. The lesson that stuck with me most is that a migration timeline is a product roadmap decision, and it needs the same rigor - milestones, stakeholder visibility, explicit tradeoffs - as any feature launch.
The mechanism that made this possible was a compatibility layer that let old and new code coexist in the same codebase and the same deploy, gated behind feature flags at the module level. This meant every single release stayed shippable throughout the migration - there was never a multi-week branch that had to land all at once, and never a moment where reverting meant losing weeks of work. Each sprint had a visible slice of both feature work and migration progress, which is what kept stakeholder confidence high enough that the migration was never at risk of being deprioritized halfway through.
Testing strategy mattered as much as the code changes. We leaned on contract tests at service boundaries and visual regression tests on the UI layer specifically because they catch the failure mode a migration is most likely to introduce: something that still compiles and passes unit tests, but behaves subtly differently for a user. Manual QA alone was never going to catch that at the scale of a platform-wide migration.
The human side of the migration took at least as much deliberate effort as the technical side. We wrote migration guides that were specific to our codebase rather than generic framework documentation, paired experienced engineers with the team members touching a legacy module for the first time, and added lint rules that made it structurally difficult to write new code against the deprecated patterns - because a lint rule scales further than asking people to remember a convention.
The question that defines when a migration is actually finished is not 'how many files have been converted' - it is 'does the team write idiomatic code in the new patterns by default, without thinking about it.' Measuring files converted encourages declaring victory early and living with a permanently half-migrated codebase; measuring team habits does not.
Key Takeaways
- Treat a large migration as a product roadmap decision with milestones and stakeholder visibility.
- Compatibility layers and module-level feature flags keep every release shippable mid-migration.
- Contract tests and visual regression catch the subtle-behavior-change failure mode migrations introduce.
- Migration guides, pairing, and lint rules do more than a wiki page nobody reads.
- A migration ends when new code is idiomatic by default, not when the last file converts.

