UI components are a deceptively hard problem. It seems just a matter of picking a UI framework or iterating on some design mocks and everything should be solved. Right?
The problem is that most of our solutions to problems come from studying small projects. On small projects, we could get away with having one style sheet, or iterating on top of a UI framework.
In a large organization, with a massive product, this won’t work. Here is a breakdown of what can go wrong:
Lack of Planning and Architecture
The designer creates high fidelity mockups for the product.
The front-end team implements the design into the application.
The application grows in size.
As the application grows, the front-end team creates a library to “share” the code.
Wasted Time and Stagnation
Product requests a change to one of the components, but that component is now on 20–30 pages.
The developers hesitate to make the change in fear that it will “break” somewhere else.
Since teams own different parts of the site, developers don’t even know what could break.
Bottlenecks are created because changes are high risk.
Now Product Owner “A” decides that she doesn’t like a select box, so she has the designer mock up a new select box she likes.
A new one-off component is created that is entered into the code base without the rest of the team knowing about it.
Now there are two select boxes.
That select box change is not propagated across the app.
Now, not only are there bottlenecks, there are design inconsistencies. There is also a lack of team buy-in to the process.
High Risk/Complexity and Spaghetti Code
Now someone submits another design request.
The global style sheet has grown so much in size it’s impossible to find any styles.
The component is referencing a base style that would update many things in an undesirable way.
So style-nestings are created, !important is added and now...
Seriously... the developers are over it, the designer has already quit, and the product owner is still asking why simple changes are so hard to make.
Luckily, there are very smart people who have worked hard to solve these problems. So how do we solve them? Here is a checklist of how to add some structure:
Step 1: Build a Component Library
Even if your component library is referencing a set of open source components, build your own library and version it. Not only that, create wrappers for all the components in your library. That way, if you need to swap something out, you don’t have to change the external API. You only need to modify the implementation details.
The component library should be the core of your application. All your apps should require this library. Instead of writing components in your application and “back porting,” work on the components up front. This will produce better code and a more meaningful application.
The other advantage of the component library is that all developers can isolate and observe changes. Especially if you are managing the code in GitHub and require pull requests before merging.
In a monolith application, it’s easy to slip changes into a feature branch that produces inconsistency. With a component library, people are able to push back on cowboy coding.
Step 2: Split Your Monolithic Front-End Into Smaller Apps
If you have an application with a lot of pages, you should try to move all those pages to their own SPA (Single Page Application). The beauty of this system is as follows. When you make a breaking change to your component library, you can make gradual updates to all your applications. It saves you from having to do “one giant update.”
This reduces the developers' fear of making changes which lead to more work. Teams are able to manage updates on their own schedule. There doesn’t have to be one giant organizational push to update a button.
Step 3: Use CSS Modules to Isolate Component Design
CSS Modules allow for two things. One, they get rid of exposure to global classes. That means when you update a class for a component, you know exactly what will be updated, as the scope of the style is relative to the component.
The second major advantage is that developer knows where all the styles live. They don’t have to worry about styles being spread out all over a set of general set of style sheets.
Step 4: Use an Interactive Style Guide as a Contract Between UX, Product, and Engineering
Any component library should be able to publish an interactive style guide. This is a middle ground for developers, the product team, and designers to meet. It allows you to isolate your components and work on them independently. You can work faster and see changes quicker. The best part is that once your publish the update, it propagates the changes through the entire application.
Now that you know where all your components live, there is no need to deliver high fidelity mockups every time. Designers can focus more on behavior, information design and researching UX. Developers don’t have to worry about design when starting a new feature. They can focus on business logic and complex behaviors.
A potential flow for this approach would be as follows:
A feature request comes through and is broken down into a set of components.
Front-end developers decide if there are new components or changes are needed for the feature.
If not, then feature development can begin.
If new features are needed, the developer creates a feature branch in the component library. The changes are reviewed in an interactive style guide by Product, UX, and other developers.
Once the change or new component has been approved, the branch is moved into the master file.
Depending on the change, a new version of the library is created following semver.
So, remember the story from before? This is a revised version using what we have learned.
- A design for an application is abstracted into a set of components and developed into a component library. This library should be versioned using NPM. Breaking API or possibly major design changes would warrant a major version change.
- Parts of the application are broken into SPAs (Single Page Application) with their own package.json. This allows them to upgrade components in a controlled, risk adverse manner.
- New Components or changes are decided up front before beginning feature development. Product, UX, and engineering can collaborate on component design using an interactive style guide and see a “real” copy of what something will look like on their site. Changes are not feared because if there is risk associated with the change, a developer can use versioning to mitigate the impact. Also, component styles are now isolated using CSS Modules, so a change to component “A” won’t impact component “B.”
- UX can now deliver low fidelity mock-ups that reference components by name. Developers know exactly which components to use and don’t have to “fish” around somewhere in the “shared” code based
- Another redesign? No problem. We’ll create a branch in our component library and update all the styles. When we are ready to release, we’ll create a major version update. Problem solved.
There will still be believers in the “whole app” update.
Breaking up an app into smaller apps may be overkill for the size of some products.
Designing components up front requires a lot of discipline from your team. Not everyone is able to abstract requirements and plan accordingly.
Cowboy coding can still happen. Your library and your process need an owner.
This is not an easy jump to make and it takes buy-in from the whole organization. If your goal is to reduce bottlenecks and sloppy updates while increasing the velocity of your team, then this is the approach for you. If you are still a believer in the “push” button update well... good luck! Don’t be surprised when you see velocity decline and costs go up.
More at https://tech.smartling.com/