I've watched three design systems get built from scratch. One succeeded wildly. One failed spectacularly. One limped along in a weird middle state where everyone used it but nobody liked it.
The difference wasn't the technology. It wasn't the design talent. It was something harder to pin down—call it product sense, or maybe just brutal honesty about what actually matters when you're trying to get 200 engineers to use the same button.
Here's what I learned.
The thing nobody tells you upfront
Design systems are political nightmares disguised as technical projects.
You're asking teams to give up control. You're telling a senior engineer who's been writing CSS for a decade that they can't just add margin-top: 8px to fix that spacing issue—they need to file a ticket, wait for review, and maybe get told no. You're telling a designer that their perfectly-crafted dropdown with custom animations needs to use the system dropdown that's... fine, but not what they envisioned.
The technical challenges are real, but they're solvable. The people challenges will destroy you if you're not ready for them.
Every design system starts with optimism. We'll finally have consistency! We'll move faster! We'll stop rebuilding the same components! And yeah, eventually you might get there. But first you're going to spend six weeks arguing about whether buttons should have 4px or 8px of border-radius.
What we got wrong the first time
Our first attempt started with tokens. Beautiful, perfectly-organized tokens. Every color, every spacing value, every shadow, every animation curve—all documented, semantically named, perfectly organized.
We spent three months on tokens before writing a single component.
Nobody used them.
Turns out developers don't wake up excited about --spacing-scale-4 versus --spacing-scale-5. They wake up needing to ship a login form by Friday, and they'll use whatever gets them there fastest.
We should have started with one component that solved a real, painful problem. Not the most foundational component. Not the theoretically correct component. The one that would make someone's week better.
For us, that was the data table. Every team was building slightly different tables with slightly different sorting, filtering, and pagination. They were all buggy. Accessibility was hit-or-miss. Nobody enjoyed maintaining them.
If we'd shipped a great data table first—one that just worked, with sorting and filtering and keyboard navigation and responsive behavior all baked in—teams would have adopted it immediately. Then we could have built momentum.
Instead we shipped tokens and a Button component and wondered why adoption was slow.
The component trap
Here's something that sounds wrong but is absolutely true: most design systems have too many components.
Every time someone needs something slightly different, there's pressure to add a variant or a new component. And early on, when you're desperate for adoption, you say yes to everything. Sure, we'll add a tertiary button. Of course we need both a Modal and a Dialog. Obviously we need separate Alert and Toast and Banner and Notification components.
Two years later you have 147 components and nobody can find anything.
The best design system I worked with had 23 components. Twenty-three. For a product suite that included a complex data analytics platform, a customer support tool, and three mobile apps.
They got there by being ruthless about composability. Instead of a DateRangePicker component, they had a Popover, a Calendar, and an Input that composed beautifully. Instead of seven different button variants, they had one Button with a few props and clear guidance about when to use each variant.
Every component was doing real work. Nothing was there for theoretical completeness.
Documentation is a product
I used to think documentation was just explaining what the props do. Here's the API, here's an example, done.
That's not documentation. That's an API reference, and while useful, it's not why people use your system or ignore it.
Documentation is answering: Why would I use this? When would I not use it? What does good look like?
The system that succeeded had a "Usage" section in every component doc. Not Usage like "here's how to import it" but Usage like "Use the primary button for the main action on a screen—the thing you want most people to do. If you find yourself wanting two primary buttons, one of them should probably be secondary."
That's the guidance teams actually need. Everyone knows how to pass props. Not everyone knows whether this situation calls for a Modal or a Drawer, and if you don't tell them, they'll guess, and half the guesses will be wrong, and your consistency is gone.
We also recorded videos. Five-minute walkthroughs of "here's how to build a typical form" or "here's how to compose a card layout." Engineers would watch these at 1.5x speed and suddenly understand the system's logic instead of just its API.
The test for documentation is simple: can someone use your component correctly without asking anyone a question? Not just technically correctly—with props in the right shape—but correctly in the sense that a designer looking at the result would say "yes, that's how it should be."
The governance question that matters
Everyone wants to know: who decides what goes in the system?
Wrong question. The real question is: how do we make it easy to say no?
If your process for adding components is "propose it and we'll discuss," you'll end up with 147 components. Every proposal will have a reasonable-sounding justification. Every proposer will have legitimate needs. And you'll struggle to say no because saying no feels obstructionist.
We flipped it. The question became: what's the cost of maintaining this for the next five years?
Every component is a maintenance burden. Documentation goes stale. Design patterns evolve. Accessibility standards change. Bugs get filed. Questions get asked. Every component needs care and feeding forever or it becomes a liability.
When you frame it that way, the bar gets higher. Not "would this be useful" but "is this useful enough to justify the permanent ongoing cost?" Usually the answer is no, and that's fine. Teams can build what they need locally, and if the pattern proves valuable across multiple teams, then we'll consider promoting it.
The system that failed never figured out how to say no. It kept growing, quality declined, and eventually teams stopped trusting it.
Adoption is not your goal
I know, weird thing to say. But adoption is a lagging indicator. It's a symptom, not the disease.
We obsessed over adoption metrics. Why aren't more teams using the system? How do we increase adoption? Should we make it mandatory?
All wrong questions.
The right question is: are we solving real problems? When we solve real problems, adoption takes care of itself. When we don't, no amount of evangelism or mandates will help.
The breakthrough came when we embedded with a product team for two weeks. Not to train them or sell them on the system. Just to watch what they built and where they struggled.
Turns out they were spending absurd amounts of time on loading states. Every feature needed loading spinners, skeleton screens, error states, empty states. They were rebuilding this logic constantly and it was never quite right.
So we built a Suspense-based pattern with really good defaults. Drop your async component inside, get beautiful loading states and error boundaries for free. It wasn't even a component, really—more of a pattern with some helper utilities.
Teams loved it. Adoption exploded. Not because we marketed it but because it solved a problem they had every single day.
The scale inflection point
Design systems don't scale linearly. There's an inflection point somewhere around 30-50 engineers where everything changes.
Below that, informal communication works. Someone has a question, they Slack the design system team, they get an answer. Someone needs a variant, you can evaluate it together. You can course-correct in real-time.
Above that, you need systems. You need documentation good enough that people can self-serve. You need contribution guidelines clear enough that people know what will be accepted before they build it. You need office hours and Slack channels and recorded demos because you can't have the same conversation 50 times.
But here's the weird part: the successful design system started preparing for scale at 20 engineers. They built the documentation, the processes, the governance—all of it—way before they needed it. It felt like overkill at the time.
Except when they hit 50 engineers, they were ready. Adoption kept growing. Quality stayed high. It just worked.
The failed system tried to bolt on process after they were already struggling. They had 100 engineers and no contribution guidelines. 100 engineers and documentation that was six months stale. They never caught up.
If you're serious about scale, prepare for it early. The overhead feels wasteful until suddenly it's not.
The dark pattern I see everywhere
Here's a thing companies do: they see that Big Tech Company has an amazing design system, so they try to copy it.
They adopt the same architecture. They use the same terminology. They even copy the documentation structure.
It never works.
Big Tech Company's design system was built for Big Tech Company's problems. They have platform teams, dedicated infrastructure, full-time maintainers, and mature products that can absorb breaking changes with careful migration planning.
You probably have two designers, six engineers who work on the system part-time, and products that need to ship fast without breaking.
The successful system I worked with borrowed ideas from Google's Material, IBM's Carbon, and Shopify's Polaris. But it wasn't trying to be any of them. It was solving our specific problems with our specific constraints.
Start with your actual problems. Maybe you don't need design tokens because you only have one product and one theme. Maybe you need tokens desperately because you're supporting three brands. Don't copy what works for someone else. Build what works for you.
What actually matters
After watching design systems succeed and fail, here's my mental model:
A design system is a product. The users are your designers and engineers. The value prop is making their lives easier. Everything else—consistency, brand alignment, scalability—is a side effect of teams choosing to use your system because it legitimately helps them.
If you're forcing adoption, you've already lost. You might achieve compliance but not success. Teams will use the system maliciously—technically following the rules while producing garbage.
But if teams reach for your system because it's the path of least resistance to building something great, you've won. They'll file bugs. They'll contribute patches. They'll advocate for the system internally. They'll get mad when components don't work the way they expect, which is amazing—it means they trust the system enough to have expectations.
That's the inflection point: when teams get frustrated because your system isn't good enough, rather than frustrated because they have to use your system at all.
The things that will break
Design systems break in predictable ways. Here are the patterns I've seen:
The system gets out of sync with Figma. Designers create mockups with components that don't exist yet or don't work that way. Engineers get frustrated implementing "the design" when the design isn't using system components. Eventually everyone stops trusting the system.
Accessibility becomes an afterthought. The first version of each component is keyboard-accessible and screen-reader friendly. Then someone needs it to do something slightly different, and they modify it, and suddenly tab order is broken. Multiply this by 40 components and you have an accessibility disaster.
Version management becomes untenable. You ship a breaking change, but three teams are in the middle of sprints and can't upgrade. Now you're maintaining two versions. Then another breaking change, and another team can't upgrade. Now you're maintaining four versions. The maintenance burden crushes you.
The core team burns out. Maintaining a design system is relentless. There's always another question, another bug, another request. If your core team is doing this on top of other responsibilities, they'll burn out. If they're doing it full-time but there's only two of them, they'll burn out. You need sustainable staffing or the system dies slowly.
What I'd do next time
Start with problems, not components. Spend a week just observing teams. What do they rebuild constantly? Where do they struggle? What takes longer than it should?
Build one component that solves the most painful problem. Make it amazing. Document it thoroughly. Support it fanatically. Get that one thing to 80% adoption.
Then build the next one.
Create clear contribution guidelines on day one. Make it easy to say no. Optimize for system coherence, not individual feature requests.
Invest in documentation like it's the product, because it is. Videos, examples, usage guidance, not just API references.
Prepare for scale before you need it. The systems that win are ready for growth before it happens.
And most importantly: remember that you're building a product that happens to be for internal users. All the normal product discipline applies. User research. Prioritization. Quality bar. Roadmap. Metrics. Iteration.
The technology is the easy part. The hard part is building something that teams choose to use, and keep choosing to use, months and years later. That's the game.
Tokens and components and documentation are just tools for playing it.