I led the design system for an enterprise security platform. Its highest-volume consumer stopped being a person some time ago — and almost everything about how you govern a system changes once that is true. Including how you answer the question an engineering leader will actually ask: what did this buy that a component library off the shelf would not have?
A design system is a set of rules for how a product should look. People used to be the only ones reading them. Now an AI reads them too, and it builds far more screens than any of us do.
An AI reads differently from a person. It never asks what you meant, it never notices that a rule is vague, and it never leaves a blank. So the documentation had to change, the order we build things in had to change, and we had to start measuring whether the system actually held.
Getting that right turned out to be worth roughly a fifth of the time it takes to produce a screen.
A design system has always had two audiences: the designer opening the Figma library, and the engineer reading the spec. A third one arrived, and it consumes documentation faster than both combined.
The difference is not that the model is worse. It is that a person and a model fail differently. Hand a designer an under-specified rule and they ask, or they copy a neighbouring component, or they leave it and flag it in review. Hand the same rule to a model and it produces a confident, plausible, wrong answer — and then repeats that answer consistently enough to look like a decision.
Which means the two things a system is normally judged on — is it beautiful, is it adopted — are joined by a third: does it survive being generated?
Everything below follows from taking that question seriously.
Back to the engineering leader’s question, because it is the one that decides whether any of the rest gets funded. My honest answer to it, for far too long, was directional — faster, more consistent, fewer rounds. Directional is not an answer. Someone deciding where two headcount go needs a number with a method behind it, and “let me get back to you on that” loses the room before you have finished saying it.
So: the number, and the counterfactual it is measured against.
The first figure is the whole argument. A component library on its own — ours, Material, Tailwind, any of them — got generated UI about a quarter of the way onto the system. The library is the part you can buy. The remaining sixty-five points arrived in two moves, and the split between them is the part worth carrying.
Writing the rules so a machine could not misread them — exact values rather than relative descriptions, component status encoded where the work happens, a style layer bound to the production tokens rather than restating them — took it to 65%. That is the move most teams stop at, and it is not purchasable, because it has to describe your product rather than somebody else’s.
The last twenty-five points came from testing those rules by generating from them and rewriting wherever the output came back wrong. Same rules, corrected against their own output. It is the cheaper of the two moves and the one that usually gets skipped — and it was worth about a third of the total gain.
One confound, named: component coverage grew over the same period, so some part of that last twenty-five points is more components having documentation at all rather than the documentation getting better. The two are not separable in this measurement.
The step that moved is correction: pulling generated output back onto the system by hand. It ran around two hours a screen and now runs about fifteen minutes. Correction sits inside a screen that takes roughly eight hours end to end, so the saving is a little over a fifth of total design time.
The accuracy percentage and the hours saved are not two findings. They are one thing measured twice — at 25% on-system a designer rebuilds three-quarters of it by hand, and at 90% they check it.
Published gains for mature design systems run 34–50% on design productivity. Mine sits below that band and I would rather it did. An earlier version of this arithmetic used a three-hour correction average and landed on 34% — precisely the benchmark floor, which is too convenient a place to land. Revising my own average down to two hours cut the headline figure by a third and changed none of the conclusions that rested on it. A number that survives being wrong is worth more than a larger one that doesn’t.
On the engineering side there is one measured instance and no rollout. A page went from Figma to a running implementation in about an hour, once its components were mapped to code, against a conventional estimate of two to three developer-days. That is a single page; the mappings were mine; and the hour covers design to correct composition, not design to done. I would say all three of those things before quoting the first one.
The figure worth carrying into a different organisation is not my total — that depends on a former employer’s volume, which is theirs and not mine to publish. It is the unit underneath it: about 1.75 hours of design time returned on every screen, before anything engineering-side. Multiply that by your own volume rather than trusting mine.
And what the arithmetic will not support: any claim about organisational adoption, engineering hours saved at scale, or what happens once the people prompting the system are not the people who built it. Those want measuring, not modelling.
The figures have a sensitivity table behind them, and payback stays under six months in every row — including the one where all three assumptions are cut at the same time. An earlier version of this model, a third larger, paid back in the same window. That the answer barely moves when the inputs do is worth more than the answer.
A number like 90% implies a straight line. It was not one. Three decisions made the rest possible, and I would defend the first two the same way today.
The layer before the backlog. There were known defects filed against components already built and in use. The obvious order is to fix what is broken before building what is next, and I argued the other way. The machine-readable layer moves the correction into the documentation, and tests the documentation by generating from it. A component is built from its doc on a test site and rendered; where it renders wrong, the doc is what gets edited. Once a doc produces the right component it is a verified specification — and a verified specification is what the already-deployed components then get corrected against. Fixing the defects directly would have fixed those defects. Building the layer produced the instrument every later fix was made with.
The honest counter, which I did not have a clean answer to at the time: people were hitting those defects while I built the layer, and asking someone to wait on a second-order benefit is a real cost paid by someone other than me. What makes it defensible is only that the second-order benefit was measured afterwards rather than assumed in advance.
An audit before a refresh. The brief was to move the system from an older visual language to a current one, and the available path was per component — corner radius, dark mode, ship, repeat. I ran the whole product first and let what came back decide the order instead. What it found was that the expensive problems were not in the components. They were underneath them, which is what the next two sections are about.
The pause, which is the one that has not paid. The platform spans two generations of a frontend framework, and the design system could only reach the newer one. Shipping into it would have left one half of the product looking current and the other half not.
The argument that settled it was already sitting in the tracker. There was a backlog of filed bugs about inconsistent UI that nobody had aggregated into anything — individually each one reads as a defect, and together they read as a forecast. I put them together and took that to my manager. We agreed to pause, and I designed the carve‑out: ship only what the older generation had no equivalent of, which meant charts and data visualisation went out while everything with an old counterpart waited. Progress continued exactly where it could not fragment.
The decision assumed the migration would follow. It did not, and a system I had made ready sat behind a dependency I did not control.
Visible inconsistency is a forcing function. It makes the debt legible to whoever has to fund the migration. Pausing made the debt invisible, and invisible debt does not get prioritised.
That reading is the one I cannot dismiss: protecting consistency may be part of why the migration never became urgent, which would make the decision partly responsible for the condition it was waiting on. Does this make the problem easier to ignore? is a question I did not ask at the time and would ask first now.
Most design systems keep governance in a document nobody opens. The status of a component — approved, buggy, deprecated, do-not-touch — lives in a wiki, and the Figma file quietly disagrees with it.
I moved the lifecycle into the file structure itself, so a component’s status is visible before you can use it:
It looks almost too simple to count as governance. That is the point. A rule that lives where the work happens gets followed; a rule that lives in a document competes with the work for attention and loses.
It matters more once a model is involved, because a model has no way to ask whether a component is safe to use. It will happily instance a deprecated component or an experiment if nothing in its context says otherwise. Encoding status structurally means the answer travels with the thing.
Governance that lives in a separate document is governance you are choosing not to enforce.
Design-system governance comes in three shapes, and it is worth saying which one this was rather than leaving a reader with the vocabulary to guess. Not standalone — the system had consumers who had not built it. Not centralised — I was not the only person who could add to it. Federated: five designers contributing into one system with a single owner, none of them reporting to me.
The statuses above are what make that shape survivable. A federated model without a visible lifecycle policy produces a library where nobody can tell which parts are load-bearing, and the failure is quiet — someone builds on an experiment and finds out two releases later.
A contribution model that runs only designer-to-designer documents what designers already know. The more valuable direction is the other one. An engineer implementing from the specs came back with values that existed in the code and in nothing I had written — orphans the documentation had no opinion about. Sitting with her and working through them is how they surfaced at all; they went into the token mapping, so the next person generating against those guidelines got a system that knew about them.
Two things follow, and the second is the more useful one. Staying in the loop after handover is not diligence, it is the only mechanism by which a specification finds out what it is missing. And part of that last stretch from 65% to 90% in §02 is not documentation getting better in the abstract — it is documentation acquiring things it had been silently missing, because somebody building from it said so.
A specification cannot discover its own gaps. The person implementing it can.
The limit worth stating. A federated model with a single owner is only ever as strong as that owner’s availability, and mine is no longer part of it. The lifecycle policy is structural and should hold on its own; the contribution loop depended on someone being there to close it. I would not claim it has been tested without me.
The most expensive mistake available in a design system is authoring a shared thing second.
A concrete case. A container mechanism — a slot that holds consumer-supplied content — was going to be shared by three components. The tempting move was to build the first component that needed it and let the slot emerge from that work.
So the slot was pulled out and specified on its own first: what a slot is, what a consumer may inject, and where the boundary sits between container-owned chrome and consumer-owned content. Each component then became a short spec that says “a slot container, positioned like this, dismissed like that.”
The same discipline catches a subtler failure I have come to think of as a borrowed token doing double duty. A token gets reused for a second purpose because the value happens to match. The moment either purpose needs to change, you cannot move it without breaking the other, and the only fix is to unpick both.
Values matching is not the same as values meaning the same thing.
A type ramp is where a system either states its domain or hides it. Ours carries the usual semantic scale — headings, body, meta — and then a second scale that exists purely because of what the product is:
A security platform is mostly numbers under pressure — counts, percentages, thresholds, deltas. Treating stats as body text and scaling them by eye is how dashboards drift into inconsistency, so they get their own ramp, paired with a name/value convention for the label-and-figure pattern that appears in every panel.
The link scale earns its place the same way. Links exist inline in a sentence, inline in a dense table, and standing alone as a button-like action. Those are three different jobs, so they are three defined styles rather than one style plus judgement — because judgement is exactly what a generator does not have.
On top of the system sits the part that makes it machine-consumable: component guidelines written so a model cannot misread them, specs carrying exact values rather than relative descriptions, and a style layer bound to the production token file rather than restating it.
The test is blunt. Regenerate a page from the guidelines alone, name no fix in the prompt, and count what lands.
Those figures are my own measurements on our system, not instrumented telemetry, and I would describe them as approximate. The method behind them is public and reproducible — I rebuilt it from scratch on a generic system so it could be checked without anything proprietary in it, and in doing so found that one of my own published claims about why it works was wrong.
None of the above matters if the system is not used by people who did not build it. That is the half with no craft in it, and it is the half that decides whether the rest survives.
Five designers worked on and against the system, across every product area on the platform. So the day-to-day was less about designing components than about the things that decide whether a system holds: reviewing contributions, keeping the library and production honest with each other, deciding what was worth standardising and what should stay local, and coaching the people whose work depended on it.
Three of those had outcomes I can point at. One designer went from occasional contributor to owning the icon set outright. One was promoted into wider scope. And a guidance practice I had built was picked up by leads senior to me — which is the version of teaching that is hardest to arrange, because nobody above you has to take it. How that works without a reporting line →
Adoption is built, not requested. Engineering could consume the system directly because 111 components were mapped to their code equivalents over about a month, so pasting a design link into an editor returned the real component — states, edge cases and accessibility already in it — rather than a picture to reimplement. Measurement worked the same way: one instrumentation spec pattern carried across seventeen requirement stories in three teams, so what shipped could be read the same way regardless of who built it.
Then you find out whether anyone used it. Six in-product guides shipped instrumented, and the lead one reached 427 unique users at 35% engagement in six weeks. Two things in that reporting are worth more than the number. One guide came back with zero engaged users and stayed in the report. And one metric read close to 100% click-through, which I footnoted so the room would stop believing it. The figure that flatters you is the one to check first.
The measurable version of adoption is not a dashboard. It is whether a new screen can be built without anyone inventing a value — and whether, when a model builds it instead, the result still passes an audit.
What I cannot tell you is whether it held. I left in August 2026, and the real test of a system is whether it outlives the person who wrote it down. That one is running without me.
The components, screens, token names and product surfaces belong to a former employer and are not shown. What is described above is architecture and method: how the lifecycle is encoded, why shared contracts are authored before their consumers, how a token scale carries domain meaning, and what changes once a model is a primary reader of your documentation.
The measurement method is public in full, rebuilt on a generic system so it can be inspected and re-run by anyone: github.com/omkarpkh/design-system-drift-lab
One last thing, and it works against everything above. A widely cited industry estimate puts roughly 45% of shipped features in the never-used column. So this makes a pipeline cheaper whose largest loss sits upstream of it. Making the wrong screen 22% cheaper is still the wrong screen — worth saying out loud before anyone treats a design system as a productivity programme.
I am happy to go deeper in conversation on any of it.