Design system migration

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.

01 · Why we are moving

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.

02 · Take stock

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.
03 · Map old to new

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.

Same thing
Only the name changes. The old name points at the new one, and the swap can be done with search and replace.
Close match
Swap it and look at the screen. The new piece is slightly different in size, colour or behaviour, so someone checks each screen after the change.
No match yet
Decide on purpose. Add it to the new system if more than one team needs it. Otherwise keep it as a named local exception with an owner, not a quiet copy.
Should not exist
Remove it. Many one-offs only exist because the right piece was hard to find. These are replaced by the nearest standard piece.
04 · Keep both working

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:

Old names keep working

Every old name still works for the whole migration window. Nothing breaks because a team hasn't got to it yet.

A fair window

At least one full release cycle for every team that uses the system, measured on their schedule, not ours.

A removal date

Published on the day the old name is retired, never decided later. A date makes the move real.

05 · The token library and Storybook

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.
06 · Choose the order

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.

First
Foundations

Colour, type, spacing and corner radius. With old names pointing at new values, these change most of the product at once.

Second
The common components

Buttons, form fields, menus and dialogs. Few in number, used on nearly every screen.

Third
One test screen

A busy screen where a mistake is cheap, moved fully to prove the path and find the gaps in the new system.

Then
Screens, with feature work

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.

07 · Track 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.

Screens moved
The headline number. The share of screens fully on the new system, weighted by traffic if possible.
Old names left
The work remaining. How many times old tokens and old components still appear in the code, counted by the build warnings.
Old pieces added
Should be zero. Old pieces added this sprint. If it isn't zero, a team is missing something in the new system and I want to know what.
Exceptions
Kept small and named. Local exceptions, each with an owner and a reason. A growing list means the new system has a gap.
08 · Bring people along

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.
09 · Retire the old system

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.

10 · Where I use it

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.

Figma Operating System cover
Figma Operating System, how I run Figma with AI
AI can now generate designs inside Figma, so the system underneath matters more. These are the naming, status icons, tokens, versions and handoff I keep in my own hands, so the output looks like the product and not a template.
Discovery to Scope cover
Discovery to Scope, from request to plan
Requests usually arrive as a feature or a screen to copy, not a problem. This is how I find what the customer is trying to get done, look at the options, and agree a plan the team can build without guessing.
Prompt Library cover
Prompt Library, prompts for design work
Twenty five prompts in the order the work happens: understanding the request, defining the problem, designing and checking it. Each one says when to use it and what to check before you trust the answer.