In the last 2 years, I have worked on developing and maintaining carwow’s design system. It’s been my first experience of this kind, and something I’ve been very excited about. During this period, our team and product have grown rapidly, especially Engineering going from 10 to 40 developers.
With a team growing fast, it can be challenging to maintain a design system effectively. Keeping everyone in sync and sustaining consistency isn’t easy with a dozen people, so now imagine with four times more collaborators. From this experience, I’ve taken out five principles that have helped to make an impact for our team and product.
1. Communicate a lot
Building a design system should be motivated by the value it offers to users and product teams. Two of these motivations are increasing consistency in the user interface and building new features and screens more efficiently. Don’t Repeat Yourself is an old engineering principle, and component libraries are all about applying this mantra to design and front-end development. Build a UI component once, use it and repeat.
This sounds great on paper, but only really works if you know what components already exist when designing and building new interfaces. How can we make sure designers and engineers don’t waste time building components and patterns we already have?
Like most companies using a design system, we have a documentation website referencing all our common principles, styles and components. We even made it publicly available. However, having information available isn’t enough for everyone to be able to find it. As a frequent contributor to our component library, people often directly ask me whether a style or pattern already exists and where to find it. Ideally, the documentation should fill this role. To improve that, we came up with a few ideas.
Going through a long list of UI components or a complex information tree can be tedious and make the documentation difficult to browse. We fixed this issue by creating a little component finder with autocompletion. We could set one up quickly using a
<datalist> element. This proved to be a popular feature amongst our product team.
Another important issue in communication is how to inform team members of changes and new additions. One idea we had was a bi-weekly newsletter from the front-end engineering team, with all the latest changes shipped since the last email.
The newsletter was also a good way to communicate the outcome of these changes. One example could be how improving a UI pattern can increase user interactions observed through analytics.
After a few months, we stopped using the newsletter and moved to punctual announcements in our company’s collaboration tool. This proved to be less work for the same result. In the end, it doesn’t matter what channels you use, don’t underestimate how communication can help in making your design system a success.
2. Set the right balance between discipline and freedom
As designers and developers, it can be appealing to sometimes throw new solutions at old problems. We get accustomed to working with the same styles and components every day, to the point where they start to look ordinary and boring. New design trends appear elsewhere and everything we have built before suddenly seems so outdated.
On the other side of the spectrum, when working on a design system and advocating for consistency and re-usability, we can easily fall into the trap of becoming the style guide police. Consistency is important, but it shouldn’t be an obstacle to change and experimentation. With sensible motives, a product team should be able to work with the system while exploring new solutions. In these situations, it is important for the team to be conscious of the trade-off it makes. Experimenting and altering an existing component on a per-case basis can result in design debt, which should be treated the same way as technical debt.
Regardless of whether a team decides to be strict or loose with its design system, I think the most important factor is to prioritise value for users and the business. We should question whether we are making meaningful improvements for users or making things look prettier and newer out of vanity.
3. Assume there will be changes, and make them easy to happen
One of the interesting challenges of building UI components within a design system is to make them flexible and agnostic. UI patterns should be decoupled from the context in which they are used. I would even say they should be untied from the purpose of the product it serves.
While carwow is a car-related product, we design our components to be generic and agnostic to the car industry. This gives us more flexibility when using components in different contexts. A card component shouldn’t be tied to a car’s attributes. It should be fine to use it to display an engine, a dealership, or a kettle.
Using Atomic Design has helped us a lot with building modular and agile components. We can implement patterns and interfaces, not just thinking of how we are going to use them now, but also how we could use them in the future. There are also other tools we use to make our styles definitions less rigid. In CSS, BEM has been useful, providing ways to alter and override stylings via modifiers.
4. Don’t be scared of making big, structural changes
When I joined carwow and looked at their existing design system, I identified some areas where the front-end architecture could be improved. For example, media queries were all done desktop-first. Switching to a mobile-first approach would be beneficial: aligning with the workflow of our designers, simplifying our CSS and focusing more on our mobile user experience (which accounts for more than half of our traffic).
For a long time, I was reluctant to make this change. I knew it would take a really long time to complete and wouldn’t be a drastic improvement for users. I chose to stick to desktop-first, mainly for the sake of consistency. I thought having a mixture of desktop-first and mobile-first media queries in the codebase would be confusing for many other developers.
At some point I was poked by a back-end developer, asking me if we would ever build designs mobile-first. This question made me realise that by not initiating a change, we would be stuck with old practices forever. In the following weeks, I started working on a new set of breakpoints so that we could build new features mobile-first. It only took a couple of weeks before we finally had a mobile-first page live on the site.
The next steps were to document how to work with mobile-first media queries, officially deprecate the old approach and present it to the wider engineering team. Today, we have a mixture of both new and old media queries in our codebase as we are slowly migrating. In the end, this hasn’t caused any issue on production and all engineers have embraced the new approach.
We have applied this approach with many other coding practices. One downside is that it may bring a little bit of overhead for new developers in our organisation, when coming across old and new techniques in the codebase. However, this forces us to get better at communicating and documenting change. It also means many areas of our front-end codebase is transient, waiting to be improved. That also encourages us to be diligent in keeping track of deprecations and taking action to clear them.
5. Everything is imperfect, and that’s OK
When I started working on the design system at carwow, I was excited about what we could possibly accomplish. With other design and front-end enthusiasts, we looked at successful libraries such as Rizzo, Polaris and Carbon. These are great examples of well-defined and documented libraries, showing a high level of attention to detail, and covering areas such as user research insights, accessibility, etc.
These examples are inspiring and make us want to build something as good, if not better.
Taking inspiration from successful products and libraries is great, but can also be discouraging. We can end up looking at our own design system with bewilderment, feeling there is so much to do reach the same level.
Many areas or our documentation website could be improved. Our documentation structure isn’t the most intuitive, many of our components are poorly documented, we lack design guidelines, etc. We could spend so much time trying to solve these issues, but our priority is to develop and maintain our product: build new features, improve the user experience, make our website load faster. This is the core of our work and is a lot more meaningful to our business and customers.
How do we balance improving the design system against other aspects of our work? In the end, it is better to simply accept our limitations. This is a tool that should help us work faster, not become a burden based on self-imposed expectations. When time allows it, we add small improvements continuously, always making sure they bring value to us.
If you have any thoughts or more ideas on how to build a great design system, we’d love to know about them! We have many other challenges with ours and will keep sharing the solutions we come up with to tackle them!