Imagine a company where design is handled on an ad-hoc basis, resulting in a chaotic array of different UI components with multiple variations and styles. This is not a hypothetical scenario; unfortunately, it was the case at my current employer.
The absence of a well-defined design system can lead to a myriad of challenges, from inconsistent branding to a slow development process. Since I have worked with design systems for personal projects and at previous employers, I'm well aware of how they can improve efficiency, increase code reuse, and limit the risk of performance and accessibility regressions.
Since being promoted to Principal Engineer earlier this year, code reuse, as well as developer efficiency have been points of emphasis.
Sweeping UI changes that span an entire range of products are not a small endeavor, so this will be the first of several posts where I write about the design and development of a greenfield design system.
The Looming Problem
My employer has 5 distinct brands, and some of them are over 40 years old. 4 of the brands started as magazines, all of which are still produced. That is to say that my employer is not a technology company, however, technology is important part of content delivery and the member experience.
The part of the product that I work on is your standard-fare media site with a subscription model and additional revenue from display advertising and other types of sponsorship. The codebase for this part of the business is also aging. The current iteration started in 2015, and there have been thousands of changes by a handful of developers since then. When the sites were first launched, there was a style guide, which is now long out-of-date. Features have been added and removed from the sites, products have been replaced, and needs from the business have evolved. All-in-all, the products are much more complex than they were years ago, and the team who works on the core product has gone from 5 people down to 3.
Without a design system in place, there are many small inconsistencies in the UI that have happened over time.
The Code Sharing Problem
In a (very) small team, every minute counts. Wasting time on redundant design and development efforts can hinder productivity. A design system plays a pivotal role in code reuse, enabling developers to work more efficiently by leveraging pre-defined components and styles. This not only saves time but also maintains consistency across the application. When you have haphazard, ad hoc UIs, there isn't a great way to achieve the type of code reuse that I think we should have as a team.
At my employer, the way development typically works is to launch a feature on one site. Once a feature launches, we collect data, make tweaks based on our learnings, and then deploy it to the other brands by copying files from one project to another. When the files were initially copied, they were identical, but there were other features such as the theme and UI that aren't shared making it a difficult problem to tackle. Since each brand also has different stakeholders on the editorial team, different questions and requests caused the code to diverge over time. We can obviously do better, so I started working on ways to sell the idea of a design system with a common structure to the business.
As Principal Engineer, I view it as my job to surface technology problems that the business is unaware of. I focus on ways to make things more efficient, adding missing measurements or application telemetry, and finding ways to improve velocity. Working at a small company, my colleagues believe that one of our advantages is to be able to move fast and not be mired down by bureaucracy.
To sell a design system to the business, I needed to show how this could improve developer velocity. The real magic usually happens when you can point to others in the same industry who are already doing what you think you should be doing, and indeed, I was able to find public record of documentation of design systems by other media companies like The Guardian, BBC, The NY Times, and The Washington Post.
Selling the Idea
Introducing a design system is no small feat, especially when there's a status quo in place. To champion the cause, I wrote a Request for Comments document (RFC) to outline a potential solution. It showed screenshots of visual regressions that were introduced due to the lack of robust front-end systems, links to our forum where the members had issues with our UI, and specific examples of how having different UIs with varying markup cost us weeks of implementation time.
I mentioned that new features were rolled out from one brand to another by copying code. That means that someone has to go first, and someone has to go last. Usually, it's the same someone who goes first, so there are 4 other brands who would immediately support any effort where they weren't waiting in line for new features. If there was a way to share code among them, new features could be rolled out to all brands simultaneously.
I did some prototyping of how things would work, the RFC got approved, and the idea got traction.
The solution involved treating our own code as if it was an external dependency. Instead of pasting code around, we needed to create packages and bring in code, even our own, with package managers like NPM. In the article about supporting color contrast in design systems, I described how I published scoped packages to AWS CodeArtifact and set up our CI/CD. We already had a relationship with AWS, and I knew the cost was negligible and what little we did have to pay could be rolled into our monthly expenditure with them.
From a product standpoint, it needed to be a more obvious visual impact than isolated Storybook components, so all the design system work was rolled into a "header refresh" project, which is the beginning of the effort to unify the UI for the site codebases.
Our product developer produced designs and pitched the concept. There was a little disagreement, so we decided to run an A/B test to gather more data on the perceived shortcomings of the current design to inform the decisions for the new header.
Once I had the mechanisms for code sharing in place, I began creating packages to manage most of our linting/code quality tools so they could be shared across projects, including:
Design Token Conversion
The first shared packages were all for configuration items that get updated very infrequently. The real productivity goal is to be able to share code that gets updated more frequently, and to do this, we needed to spin down the use of the legacy design tokens that were defined inside each project.
I wanted to actively discourage anyone from adding new variables to the SCSS files that were used on the site, so I removed the variable definitions from each site, set them up inside the shared Style Dictionary project, and then imported that file back into the site projects as a dependency.
Verifying that the conversion was successful was easy. The SCSS file still needed to build without errors, and the output of the CSS file before the conversion should have a clean diff when compared to the compiled CSS file after the changes.
With the shared code quality tools and the style tokens set up, I was ready to turn my attention into building components. We're at a good stopping point here, so I'll write more details about the component development and rollout in another post.