{"id":"l-ds-5-1","courseId":"design-systems","lang":"en","kind":"lesson","summary":"Run a contribution model with clear tiers for fixes, enhancements and new components, triage requests with a decision flow, and set up lightweight governance that decides quickly and in the open.","html":"<p>In Northwind’s first year of opening contributions, we received 112 pull requests and merged 41. Most of the rest died waiting: for an API review that had no schedule, for a designer who was on another project, for a decision nobody felt allowed to make. Teams learned the lesson fast. By month nine, contributions had almost stopped, and three teams had started their own component folders again. A contribution model isn’t a document; it’s a promise about how quickly and fairly the core team will respond.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Three tiers of contribution</h2>\n<p>The fix was to stop treating every contribution the same. Northwind now has three tiers, each with its own path:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Tier</th>\n<th>Examples</th>\n<th>Path</th>\n<th>First response</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Fix</td>\n<td>Bug, docs error, missing story</td>\n<td>Pull request, one core reviewer</td>\n<td>2 business days</td>\n</tr>\n<tr>\n<td>Enhancement</td>\n<td>New variant, new prop, new token</td>\n<td>Short proposal issue, then pull request</td>\n<td>1 week</td>\n</tr>\n<tr>\n<td>New component</td>\n<td>Date picker, data table</td>\n<td>RFC, API review, then build with a core pair</td>\n<td>2 weeks for a decision</td>\n</tr>\n</tbody>\n</table></div><p>A fix needs no permission. An enhancement needs a short written case: what problem, which teams, and the proposed API snippet, because props are public API forever. A new component needs a request for comments (RFC), a document that lays out the problem, evidence from at least two teams, the API sketch, the state matrix and the accessibility plan. The RFC gets discussed at the weekly API review, and someone leaves with a decision.</p>\n<p><code dir=\"ltr\">CODEOWNERS</code> makes the routing automatic: the core team owns the token files and the public API exports, so any change to them requests a core review, while docs pages can be approved by any experienced contributor.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Triage: where does a request belong?</h2>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 260\" role=\"img\" aria-labelledby=\"l-ds-5-1-t1\"><defs><marker id=\"arrow-l-ds-5-1-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-ds-5-1-t1\">Decision flow. Can an existing component do it, maybe with better docs? If yes, point to it. If no, do two or more teams need it? If no, build it locally from system parts. If yes, contribute it to the system with a core pair.</title>\n  <rect class=\"d-box-primary\" x=\"20\" y=\"100\" width=\"130\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"85\" y=\"133\" text-anchor=\"middle\">Request</text>\n  <rect class=\"d-box\" x=\"190\" y=\"90\" width=\"170\" height=\"76\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"275\" y=\"122\" text-anchor=\"middle\">Existing component</text>\n  <text class=\"d-label\" x=\"275\" y=\"142\" text-anchor=\"middle\">covers it?</text>\n  <rect class=\"d-box-success\" x=\"190\" y=\"10\" width=\"170\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"275\" y=\"37\" text-anchor=\"middle\">Point to it, fix docs</text>\n  <rect class=\"d-box\" x=\"400\" y=\"90\" width=\"150\" height=\"76\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"475\" y=\"122\" text-anchor=\"middle\">Two or more</text>\n  <text class=\"d-label\" x=\"475\" y=\"142\" text-anchor=\"middle\">teams need it?</text>\n  <rect class=\"d-box-warn\" x=\"400\" y=\"200\" width=\"150\" height=\"48\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"475\" y=\"229\" text-anchor=\"middle\">Build locally</text>\n  <rect class=\"d-box-accent\" x=\"580\" y=\"100\" width=\"90\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"625\" y=\"124\" text-anchor=\"middle\">RFC and</text>\n  <text class=\"d-label\" x=\"625\" y=\"144\" text-anchor=\"middle\">contribute</text>\n  <path class=\"d-arrow\" d=\"M150 128 L186 128\" marker-end=\"url(#arrow-l-ds-5-1-1)\"/>\n  <path class=\"d-arrow\" d=\"M275 90 L275 58\" marker-end=\"url(#arrow-l-ds-5-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"290\" y=\"78\">yes</text>\n  <path class=\"d-arrow\" d=\"M360 128 L396 128\" marker-end=\"url(#arrow-l-ds-5-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"366\" y=\"118\">no</text>\n  <path class=\"d-arrow\" d=\"M475 166 L475 196\" marker-end=\"url(#arrow-l-ds-5-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"485\" y=\"186\">no</text>\n  <path class=\"d-arrow\" d=\"M550 128 L576 128\" marker-end=\"url(#arrow-l-ds-5-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"553\" y=\"118\">yes</text>\n</svg><figcaption>How Northwind triages a new component request</figcaption></figure>\n<p>Most requests end at the first question. About half of Northwind’s “new component” requests turn out to be an existing component that the docs didn’t make findable, which becomes a docs fix.</p>\n<p>The second question is where teams get nervous, because “build it locally” sounds like rejection. It isn’t. A one-team component built from system tokens and components looks native and costs the system nothing to maintain. Northwind keeps a list of these local components, with owners, and checks it every quarter. When a second team needs something on the list, the original becomes the starting point for an RFC. Three of our current components, including the date picker, started this way.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Accepting every contribution because it’s free</span></p>\n<p>A contributed component isn’t free. Once it’s in the system, the core team owns its bugs, its accessibility, its theming in every brand and its migrations for years. Accepting a one-team component to be nice to that team is how systems bloat to two hundred components that nobody can keep consistent.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">Governance without a bureaucracy</h2>\n<p>Governance is how decisions get made when people disagree. Northwind’s version fits on one page:</p>\n<ul>\n<li><strong>The core team decides</strong> on anything that becomes public API, after hearing from the teams affected.</li>\n<li><strong>Decisions happen in the open</strong>, at the weekly API review that anyone can attend, or asynchronously in the RFC.</li>\n<li><strong>Arguments cite the principles and evidence</strong>, such as usage numbers or research findings, not job titles.</li>\n<li><strong>Every decision is written down</strong> in a decision log with its reasons, so it can be revisited when the evidence changes rather than re-argued from memory.</li>\n</ul>\n<p>Speed matters as much as fairness. A clear “no” in two weeks is better for a product team than a “maybe” for two months, because they can plan around a no.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Make contributors visible</span></p>\n<p>Northwind credits contributors by name in the changelog and in the release announcement, and engineering managers count system contributions in performance reviews. Recognition costs nothing, and it turns contributing from a favor into a career signal.</p>\n</aside>\n<p>In the exercise, you put a new component’s path into the system in order. Next, you scale the theming architecture to a second brand.</p>\n","toc":[{"id":"sec-1","text":"Three tiers of contribution","level":2},{"id":"sec-2","text":"Triage: where does a request belong?","level":2},{"id":"sec-3","text":"Governance without a bureaucracy","level":2}],"takeaways":["Match the process to the size of the contribution; a typo fix and a new component should not go through the same gate.","A new component enters the system when several teams need it, not when one team built it first.","Building something locally is a legitimate outcome of triage, and the system should make that easy rather than shameful.","Governance works when decisions are fast, written down and argued from the principles, not from seniority."],"further":[{"title":"Contributing guidelines for a repository (GitHub Docs)","url":"https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors"},{"title":"About code owners (GitHub Docs)","url":"https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners"},{"title":"Contribute to the U.S. Web Design System","url":"https://designsystem.digital.gov/about/contribute/"}],"quiz":[{"q":"<p>A team fixes a typo in the Tabs documentation and opens a pull request. The contribution guide requires an RFC and an API review for every change. What’s the likely outcome over time?</p>","options":[{"text":"Higher quality, because every change is reviewed thoroughly.","why":"Heavy review on trivial changes doesn’t add quality; it mostly adds delay."},{"text":"Teams stop contributing small fixes and work around problems locally instead.","why":"Correct. When the cost of contributing exceeds the cost of a local workaround, people choose the workaround."},{"text":"The core team gets more time for important work.","why":"The core team spends that time running the heavyweight process instead."},{"text":"Nothing changes; teams accept process as part of the job.","why":"People respond to friction; process cost always shows up in behavior."}],"answer":1},{"q":"<p>The billing team asks the system to add an InvoiceTimeline component. No other team has a similar need. What does Northwind’s triage recommend?</p>","options":[{"text":"Add it to the system, since a contribution is free capacity.","why":"Every component in the system is a long-term maintenance cost paid by the core team, even if someone else wrote it."},{"text":"Reject it and tell the team to wait until another team needs it.","why":"The billing team needs it now; waiting helps nobody."},{"text":"Ask the core team to build it as a priority.","why":"Spending core capacity on a one-team component takes it away from shared needs."},{"text":"Build it locally from system tokens and components, and revisit if other teams need something similar.","why":"Correct. The team gets unblocked, the result still looks native, and the system learns whether the need is shared."}],"answer":3},{"q":"<p>Two senior designers disagree about whether Select should allow free-text entry. Which governance approach resolves it best?</p>","options":[{"text":"Bring it to the weekly API review, argue from the principles and usage evidence, record the decision and its reasons.","why":"Correct. Decisions made openly and written down can be revisited with new evidence instead of re-argued from scratch."},{"text":"Let the more senior designer decide.","why":"Seniority settles the argument today and teaches everyone that evidence doesn’t matter."},{"text":"Ship both behaviors behind a boolean prop.","why":"Avoiding the decision moves it into every product team and adds a prop that interacts with every other one."}],"answer":0}],"exercise":{"kind":"order","items":["search","evidence","rfc","review","build","release"],"title":"Bring a date picker into the system","prompt":"<p>The dispatch team built a good date range picker locally. Now the billing and reporting teams want one too. Put the steps that take it into Northwind UI in order, following the contribution model from the lesson.</p>\n","hints":["Triage starts by checking whether the need is already covered.","The RFC needs evidence before anyone reviews it, and review comes before building the system version."],"explanation":"<p>Triage first confirms that no existing component covers the need, then gathers evidence that several teams share it, which is what moves a component from local to system. The RFC turns that evidence into an API sketch, state matrix and accessibility plan, and the open API review produces a recorded decision. Only then does the dispatch team build the system version with a core pair, and it ships like any other change: stories, docs, a changeset and contributor credit in the release notes.</p>\n","checkLabels":{},"itemText":{"search":"Confirm that no existing component, or better docs for one, already covers range selection","evidence":"Collect the use cases from the dispatch, billing and reporting teams","rfc":"Write an RFC with the API sketch, state matrix and accessibility plan","review":"Discuss it at the weekly API review and record the decision and its reasons","build":"Rebuild the picker in the system repository with a core designer and engineer pairing","release":"Ship it with stories, docs and a changeset, crediting the dispatch team in the release notes"}},"words":649,"readMinutes":3}