How using component-based design helps us build faster

By and
Tuesday, 16 July 2019

To understand the impact of a component-based design and how it enables us to develop faster on the Twitter web codebase, we’ll need to take a step back. Not too long ago when you opened up Twitter it was a lot more “square”, from borders to buttons to avatars. When it came time to do a design refresh in 2017, we needed to coordinate the change across all of our clients: the Android and iOS apps, the desktop website, and the mobile website.

Updating the design of the legacy desktop website took many engineers two months to complete. One particularly painful change probably sounds like it should have been quite trivial: rounding the edges of buttons. But because the legacy website had no universal button component and many different variations even within re-used button code, this involved manually updating and visually verifying 442 different instances of buttons.

This post is unavailable
This post is unavailable.

In contrast, the redesign on the new Twitter web codebase took far fewer engineers only two weeks. For rounding buttons, only the core button component needed to be updated and those changes propagated to the rest of the app, since every button instance used a standard component rather than defining a custom implementation.

With that context, let’s talk a bit about the benefits of components more broadly. You probably know what a component is: a self-contained implementation of a piece of an app’s UI (button is one, username is another). Our new React-based web app is built from more than a hundred components, many of which are themselves created using simpler building block components.

Extracting out UI presentation and interaction logic into an isolated component makes it easier to reuse, modify, and test. It also provides an opportunity to be intentional about its API (more on that later). For additional encapsulation we define each component’s styles in JavaScript within that component (we rely on the StyleSheet API from react-native-web), which means no bleeding of CSS class styles or need to be careful with import order so that styles are not improperly overridden--a frequent source of headaches on our old codebase.

The web app takes this a step further and has the concept of a stylesheet theme, which components use as the source of truth for design constants like colors. These themes can then be swapped out to easily toggle between different modes. Today, this is what we do for switching to “dark” and “lights out” modes.

Using a component-based design provides the opportunity to create a shared vocabulary between design and engineering that can be used to speed product delivery and improve user experience consistency. We’ve accomplished this by building our components in a standalone library, which is documented using Storybook. The docs can be accessed internally by designers and developers to see which components are available for use, what they are called, what they can do, and how they behave. This means that when a designer is describing a new screen, they can hand the developer a wireframe that references various components used by the screen, and both know exactly what it describes. 

This post is unavailable
This post is unavailable.

With component-based design, development becomes an act of composition, rather than constantly reinventing the wheel. Using shared components as building blocks frees us up to focus on what matters, without getting bogged down in implementation details.

All of the components used in the Edit List screen already exist in our library, which means there was no need to create any new components to build this screen. With component-based design, development becomes an act of composition, rather than constantly reinventing the wheel. Using shared components as building blocks frees us up to focus on what matters, without getting bogged down in implementation details.

Extracting our core components into the component library also means they can be reused outside of the web app, so that other Twitter teams working on web can leverage our work. Components as simple as “text” or as complex as our emoji picker are available for any internal team to use. The rich toolbox of available components has made the Twitter web app the development platform of choice during company-wide hack weeks.

But component-based design and component libraries are only as good as the principles that drive them. Much like the universe itself, as client code matures and grows, there is a natural inclination towards entropy. We like to call this the “why don’t we just...” fallacy. “Why don’t we just add a special prop?” “Why don’t we just fork that component with some tweaks?” “Why don’t we just make a one-off component for this custom UI logic?”

None of those are inherently wrong in isolation. Sometimes it makes sense to expand a component’s API or for special UI logic unique to a particular area of the code, like Direct Messages, to be implemented with the screen itself. Where it becomes a problem is when those extra fields and forked/one-off components build up over time at the cost of consistency and maintainability.

For that reason, we encourage designers and developers alike to push back against that inclination and to simplify whenever possible, even at the cost of flexibility. To give an example, at one point, the button component’s API had become highly customizable to a fault. Developers could specify the label color, background color, and border color independently, which made for a very large set of possible button looks. One of our principles is to make opinionated components that support visual consistency so in this case we opted to distill those three options into a single choice, a type field that only allows choosing from a predefined set of valid button appearances.

This post is unavailable
This post is unavailable.

We strive to maintain a high standard of quality for code extracted to our component library. Components must support internationalization, look correct in RTL languages, and be accessible to screen readers out of the box. They should also have fully fleshed-out documentation and tests and be typed. They must be intentional about their API. “Are all of the customization options necessary?” “Can we consolidate the variants to make it more opinionated and easy to use?” Asking these questions early saves headaches later, and it means we can use a component with the confidence that it “does the right thing” out of the box, even for tough edge cases like RTL and screen reader support.

Adhering to these principles in our component-based design system has allowed us to develop and iterate quickly and with confidence from the early design phases through implementation. Building web at Twitter is easier to learn than ever, allowing us to ramp up new team members fast.

And if we ever have to make avatars and buttons square again, that should hopefully only take one engineer one day….

This post is unavailable
This post is unavailable.