Rollup: What we have learned from sharing UI code at AdRoll
This is the third post in a series of three blog posts about Rollup, AdRoll’s UI component library. This post covers what we learned from building a UI component library. For details on why we built the UI component library see the first post in the series and for how we built it see the second post.
At end of Rollup component and developer tool development, we reflected on what we did and realized that we had learned four important lessons from the work:
- Making reusable components for app developers is hard
- Making reusable components for contributors is hard
- Interfaces are hard
- Making everything look the same is hard
Let’s dig into each one separately.
Making reusable components for app developers is hard
When we first started working on Rollup, we wanted to make sure that individual components would be easy to use for any developer on any team. For app developers, we wanted them to be able to use Rollup components even if:
- they’re new to JavaScript
- they don’t use React in their application
- they don’t have fancy build tools, e.g. Gulp or Browserify
- they want to use the same version of a component forever
- we never thought about their use case
To address the first three points, each published component has a CDN build. The CDN build, among other things, contains transpiled code. The transpiled JS means that other teams do not need to have a fancy build tool to use a component in their project and that app developers do not need to use JSX in order to add the components to their application.
Not only does this mean that components can be used in projects not built in React, but also makes it easier to use for those who are new to JavaScript. The CDN build accomplishes this by allowing them to use whichever framework they feel most comfortable with (e.g. jQuery or plain JavaScript).
To allow app developers to use the same version of a component forever, components are published to npm
according to SemVer and the CDN assets are pushed to S3 under a versioned URL. Other applications using npm
can leverage npm
’s dependency management to use the same version forever. While other app developers loading components via the CDN can always load the same version through the versioned URL.
We also want app developers to be able to use the components even if we had not thought about their use case. We can accomplish this in two ways. The first way is to provide well defined and general interfaces for the components (see interfaces are hard for more details).
The second way we want to accomplish is through open communication channels. Even if interfaces are well-defined and general, we want to be able to customize and iterate on them quickly. In order to accomplish this we have created a Slack channel, #frontend_helpdesk, dedicated to frontend and Rollup help. We track all bugs and feature requests received there using GitHub issues.
By keeping the lines of communications open and judiciously tracking feature requests and bug fixes, we aim to quickly address new use cases. The issue tracking allows us to track and implement the needed fixes ourselves, and serves as a place where we can foster further conversation on how to support a new use case.
Making reusable components for contributors is hard
The developer tools we put in place surrounding the components aim to make it easier to contribute to the component library. The goal of the tooling was to enable Rollup contributors to:
- focus on the important things when developing a component
- focus on the important things when reviewing a component
Focus on the important things when developing a component
To develop and publish a new component, contributors should not have to worry about boilerplate like build configuration and file structure. To that end, we built a Rollup component generator using Yeoman. The generator takes the name of the component and then creates all files needed for a blank component. After running the generator, contributors can just hit the ground running.
Each component has an examples/ directory and a Gulp task for watching the changes made to the component and reloading the example in the browser. This makes it easy for contributors to test the functionality that they are working on in an isolated development and removes the hassle of having to set up a test application to interact with the component.
Focus on the important things when reviewing a component
We also want reviewers of pull requests in the Rollup repo to be able to focus on the important things when looking over someone else’s work. To that end, we have set up a global linter and Jest tests. The linting and test suite are automatically run on every single PR by Jenkins. The reviewer no longer needs to remember to do these checks, since it is done for them.
Instead, the reviewer can focus on the code quality and changes to component interfaces. In addition and thanks to each component’s examples/
directory, reviewers can also interact with the component and verify functionality, just like the component’s author(s) can.
Interfaces are hard
When starting work on Rollup and trying to use these components in other applications, we quickly realized that it mattered how easy it was to integrate a component. Enter the interface. Here are some of the best practices we learned while building them:
- Limit the number of files that need to be included to get a component to work. One JS and one SCSS file is ideal.
- Make sure component and React prop names make sense. For example, a click handler should have the word
click
in it somewhere. prop
definitions that allow develops to achieve the most types of interactions with the least number of props.
The first point hopes to limit the pain for app developers integrating components into their applications.
The second and third points together form the essence of what we learned when designing component interfaces. The idea behind these two points is to encourage contributors to limit the number of React props each component needs to be minimally functional, but be careful to define each prop in such a way that also supports more complicated use cases at the same time.
To help explain what we mean by that, let’s take a look at the columns
prop
for our data table component:
Let’s take a look at the accessor
and render
attributes that each column can have. In addition to the columns
prop
, the table also expects an array of data
. The accessor
in the columns
prop
is a function, that when given a data item will return a value that is then fed to the render
function for that same column.
Together, the accessor
and render
support the simple use case of simply displaying information for a given data item without the data table component having to know the internal organization of each item.
However, because the accessor
and render
attributes are functions, they also support more complicated use cases. For example, these two attributes on a given column can be used to render line charts that summarize data for a particular data item. To do that, the accessor
could return an array of data points, while the render
can return anything as long as React can render it. Allowing the app developer using the component to return JSX or a React element for the line chart. For example:
In the above example, the accessor
and render
functions are general enough to support straightforward uses of the data table and more complicated ones as well.
Making everything look the same is hard
Making things look the same across products and components developed in isolation is not an easy feat. For example, the blue used in the date picker needs to be the same blue that’s used in our data table. As another example, the top navbar needs to be pixel perfect across products, so that the end user feels like they are navigating within the same application, even if that’s not how the product is implemented.
To solve this problem, we came up with a Rollup component that maintains styles shared by applications and components. This component is called ar-style-base
, and all common styles are built into it:
- Common colors are built in as Sass variables
- Common icons and typography are also defined for components and different products
- We also include bootstrap customizations in
ar-style-base
since many of our UI components use React Bootstrap
Even though the base styles can be overridden, they provide a good starting point for new components and products that need to share colors, typography, and iconography.
So what’s next?
We continue to encourage the adoption of our components at AdRoll and share the things we have learned. Not only will sharing internally the things we’ve learned help grow our contributor pool, but it will also help grow the frontend expertise of the company as a whole.
As we mentioned at the end of the first blog post in this series, we decided to build our own components since we were not happy with open-source solutions. So, the next big thing we have in mind for Rollup is to open source them one at a time.
Thanks for reading this series and we hope you have enjoyed what we have to share about Rollup!