- Our Current Problems
- Previous Refactoring Strategies
- The Connection Between Style and Design
- Our Solution
In a startup that has evolved over several years, in which projects are cycled through at a high rate in pursuit of the ones that truly work, you're going to cycle through a lot of code. Successful projects pick up steam and expand, or are short-lived; unsuccessful projects are transformed into newer experiments, are set to "pending", or are abandoned halfway through. With a lot of these projects, style code is either expanded on or created from scratch.
As a new member of such a company or team, especially as a style-focused designer or front-end developer, it's easy to find yourself in a labyrinth of style code that has been piled on layer-by-layer, project-by-project, with little thought to a wider sense of order. In our case, we finally figured enough was enough, and are starting to do something about it.
A disclaimer: this article doesn't claim to have the answers as to how you should clean up or refactor style code - there are countless ways that you could tackle this problem, and this is the one that currently makes the most sense for us. We decided on a process that fits us based on a combination of criteria, including, for example:
- the set up of our team
- the depth and breadth of our codebase
- the cycle-rate of features and projects in development
- the strategies we employed in the past
So as you read this, feel free to comment if you have any alternative solutions, questions or thoughts!
Our Current Problems
In our environment, we face a few different problems surrounding our style code.
1. DRY - Don't Repeat Yourself
Simply put, we repeat our styles A LOT.
Over time, we've accumulated multiple sets of CSS stylesheets, duplicate font/icon sets, multiple external style libraries and frameworks, and added a huge volume of our styling within individual components (through the use of "Atomic design").
Atomic design, by its very nature, makes use of components broken up into atoms, molecules, organisms, templates and pages; building blocks that work both as a whole and as smaller collections of parts.
To take an example from Brad Frost's Atomic Design Methodology:
Often these different parts include styled-components, that add to the layers of styles already in place, requiring even further styling simply by way of needing to cancel out and overwrite unwanted styles.
2. Locating implementations of CSS
Many of our CSS selectors are named in such a way that it's no simple task to trace exactly what they're being used for, and in which components. This is one of the reasons why a lot of style code tended to be added as opposed to updated.
3. We spend too much time on styling
A combination of the two above points means that we spend more time than necessary on making sure styles are functioning properly.
Previous Refactoring Strategies
A few strategies had been implemented in the past in an effort to make our style code more organized and functional, however, they never quite went as expected.
1) Bootstrap: - A long time ago we installed bootstrap into our front-end codebase, but it's barely been used since. - It's also caused a lot of extra styling to be added as an overriding measure.
2) Sass global stylesheet: - This ended up being a humungous CSS file that was very hard to use. - It did nothing to fix the issue of difficult-to-trace styling.
3) Modules of styled-components: - While this made styles easier to trace, it was inefficient because it required importing the styles you needed into every component separately. - It also ended up just being another version of a huge CSS stylesheet.
4) Creating a separate design repository: - A specific design repository was set up in Github, but it was difficult to keep up to date and quickly fell out of sync with our products' designs as they evolved. - This was essentially a view-only repo that was supposed to act as a set of design guidelines, but it completely fell out of use once members came in and out of our team.
In the end, these strategies weren't used as replacements and were piled onto each other, making the style codebase that much harder to navigate.
The Connection Between Style and Design
For a long time, we didn't have a dedicated design team. Design wasn't worked into the workflow besides the initial push to create version 1.0 of the product, so - in terms of design - most requirements simply boiled down to accomplishing business objectives. Once designers were brought back in and a new design team was set up, systems around it naturally also began to take shape.
This is important if you consider that one of the reasons behind the constant addition of styling lies in the design strategy that comes before the code. While the company has had phases of adhering to design guidelines, without it being enforced on a design team itself, developers are at a disadvantage. New features call for new designs, and new designs call for additional components or variations of existing components that were not considered until then.
Unless you're working in a "waterfall" style of product management with a meticulously detailed roadmap that you're able to stick with to the letter for two years, this limited planning is unavoidable to a certain degree. It can be very difficult to predict in month 1 what kind of designs you'll be making in month 24.
So, at a certain point, designers need to start thinking ahead with more clarity on the burden placed on developers. This is where a design system can make a big difference. By taking into account a platform's design as a whole, and considering possible dynamic variations of elements, you're able to come up with patterns that help developers create more efficient style code.
This is not to say that you need to try to predict exactly what month 24 of your roadmap looks like - this is more of a shift in design thinking: in creating designs that are malleable and can change over time.
For example, say you have a dropdown design. Instead of creating just a single version, consider what kind of design allows for the most variation:
- Additional icons
- Search fields
- Single-select vs multi-select
A developer need not create every single variant of this component from the get-go - it might be that your feature requires the simplest version of this. But they might start to think:
How can I make this component so I don't have to do it all over again next time?
Though this may add a little bit of time to initial development, in the long run, it's a game-changer because it gives developers a bird's eye view of all the components and all their possible states. By presenting each widget systematically, engineers are more likely to develop the components systematically, rather than in an ad-hoc fashion. This leads to more reusable and easier to understand code, as well as a more cohesive UI though out the app.
I mentioned design systems up above, but what does that really mean?
"A design system offers a library of visual style, components, and other concerns documented and released by an individual, team or community as code and design tools so that adopting products can be more efficient and cohesive."
And what does that mean? Well, it can mean a number of things, ranging from simple UI Kits and frameworks such as Bootstrap, to a vast system of guidelines, documentation and tutorials (think Apple's HIG, or Google's Material Design).
In our case, our solution builds on a combination of two types of design systems. The hope is that using these systems and tools in tandem allows for better cooperation between teams, as well as simpler designs and code:
A designer-facing UI Kit, created on Figma
- Designs can be iterated along with business requirements
- Figma becomes a catalog of what is necessary to realize the product's vision
A developer-facing UI/Component Library (Storybook)
- Storybook becomes a catalog of all the actual React components developers need to implement the designs in code
While we've made use of Storybook in the past, it's been at a very basic level that has come as more of an afterthought, rather than as a marker or guidepost for development.
Organising Our Codebase
Using Storybook means, inevitably, that there will be a heavier reliance on styled-components. However, this also gives us the opportunity to severely cut down on our reliance on CSS stylesheets and Bootstrap libraries.
One step we've already taken is to do a full audit of our CSS stylesheets, identifying where styles were used and/or duplicated, and as far as possible removed styling that was unnecessary or not in use at all.
This was a behemoth, tedious task. It could well have been helped along by using third-party software, but after researching how they might be used and how effective they were, it seemed to make more sense at the time to make the effort of going through each stylesheet one at a time, if only just to get to grips with the style code to a much more intimate level.
It was dirty work, but now that it's been done, we've reached a point where we can more clearly think about the work that's required on our catalog of components.
We wanted to simplify our style code by trying to eventually phase out the multiple methods of adding styles. Rather than having a mix of styles across JS and CSS stylesheets, we plan to phase out one of those so there is just one source of truth: the design system.
In addition to this source of truth, our plan utilizes one source of implementation: styles in JS with Storybook giving us feedback as to whether or not we're abiding by the design system.
Storybook as a platform gives us a lot of support that will allow us to use it as a developer-facing design system. Add-ons that can link components to their Figma counterparts for visual referencing, or markdown notes that can be used to create more illustrative documentation for each component.
It goes beyond creating a list of components, it becomes a system that helps us identify how these components function and in what context you might expect to see them.
What we hope to achieve at the end of all this is a system of style code that lets us both design and develop features and projects more efficiently, and that provides us with more clarity on the usage of styling within our codebase. As our team continues to grow, the transfer of knowledge between team members becomes more and more essential, and having code that's easy to understand and explain makes that task a whole easier.
The set up of this kind of system and process may seem a monumental task, but as I mentioned earlier: in the long run it's a game-changer, which is exactly what we're after.