Triumphs, losses, and lessons from building a design system

Alex Couch
Alex Couch's portfolio
14 min readJul 12, 2019

At Hired, we inherited, modified and re-built a design system; then ultimately scrapped it all and adopted a component library off the shelf. Here are some highlights (and lowlights) from that experience.

Illustrations courtesy of … me!

Background

I led the design system initiative at Hired for 16 months. We had an existing front-end style guide called Fortitude: it was a library and implementation method based on BEM-style and functional css classes.

The system became outdated and misunderstood over time — especially without the original developers around to own, teach, and enforce it — but it still had a lot of utility. For Hired’s product designers, it granted us visibility and (some) predictability in implementation, but often locked us into old styles and undesirable limitations.

The original “design system refresh” initiative was just that: a refresh. Modify some components, and add new ones where necessary. Freshen it up. Our resources were limited accordingly. But the complexity of changing a production-live system led us down different roads (and bumps in the road): we shifted to building new components on top of the existing system, introducing some temporary redundancies (e.g., old buttons vs new buttons) to help ease the transitions and let teams move forward without having to confront design/tech debt on every project.

Eventually, organizational changes to our front-end tech stack — and the continued limitation of “design systems” resources — led us to adopting a component library off the shelf (Ant Design’s) and starting fresh. It was a disappointing, but responsible outcome for my scrappy guerrilla design systems team. And we learned a few things along the way.

Triumphs

Establishing a shared language

Design systems (can) help improve organizational communication: I’m certainly not the first designer to discovery that. At Hired, the phrase “shared language” is something I used a lot to sell this project to product leadership and the engineering organization. And it worked!

Some of that added value is tactical , e.g., everyone using the same name for a literal component that would be re-used: “primary button” has meant different things at Hired over the years, but defining and then using that language in design specs made our teams move more deliberately.

There was broader organizational benefit as well: the design systems project was a great conduit for teams to talk about how developers and designers wanted to work together. It wasn’t about individual projects and requirements; instead, it was a rare conversation about how front-end (and design) should work at Hired. Not everyone had cycles to work directly on the design systems project, but we did have some important checkpoints — e.g., weekly updates and design team updates in developer meetings — that helped us identify and involve our most enthusiastic and experienced teammates.

Ultimately, I bet this is the primary upside from the entire project. In a (admittedly frustrating) twist, the biggest win wasn’t the technical system per se, but instead the organizational choreography around “a system.”

A portion of the Demo page we set up to describe the system: this helped to educate about usage, but also about value.

A few components can go a long way

We spent months (albeit, time “on the side,” rather than dedicated) solidifying six foundational categories of components: Buttons, Inputs*, Sections, Cards, Modals, and Tooltips. Just six. It was hard to explain, and it made the system seem small and insignificant. And later on, when evaluating external component libraries, people would get stuck on a library’s “quality” correlating to the number of components available. More is better, right?

But after inventorying Hired’s products and existing design system, these six appeared to be the building blocks that everything else was built from: from the Hired.com homepage, to a settings form in the Employer project, these six component categories were the DNA.

Plus, having more components available isn’t necessarily better: we were trying to consolidate patterns, not create superfluous options and opportunities for accidental fragmentation. We wanted designers thinking about the user, use case, and the content; by having fewer (but stronger) patterns available to them, we’d reduce the amount of UI tinkering that our designers had to do, encouraging them back to the big questions.

* Yes, “inputs” is a category that includes plenty of individual ‘atomic’ components, but it was an organizational category that we carried over from the old style guide. The point is: the system only showed a few pages and looked deceptively small.

Tools, not rules

I threw this phrase around a lot during this project, and it’s become a bit of a mantra of mine. Often, designers and creative types can see a system as a set of mandatory rules: at worst, another set of meetings and hoops they have to jump through to hand off his or her (now-generic) designs.

But it doesn’t have to be that way: a good design system should help designers and developers in their work. Speed things up. Raise the floor. Save file management. Save lines of code. They should be useful tools that help these creators work faster and focus on the important stuff.

Characterizing our system this way made it easier to communicate about it in a way that wasn’t as invasive or uninvited. It gave designers the flexibility they needed to do good work: If a component isn’t working for you, then the tool isn’t working hard enough… make the overrides you need to in this case, then let’s talk about how to make the system work better for you.

This introduced some micro-fragmentations early on, but ultimately allowed our busy team to complete project work without too much perceived “slowdown from the design system.”

Versioning design system files

Once we started road-testing our Sketch libraries, we quickly ran into change management issues. This early on, we were still making a lot of changes: not only the individual component styles, but even the assembly we’d use to make these Sketch objects more usable and helpful (e.g., building the default padding around a field and label, or including spec overlays for containers like sections and cards, or in nesting components).

At first, we wanted to follow a “grand workflow” that some design teams aspire towards: that our Sketch files would always be 1:1 updated against the live code of our products. Where we could revisit an old Sketch file, let the components to update from our current centralized library, and be able to work from that baseline.

But after this exercise, I can’t even imagine that reality: so many things can change at a component level, especially in sizing and spacing, that Sketch can’t reasonably respond to (at least Sketch in 2019). By updating all of the components in an old file, we’d instead end up with a mishmash of new components forced into a now-outdated layout. Instead of treating an old Sketch file as “an outdated, but at least intentional, artifact,” updating components in old files created unintentional designs that didn’t reflect any design or code reality.

Besides, that implied use case — “revisit an old file and update the components” — wasn’t an important one for our team. We were too busy to care about updating old Sketch files! So, I started versioning our Sketch library files and storing them in the same folder.

And example of a versioned Sketch Library file: this was our “v2”, which had a ton of changes in it.

By defining distinct versions for our library files, we saw some advantages:

  • We (mostly me) had to be more mindful about our changes to the library: we had to note all additions and adjustments in a changelog. This exercise also helped us later communicate changes to our teammates.
  • It allowed us to include aspirational components in the system. We always had a labeled “beta version” that the design team was working on, so that was a great place to collaborate, rather than a live file that was supposed to represent actual coded components.
  • By preserving old versions of the library in the same shared folder, we preserved the linkages between those and their corresponding (older) Sketch project files. No file-breaking updates prompts from Sketch: the old files and libraries were standalone and stable.

Better-looking UI, built faster.

Frankly, this didn’t end up the grand, highly-visible win that we intended: I don’t have an entire suite of Hired products to show you that have clean, updated, consolidated UIs. That isn’t the current reality, and often isn’t for complex products like like Hired’s. But I’m hopeful that we set the foundations for that longer-term win.

Plus, we did start seeing short-term gains. After collaborating with designers and communicating enough about the system, we saw some consolidation in our interfaces; both in our Sketch files, then in production. Designers learned to pull from our Sketch libraries; they learned how to verbally share those decisions with our new vocabulary; they spec’d design files for developers using that shared language (at best, literally using CSS class names).

Getting to those results was, at times, like pulling teeth: but I’ll call this a win any day. Huzzah!

Losses and lessons

Recognize your needs, a.k.a., “what’s a design system?”

“Design system” means different things to different designers: from a simple set of shared components (“just a component library,” to some), to an entire philosophy of design, content, and implementation. I think of Shopify Polaris and Mailchimp’s handful of design guides when I think about a real, robust, full “Design system.”

At Hired, we lacked the bodies to build — much less maintain — a robust design system (the creator of Shopify’s Polaris wrote a great post that highlights the unexpected costs of maintaining a design system). Some of the features of an advanced design system — design guidelines, notes on copywriting tone, complex organism- or page-level components — are great; but they weren’t necessary and we couldn’t afford them.

Our needs were basically a component library, with methods for assembly — e.g., pages made up of sections with content, cards, and other components — a layout grid, and functional modifiers that developers could use to place all of these objects. I don’t know if that’s a “design system,” but it was enough complexity to demand a lot of care and attention.

Have a project lead

In reading about others’ design systems, I often encounter this vision: a grand, de-centralized front-end utopia where everyone can contribute to the design system as they work on their individual projects. Where everyone can share and join an omniscient, organizational mind.

But I haven’t found that to be the case — or necessarily even desirable — in my systems work across a few organizations. In my view, a system is at it’s most systemic when it’s deliberate, organized, and follows a consistent philosophy.

I was designing our system at Hired around atomic components (like form fields and buttons), a hierarchy of containers (pages > sections > cards > modals), and workable defaults: if a developer just put a bunch of these components from a designers’ napkin sketch, that resulting UI would theoretically be passable (i.e., without obvious design bugs). From there, devs used functional modifier classes to adjust component dimensions, spacing, and fine-tune per feedback. It’s not a perfect system, but at least it’s a consistent, functional philosophy.

An example screen showing a simple Web/Growth layout.

Without that central philosophy driven by a lead designer, you can easily end up blending multiple micro-philosophies into a system that lacks cohesion and loses some of its systemic value. I’m not saying that designer collaboration isn’t valuable — the lead-and-feedback loop is crucial in product design! — but unfiltered, decentralized inputs don’t naturally build very efficient systems.

Plus, so much of this project was about organizational communication: having a lead designer and lead developer on this project was critical to making that kind of progress. On the design team alone, having this project anchored on a single designer provided needed clarity (and helped me figure out how to involve folks in meaningful, but efficient, ways).

Beware the “North Star,” but still have a “known checkpoint”

Last year (working on this same system), I wrote a post about employing “evolution,” rather than “revolution,” in building a design system: that is, instead of proposing dramatic change with a “North Star” design goal, take the best of what your organization already does, and make positive incremental changes from there. Start small, be persistent.

I still believe strongly in that “evolution” philosophy, but I’ve softened my position to some extent. In this project, we did start small: we thought that we could make changes to existing production-live components to make quick, visible progress.

It was only a few weeks, though, before we changed our approach. For one, updating individual components one at a time (e.g., consolidating our buttons) is a costly cosmetic exercise. Sure, your buttons all look the same now, but the adjacent (non-updated) patterns are outdated and, now, poorly matched. Basically, we were taking outdated (but at least intentional) screen designs and making these unintentional franken-screens; all with the premise that the system would win out in the end. It did not.

Additionally, moving at such a small scale wasn’t very persuasive when it came to selling the merits of the system (and getting volunteer resources to help develop it). It was hard to convince folks that our obsessive work on buttons was going to give us notable positive outcomes. And by micro-focusing with this incremental approach, we made some early development decisions that we’d have to reverse course on later on. Sigh.

By shifting our approach, we became more successful: we defined a major “checkpoint” that was conservative — “evolution”, after all — but significant and desirable. It demonstrated how this new family of components and modifiers was designed to work together. Showing this as page- or screen-level change was more compelling for our stakeholders.

We also more clearly defined when we’d install the new system: if an individual project was updating enough UI at once (e.g., an entire page), we’d employ the new system. Eventually, each page would be revisited and we’d have a shiny new product. It was still incremental change, but not overly so.

Don’t get stuck in code; designers are your friends!

We started the project with something of a mantra: if a component doesn’t exist in code, it doesn’t exist at all. It made sense at the time… historically we’d been dealing with gaps between design mockups and production builds: “this component doesn’t exist in our style sheets.” So in our new design systems work, we challenged ourselves to prioritize code implementation, even if it meant a slower redesign of new components.

But, we ended up over-focused on code for a while. By being very involved at the code level, I was undervaluing the bigger picture (and was creating a redundancy with my perfectly-capable developer resource). We were also getting too distant from the design team: these were supposed to be some of our strongest evangelists at Hired, but instead they were feeling uninvolved and unaware. The “design team’s voice” wasn’t consistent and strong.

Once we got back to basics and spent more time with the design team, we were able to get back on track. By prioritizing the design process, we restored those communications and figured out how to queue up new component designs for our design system devs.

But… don’t ignore the code either

We also had some shortchanged communication on the other end. It wasn’t enough to simply work with the developers that were closest to us: we needed to get in front of a broader set of teams to understand their varied needs and contexts; those communications didn’t just happen organically once people knew that there was a “design systems project and team.”

A spec’d example of a crucial container component across our org. Getting to a unified solution here was more of a technical challenge than a design one.

A big stumbling block (one that kinda changed everything) was React. Some of our teams started moving towards React.js as the foundation of their frontend work, while other teams needed to retain a CSS (specifically, Sass) implementation. Supporting both of those technical branches was a critical condition for our system — and that dual compatibility became an important criteria when we evaluated off-the-shelf component libraries later on.

There were other tech considerations as well, e.g., compatibility tables for things like CSS Grid and Flexbox; Sass vs Less; Styled Components vs integrated classes in React. The point is: talk to all of your developers (or Engineering Managers, etc.) early and often, be clear about your decisions and rationale, and be careful when hand-waving around unresolved issues… “we’ll figure that out later.”

// *** AN EXAMPLE OF SASS IN OUR CSS BRANCH. Using Sass literally changed the way we thought about these components even in Sketch// Containerssection, .section {
background-color: $color-gray-0;
padding: 8rem 2rem;
}
.section {
&--inner {
max-width: 120rem;
width: 100%;
margin: auto;
}
&--short { padding: 4rem 2rem; }
&--tall { padding: 16rem 2rem; }
&--white { background-color: $color-white; }
&--dark { background-color: $color-black; }
}
.card {
background-color: $color-white;
margin-bottom: 2rem;
// margin-right: 2rem; not needed with grid gaps
padding: 4rem;
&--tight { padding: 2rem; margin-bottom: 1rem; }
&--loose { padding: 6rem; }
}
...

Know when to bring something off the shelf

*Sigh*. Eventually, we did choose to freeze our homegrown system and start developing from an off-the-shelf library. There were lots of factors going into this decision:

  • More of Hired’s product teams were moving onto React. This stressed a bunch of technical dependencies for our Developers, and it affected how we’d build (or build copies of) components for even more teams. This change created a reasonable opportunity to challenge our old design system from the top down.
  • We were constantly under-resourced for design system maintenance. I’d freed up time from developers here and there, and been granted a contract frontend resource; but it wasn’t enough to build out new components, maintain our homegrown system as a whole, and to combat emerging technical needs (see point above).
  • Some off-the-shelf systems did a lot of what we wanted anyway. By pulling a well-tested library off the shelf, we could “fast forward” a boatload of technical progress… albeit while removing some control from Design, and by pulling in perhaps too many components (see my point above about having an opinionated system philosophy).

We defined the criteria with developers across our product teams, selected a set, and the Design team evaluated the last few finalists. We ended up choosing Ant Design due to it’s popularity (well-tested, with pretty good documentation), breadth of components, and its general aesthetic being close enough to what we wanted at Hired.

The rest is history. I left Hired shortly after setting up some process and plumbing for the new “Hired-Ant” library. I was disappointed that we didn’t carry our own system through, but the teams and the entire organization still benefit from the work. I’m proud of it.

--

--

Alex Couch
Alex Couch's portfolio

Product Designer in the SF Bay Area. Music fan, pizza eater, Medium reader. linkedin.com/in/alexcouch/