EduFlow

Section 5 · Adoption at Scale

Measuring Adoption and Saying No

18 min3 min readCode playground

Lesson at a glance

Measure design system adoption, token coverage, version currency and team satisfaction with automated scans and surveys, and say no to requests in a way that protects the system and keeps teams’ trust.

  1. 1What to measure
  2. 2Collecting adoption honestly
  3. 3Saying no
  4. 4Where you are now

No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.

Two years into Northwind UI’s second life, I reran the color script from the interface inventory lesson. Sixty-four greys had become nine, and seven of those were primitives in the token file. That one number did more for the team’s funding than any presentation, because it was the same measurement, taken the same way, showing a before and an after. Measuring is how a design system proves it’s a product worth paying for, and how it finds out where it is failing its users.

What to measure

No single number tells you whether a system works. Northwind tracks four trends, reviewed monthly:

Metric Question it answers How we collect it
Component adoption How much UI is built with system components? Import scans of every repository, DOM scans of key pages
Token coverage Are styles using tokens or raw values? The color script, extended to spacing and type
Version lag How far behind are teams? Package versions across repositories
Satisfaction Does the system make teams faster? A five-question survey every quarter

Together they resist gaming. A team could inflate adoption by wrapping everything in a system Box, but token coverage and satisfaction wouldn’t move.

Collecting adoption honestly

Import scans are the backbone. A script walks each repository, parses source files, and counts JSX elements whose component was imported from @northwind/ui versus components defined locally. GitHub’s code search gives a fast first answer for a single question, such as “where is the deprecated kind prop still used?”, which is how the deprecation lesson’s usage tracking works.

For production pages, every Northwind component renders a marker attribute:

HTML
<button class="nw-button" data-variant="primary" data-nw-component="Button">Assign driver</button>

A scan of the rendered page counts elements with data-nw-component against all elements of the same kind. The attribute matters more than it looks. The first version of our scan counted the nw-button class, and reported 94% Button adoption on the marketing site. Then we found three teams had copied the Button’s CSS into their own components, class names included. Only the real component writes the data attribute, so forks stop counting as adoption.

The numbers then roll up per team:

JavaScript
const scans = [
  { team: 'Dispatch', system: 412, total: 455, version: '5.2.0' },
  { team: 'Billing', system: 96, total: 180, version: '4.9.1' },
  { team: 'Marketing', system: 230, total: 251, version: '5.2.0' },
  { team: 'Reporting', system: 40, total: 160, version: '3.8.0' },
];
const latestMajor = 5;

for (const s of scans) {
  const percent = Math.round((s.system / s.total) * 100);
  const majorsBehind = latestMajor - Number(s.version.split('.')[0]);
  console.log(`${s.team.padEnd(10)} ${String(percent).padStart(3)}%  ${majorsBehind ? `${majorsBehind} major(s) behind` : 'current'}`);
}

Reporting’s 25% isn’t a reason to send an email to its manager. It’s a research question: what does Reporting need that the system doesn’t have? In Northwind’s case the answer was charts and dense tables, which became the next two quarters of roadmap.

Saying no

A system that says yes to everything turns back into the 64 greys, with extra steps. Northwind says no to one-team components, brand behavior changes, props that bypass tokens, and anything that breaks a principle. The skill is saying it in a way that keeps the team’s trust:

  1. Name the reason, using the principle: “Per-component colors would break dark mode and Tidewater, and our principle is accessible before beautiful.”
  2. Offer an alternative: a local build from system parts, an existing component with better docs, a documented escape hatch such as a component token.
  3. Write it down in the decision log, so the next team with the same request gets the same answer and the same alternative.
  4. Leave the door open: “If two more teams need this, bring it back to API review.”

Where you are now

You’ve followed Northwind UI from an audit of 64 greys to a system that forty teams use: principles and a team model, tokens in three tiers with themes and brands, components with composable APIs and accessibility built in, versioned releases with deprecation paths and visual tests, and a contribution model with governance and metrics. None of it is finished, and that’s the point of running it as a product. In the exercise, you write the adoption scan that tells you where to look next.

Key takeaways

  • Measure adoption with signals only the real system produces, such as package imports or a component’s own data attribute, not class names that can be copied.
  • Track a few trends together - component adoption, token coverage, version lag and satisfaction - because any single number can be gamed.
  • Use metrics to find where the system is failing teams, never to shame teams for low numbers.
  • A good “no” names the principle, offers an alternative, and is written down so it can be revisited with new evidence.

Further reading

Check your understanding

3 questions

Question 1 of 3

Northwind’s VP sets a target: “100% design system adoption by Q4”. What’s the main risk?

Question 2 of 3

Which signal is most reliable for counting where the real system Button is used in production pages?

Question 3 of 3

The reporting team asks for a chartColors prop on every component so they can match their dashboards. How should the core team say no?

Practice

Code playground

Write the adoption scan

Write measureAdoption(root) so it scans the <button> elements inside root (a document or any element) and returns { total, system, percent }:

  • total: the number of <button> elements,
  • system: how many have data-nw-component="Button", the marker only the real component renders (the nw-button class doesn’t count; teams copied it),
  • percent: system / total as a whole number rounded with Math.round, or 0 when there are no buttons.

Then write the page result into #report as 4 of 8 buttons use Northwind UI (50%), using your computed numbers.

<header class="bar">
  <button class="nw-button" data-nw-component="Button" data-variant="primary">New shipment</button>
  <button class="btn-legacy">Export</button>
</header>
<main>
  <button class="nw-button" data-nw-component="Button">Filter</button>
  <button class="nw-button" data-nw-component="Button">Sort</button>
  <button class="nw-button">Refresh</button>
  <section id="settings">
    <button class="nw-button" data-nw-component="Button">Save</button>
    <button class="btn-legacy">Reset</button>
    <button class="nw-button">Advanced</button>
  </section>
</main>
<p id="report"></p>Loading the editor…

Ctrl/⌘ + Enter to run · Runs in a sandbox in your browser. Nothing leaves your device.

Preview

Console

(no output)

Checks

  • measureAdoption(document) returns 8 buttons, 4 from the system, 50%
  • Copied nw-button classes don’t count as adoption
  • Scanning #settings alone returns 1 of 3 (33%)
  • The count updates when a system button is added to the page
  • #report shows “4 of 8” and “50%”