How I build design systems that engineering teams actually adopt
Sunny Kotecha
Sunny Kotecha
January 12, 202613 min read

How I build design systems that engineering teams actually adopt

Battle-tested tactics from 20+ years of bridging Figma components and production React code with a pragmatic design system.

Design systems fail when they are treated as a gallery of pretty components instead of a product that solves real constraints for engineers. After 20+ years working with front‑end teams, the only systems I've seen succeed long‑term are the ones that are opinionated, versioned, and maintained like real software.

The goal is not to have the most comprehensive Figma file. It is to reduce the time between idea and reliable, accessible UI in production. That means your system has to answer specific questions for engineers under pressure: "Which button variant should I use here?", "How do we handle loading states?", "What does an error look like on mobile?"

Some of my favourite public systems come from Atlassian and Shopify's Polaris. Both treat their systems as living products: they have roadmaps, release notes, deprecations and a clear feedback loop with the teams who consume them.

1. Start with tokens and layout primitives

Before you design buttons, lock your foundations: color, typography, spacing, radius and elevation. Encode them as tokens, whether that's a JSON file, a Tailwind config, or a tool aligned with the W3C design tokens spec.

Once you have tokens, ship a tiny set of layout primitives: Stack, Inline, Cluster, and a responsive Page container. These primitives encode your spacing rules so engineers don't hand‑roll new flexbox layouts on every screen.

2. Design for real product flows, not just states

A system that only documents "happy states" will be ignored the moment engineers have to implement real flows. For each core component, I capture:

  • Default, hover, active and disabled states.
  • Loading and error states, especially for API‑driven components.
  • Responsive behaviours: how it collapses on smaller screens or in tight layouts.

In practice this means pairing engineers and designers early. We build examples in Storybook (or similar) that mirror actual product flows, not just abstract controls. This keeps the system grounded.

3. Version your components like an API

Engineers are wary of design systems that break them without warning. If you want adoption, you must treat your components like a public API. That means:

  • Semantic versioning with clear meaning for MAJOR / MINOR / PATCH changes.
  • A simple CHANGELOG.md that documents breaking changes and migration paths.
  • Deprecation policies that give teams time to migrate away from old patterns.

Tools like Storybook or Ladle make it easy to showcase variants and link them directly from release notes. When a new button variant is introduced, we ship the documentation and example code in the same PR.

4. Integrate with your front-end stack

A beautiful Figma file that doesn't map cleanly to your front‑end stack will always be a source of friction. I prefer systems that ship as an actual package, whether it's a private npm library or a monorepo workspace, that engineers can import like any other dependency.

For React/Vite projects, that often means a combination of:

  • A @company/ui package exposing composable, accessible components.
  • Tailwind or CSS variables wired up to your design tokens.
  • Lint rules or code mods that nudge teams toward system components instead of raw HTML.

5. Governance that doesn't slow teams down

Finally, a system needs a governance model that balances quality with speed. On healthy teams I usually see:

  • A small core team responsible for tokens, base components and accessibility.
  • Contribution guidelines for feature teams to add new patterns.
  • Regular design reviews focused on how well the system is serving real product work.

When your design system behaves like a product, with ownership, metrics and feedback, it becomes something engineers want to use, not something they are forced to adopt.