case study

LinkedIn Design System

A design system isn't components — it's the version of the codebase your colleagues actually use.

company
LinkedIn
role
Design Engineer
period
2019–2025
published

The design system isn’t the thing in the docs site. It’s the version of the codebase your product engineers actually reach for at 4pm on a Friday when the deadline is Monday. Everything else — the components, the docs, the Figma library — is in service of that one moment.

Context

LinkedIn at this scale has hundreds of engineers shipping into the same product. Coherence is the default failure mode: ten dropdown menus, twelve toast variants, four spinners that all spin slightly differently. The system’s job is to make the coherent choice cheaper than the bespoke one — for the engineer typing at 4pm, not for the system reviewer six weeks later.

Move

I used to be in the camp that a design system needs to be strict — fewer knobs, tighter constraints, the system’s opinion wins. I’ve changed my mind across a career of watching what actually happens when a system is too strict: pillar teams quietly recreate components because they need to override a color, a font size, one thing — and you end up with three implementations of the same dropdown anyway. The strictness doesn’t prevent the fork; it just hides it.

So the thesis I built around was flexibility, not control. The system’s job is to make the coherent choice cheaper than the bespoke one, which means giving teams real levers — themable components, themable sections, themable pages. At the end of the day every company is product-driven, and the product should be able to control the system rather than negotiate with it.

I picked the components to invest in by watching what teams reinvented. Buttons, dropdowns, dialogs, toasts, form inputs — the predictable five — got the deep treatment: composable APIs, slot patterns, accessibility wired through, theming surfaces exposed where a pillar team needed to override without forking. Anything past that list, I deliberately left thinner. A documented utility class for spacing was worth more than a <Spacer> primitive that nobody would import.

The contribution model was what kept the system from forking. Any product team could propose a new primitive — they didn’t have to wait for our roadmap — but the proposal lived in our repo under a clear review path. Two outcomes were possible: the primitive got accepted and consolidated (and the proposing team stopped maintaining it), or it stayed in their product as a documented exception. The point was to give teams a fast yes path. Once you have that, the bespoke-fork problem mostly evaporates.

Docs paired with lint rules were the third leg. A doc that said “don’t reach for raw color tokens directly, use the semantic ones” was a doc we’d write three times and still find violated in code review. The same rule as an ESLint check caught it before the review. The system stopped being a library and became infrastructure the moment its rules were enforceable in CI.

Outcome

The shift I’m proudest of was behavioral, not numeric. PR reviews changed. People stopped writing can we use the system component here? because not using the system component became the thing that needed justification. The default flipped.

The pattern-debt audit was the cleanup work — every six months, sweep the codebase for the components that had drifted, the props that were quietly being abused, the tokens that had earned a deprecation. Some of those got absorbed into the system (the product team had found a pattern worth keeping); others got killed. Either way, the audit ran on a schedule, not on vibes, and the system’s surface area stayed proportional to the parts that actually carried weight.

The tooling that made all of this stay coherent is its own story — see LinkedIn design tooling for the tokens repo, the Figma plugin chain, and the QA plugin that did the unglamorous work.

What I’d do differently

I over-built one primitive — a flexible-but-clever container that tried to be every layout. It ended up being used wrong more often than right, because the affordance for “the easy thing” was buried under the API for “the powerful thing.” A simpler version that did 80% of the cases, paired with a documented escape hatch, would have shipped sooner and gotten misused less.

And the docs that should have been lint rules — I’d write the lint rule first, every time. The doc is for the cases the lint rule can’t cover. Reverse that order and you spend a year asking people to read carefully when you could have spent a week writing a check.