Good design systems defend themselves
Most design system advice stops at components. The harder problem is making the system usable without you: documenting layout, recording deviations, and knowing when custom is worth the upgrade bill.
11 min
The real measure of a design system is not how good the components are. It is whether someone can build a screen you have never seen, get it right, and never once ask you about it. By that measure most of the systems I have worked on failed for their first year, including ones I was proud of.
The baseline is the least interesting decision
Teams lose weeks arguing about which foundation to build on. MUI, AG Grid, Fluent, Ant, Radix plus a styling layer, or something in-house with opinions nobody remembers choosing. I have worked on top of most of those. The honest answer is that whichever one you inherited is usually the right one, because it is already in production, the team already knows its failure modes, and switching costs more than it returns.
What the framework gives you is behavior, accessibility plumbing, keyboard handling, and a maintenance contract you did not have to write. Rebuilding that is almost never defensible. The interesting work all happens in the layer above it: the tokens, the domain-specific components, the composition rules, and the documentation that decides whether any of it survives contact with a team.
So the baseline is a starting condition, not a strategy. Everything below is about the layer you add on top, and the two questions that layer keeps forcing: when is going custom worth it, and how do you let people add to the thing without quietly breaking it.
When custom is actually worth it
Every deviation from a framework default is a small loan. You get the behavior you want now and you pay interest at the next major version, when the thing you overrode has moved underneath you. That is fine. Loans are a normal way to build. You just want to know you are taking one, and roughly what it costs.
- 01Cosmetic or behavioral restyling is a token problem and should never become a fork. Changing what a component does, or when it does it, is a fork whether or not you call it one.
- 02Does the domain contradict the model this is the strongest case for custom. A data grid that assumes rows are independent will fight you forever if your rows are hierarchical and inherit state from a parent.
- 03Can tokens get you there if the answer is yes, stop. A surprising share of custom components exist because nobody checked whether the token layer already had the answer.
- 04What does the upgrade look like if you cannot describe how this deviation survives the next major version, you have not finished designing it.
My rule of thumb: go custom when the framework's interaction model is wrong for the domain, not when its defaults are ugly. I have got this wrong in both directions. I have over-customized a component set that a few tokens would have handled, and I have also forced a framework pattern onto a workflow that genuinely hated it, then spent a quarter apologizing for the result in usability sessions.
Write down the deviation, not just the component
Design systems rarely die of bad components. They die of undocumented deviation. Someone changes how a select behaves for a good reason, ships it, and moves on. Six months later nobody can tell whether that behavior was a decision or an accident. So nobody touches it. Then the next person needs something similar and builds their own rather than risk it, and now you have two.
The fix is boring: document every component from two angles. What it is for, and exactly how and why it departs from the framework default. The first half everybody writes. The second half is the one that gets skipped, and it is the half that keeps the system honest.
Undocumented deviation is how a system quietly forks from its own foundation until neither designers nor engineers trust it.
- What the framework does by default, stated plainly.
- What we changed, in behavioral terms rather than visual ones.
- Why, in one sentence a stranger can evaluate.
- What this costs at the next upgrade, and who should look at it then.
- Who decided, and roughly when.
Five fields. It takes about four minutes per component and it is the difference between a system people extend and a system people work around. If you only adopt one thing from this piece, adopt this one, because it is the cheapest and it compounds.
Document layout, not just components
Here is the gap almost every system has. A component library tells you what a button is, what states it has, which props it takes. It tells you nothing about how to compose a page. So the moment a developer needs a screen that does not already exist, the only place the answer lives is in the designer's head, and back it comes to the designer.
That is a documentation failure disguised as a collaboration problem. Layout is part of the system. It just rarely gets written down, because it feels like taste rather than rules. Mostly it is not taste. It is a set of decisions you have already made and could describe.
- Page archetypes: the four or five shapes your product actually uses, and which one a new screen belongs to.
- Grid and container rules, including what is allowed to break out of them and why.
- Density: which surfaces are compact, which are comfortable, and what decides that.
- Where empty, loading, and error states sit inside each archetype, so they stop being an afterthought bolted onto a happy path.
- What earns a place above the fold on a data-heavy view, expressed as a rule rather than a preference.
- Spacing decisions with their reasons, so someone can extend the scale instead of guessing at it.
I keep this in markdown files in the repository, next to the code, not in Figma and not in a wiki. Three reasons. It is version controlled, so a layout rule changes through the same review as everything else. It is where developers already are, and documentation nobody walks past does not get read. And it survives the design tool, which you will change at some point whether you plan to or not.
The payoff is asynchronous work. A developer picking up a new screen at eleven at night has the composition rules in front of them, in the repo, without a meeting. They can prototype something structurally correct and bring it to me as a question about judgment rather than a question about the rules.
The designer as bottleneck
Owning a design system feels like influence. Some of the time it is actually a queue with your name on it. Every quick sanity check is a branch someone is not merging. Every unwritten convention is a message they have to send you and wait on. Nobody sets out to build that; it accumulates, and it accumulates fastest when the system is good enough that people genuinely want to use it correctly.
It also feels productive, which is the trap. You are busy, you are consulted, the work looks like leadership. But a system that only produces correct output when you are in the room is not infrastructure. It is a service with one person behind the counter, and it stops the day you take a holiday.
Good design systems defend themselves. They let developers use them correctly without me in the room.
Self-defense is mostly unglamorous. Constraints encoded in props and types, so the wrong combination is hard to express rather than merely discouraged. Documentation that answers the predictable question before it gets asked. Deviation records that explain where the boundary is and why. And components that fail loudly instead of degrading quietly, because a system that bends silently teaches people the rules do not matter.
Additions that do not break the thing
The other half of self-defense is governance, which is a heavy word for a short list of rules about how things get added. Most of it is naming, and naming is load-bearing far beyond what it looks like.
- Component names match what the code exports. If the design file says Panel and the code says Card, every conversation now carries a translation step.
- Token names describe role, not value. A surface-raised token survives a palette change; a gray-100 token becomes a lie the first time the palette moves.
- A rename ships as a deprecation with a migration note, never as a surprise on a Tuesday.
- A new component needs a stated reason it cannot be composed from what exists. Usually there is one. Sometimes writing the sentence is enough to reveal there is not.
- Accessibility criteria live per component, not in a central document nobody opens. Contrast, focus behavior, and semantics belong next to the thing they describe.
None of this requires a committee. It requires that someone can propose a change, someone can approve it, and the decision gets recorded where the work lives. A system without that decays at the speed of its busiest sprint.
AI is a good pressure-tester and a bad authority
There is a second reason the markdown matters, and it is newer. Developers now build with assistants reading the repository. Documentation written as markdown next to the code is documentation those tools consume. Component docs and layout rules stop being a courtesy to humans and become the context that shapes generated code. Write the rules down properly and you are not just briefing your team, you are briefing the thing sitting next to your team.
I also use it in the other direction, as an adversary for my own specifications. Paste in a component spec and ask which states are undefined. Ask what happens at 400 pixels, or with ten thousand rows, or when every string arrives twice as long from a German translation. Ask it to argue the opposite of a decision I have already made and see whether my reasons survive being said out loud. It is very good at finding what I forgot.
The useful side effect is that writing documentation precise enough for a machine forces the same precision a new developer needs. Vague docs and unusable-by-AI docs fail for the same reason: they describe intent without describing rules. Fixing them for one audience fixes them for both.
If the system is already in trouble, audit before you rebuild
Inherited systems in bad shape invite the rewrite instinct, and the rewrite is almost always the wrong first move. You cannot fix drift you have not measured, and a rebuild reintroduces every unrecorded decision as a fresh mystery.
- 01Inventory what components exist and how often each is actually used, which is usually not what anyone predicts.
- 02Drift where design and code disagree, listed specifically rather than lamented generally.
- 03Duplication which components are three near-identical things wearing different names.
- 04Sequenced repair an order of operations a team can absorb alongside feature work, because nobody is stopping the roadmap for this.
The output is not a verdict on whoever built it. It is a backlog with an argument attached, which is the only form of criticism that tends to get acted on.
The test
You know the system is working when you find out about a screen after it shipped and it is right. Not approved by you, not reviewed by you. Just correct, because the rules were legible enough that being correct was the easiest path available.
Worth saying that not every product needs this. Below a certain size, a strict token sheet and a well-kept component folder deliver most of the benefit at a fraction of the ceremony, and the full apparatus becomes overhead you maintain instead of work you ship. The threshold, roughly, is more than one team building the same patterns, or a second brand becoming plausible. Past that point, the documentation is the system. The components are just what it happens to contain.