How I move a product to a new design system without stopping the roadmap.
A migration almost never gets its own quarter. Feature work keeps shipping while the old system and the new one live side by side. This is how I plan the move, keep both systems working, and actually finish it instead of leaving it half done.
- Input
- Two systems
- Output
- One system
- Runs
- Inside feature work
- Status
- Living
Write down why the move is worth it
Every migration hits a week where a deadline makes the old way look easier. If the only reason for the move is that the new system looks nicer, that is the week it stops.
So before any work starts I write the reason in one or two sentences, with someone in product or engineering who owns it alongside me. A good reason names a cost the team already feels: screens that don't match, every new feature built from scratch, accessibility problems fixed one screen at a time, or a rebrand that would mean touching every file.
I also write down what finished looks like, so we know when to stop. Usually that is: no screen uses the old system, and the old system can be deleted.
Count what is really in use
The old system's documentation describes what it was meant to be. The product shows what it became. I take stock of the product, in both the code and the design files.
For each colour, type style, spacing value and component I note how often it appears and where. I also look for the one-off copies: a button someone rebuilt for one screen, a colour typed in by hand instead of taken from the system.
The list nearly always has the same shape. A small number of items cover most of the screens, and a long tail of one-offs covers the rest. That shape decides the plan: the common items are where the move pays off, and the tail is where most of the decisions are.
- In the code. Search for old style names, old components and colours typed in by hand, and count each one.
- In the design files. Library usage reports show which components and styles are used and how often, and which files have detached copies.
- By screen. A list of screens with their traffic, so the order of the move can follow what customers actually see.
Decide what each old piece becomes
Before mapping anything, I clean up the values. Near duplicates are grouped into one scale, so eleven greys that differ by a hair become five. This is the only step that removes anything, so it comes first and gets reviewed. Then I name each value by what it is for, such as background, text, border or warning, instead of what it looks like. A value with a job comes across. A value with no job does not.
With the list in hand, every item gets one of four answers. I write them in a single mapping sheet that design and engineering both work from, so nobody has to guess what replaces what.
Nobody should have to stop work to move
If the move breaks things, teams put it off. So before any screen moves, I make the old and new systems work together. Old colour and spacing names point at the new values in the token library, so most of the product changes in one release without anyone editing a screen. Old components are rebuilt on the new ones underneath, so they keep working while teams switch over.
The same rules I use for retiring a value in Kinetic apply here, and all three are agreed at the start:
Every old name still works for the whole migration window. Nothing breaks because a team hasn't got to it yet.
At least one full release cycle for every team that uses the system, measured on their schedule, not ours.
Published on the day the old name is retired, never decided later. A date makes the move real.
Where the move actually happens
Two tools carry most of the migration: the token library, which holds every value, and Storybook, which shows every component. If both are set up well, most of the move is a change in one place that the build carries everywhere else.
The token library
I keep tokens in three layers: the raw values, what each value means (like "text on a warning" or "page background"), and the tokens a single component uses. Screens only ever use the second and third layers. That is what makes a migration possible, because the old system's names can be added as aliases in the meaning layer and pointed at the new values.
- One source for design and code. The library is built with Style Dictionary into CSS variables for the code and variables for Figma, so a value changes in both at the same time and the two never drift apart.
- Old names are marked in the library. Each alias carries the name that replaces it and its removal date. The build prints a warning wherever an old name is used, which is also how the remaining count in section 07 is measured.
- Themes come along for free. Light and dark mode, and any second brand, are just another set of values for the same meanings. Once a screen is on the meaning layer, it supports every theme without more work.
Storybook
Storybook is where designers, engineers and product see the same thing. Every component has a story for each of its states: default, hover, focus, disabled, error, loading, empty, long text, and light and dark. During a migration it becomes the checklist.
- A component is moved when its stories say so. Every state renders on the new tokens and passes the accessibility checks. Not before.
- Old components stay in Storybook, labelled. Each one carries a deprecated tag, a link to what replaces it and the removal date, so nobody picks it up by mistake.
- Old and new side by side. For close matches, one page shows the old and new component together, so the team can see exactly what changes before they swap it.
- A migration page in the docs. The mapping sheet lives in Storybook next to the components it describes, with the status of each one, so there is one place to check.
Move the pieces that change the most for the least risk first
The order matters more than the speed. I move in layers, and within each layer I start where the change is visible but a mistake is cheap.
Colour, type, spacing and corner radius. With old names pointing at new values, these change most of the product at once.
Buttons, form fields, menus and dialogs. Few in number, used on nearly every screen.
A busy screen where a mistake is cheap, moved fully to prove the path and find the gaps in the new system.
When a team works on a screen, they move it. Screens that handle money or sign-up wait until the path is proven.
One rule starts on the first day: new work is built on the new system only. Otherwise the old system keeps growing while we try to shrink it.
A few numbers, looked at every sprint
A migration nobody can see slowly stalls. I keep a handful of numbers that update by themselves from the code and the design library, and we look at them in the same meeting every sprint.
Most of a migration is people, not pieces
Designers keep using the old library because it is already in their files. Engineers keep the old component because they know how it behaves. Neither is resistance. The new way just has to be the easiest way.
- A short guide per component. What replaces it, what changes, and a before and after. Written once, linked from the warning in the code.
- Pair with the first team. I sit with the first team that moves a screen. Their questions become the guide for everyone after them.
- An open hour each week. One place to ask, so questions don't scatter across chat threads.
- Swap the libraries for people. Moving design files to the new library in bulk saves every designer from doing it by hand.
Finish it, on the date we said
The last ten percent is where migrations stall, because the remaining screens are the hard or forgotten ones. The published removal date is what gets them done. A few weeks before it, old names start showing warnings in the code, and I go through the remaining list with each owner.
On the date, the old names stop working, the old design library is unpublished, and the documentation points only at the new system. Then I tell everyone it is done. Saying it out loud matters, because otherwise people keep the old system around just in case.
Where this shows up on this site
The rules for keeping old names working come from how Kinetic retires a value: the old name keeps working, the window is set by the slowest team, and the removal date is published up front. A migration is the same rules applied to a whole system at once.
For the design file side, the Figma Operating System covers the naming, tokens and versions I keep in my own hands in Figma. The new library has to get those right before any file moves to it.
In short: know why you are moving, count what is really there, keep both systems working through the token library so nobody has to stop, let Storybook say when a component is done, move in a deliberate order, keep the numbers in view, and finish on the date you published.
More playbooks
The Figma Operating System is how I keep a design library in order. Discovery to Scope is how I turn a request into a plan. The Prompt Library has prompts for each step of the work.