Serguey Asael Shinder: A migration ends when the old path has no callers, not when the new one works
GitHub's engineering blog described this week how it moved github.com from CSS-in-JS to CSS Modules. The new approach was working in its design system by December 2024, with server-side rendering 55% faster. The site as a whole ran entirely on CSS Modules only in June 2026. What filled the time in between is the part worth learning from.
The new components were ready early. What was not ready was everything that used the old way of customising them - an sx prop that, at its peak, appeared about 7,760 times across the codebase. GitHub built a wrapper library so that old usages kept working while new code went straight to the migrated components, then converted the codebase package by package. A rotation of eight engineers migrated 6,419 props over six months. The last 895 were removed in three weeks by two engineers with coding agents. Then the themes had to be untangled from the old library before the dependency could finally be deleted - behind a feature flag, like every other step.
I think this is the normal shape of a migration, and it is worth saying plainly because it is so often misjudged. The work that feels like the migration - designing the new path, proving it is better, shipping it - is the first half. The second half is removing the old path, and it has three properties that make it easy to abandon: it has no visible benefit to users, it is spread across teams who did not ask for it, and each remaining usage is individually small.

A few habits make the second half finish instead of fading out:
- Count the old usages and publish the number. "About 7,760 props" is a goal; "migrate to the new styling" is a direction. A number that goes down is the only progress report people believe.
- Make the old path work through the new one. GitHub's wrapper meant that nothing broke while migration was half done, which is what allowed it to be done in pieces.
- Prove equivalence, not just improvement. GitHub required visual regression snapshots to be identical between old and new styles. "Looks right" does not scale to thousands of call sites.
- Delete the dependency on purpose. The migration is not finished when the count reaches zero; it is finished when the old library is gone and cannot be imported again.
The detail I keep coming back to is that automation helped most at the end, not the beginning. By the time the last 895 usages remained, the patterns were known and the cases were well understood, which is exactly when a tool - human-written codemod or AI agent - does its best work.