00:00:11
Hello, thank you for coming today. I'm going to share the story of how the team at Betterment secretly swapped out the entire front end for several of our apps last year in order to launch our new brand identity. My name is Chris LoPresto, a lead front-end architect at Betterment, and I love helping engineering and design teams find ways to work better together. You can find me at Chris LoPresto on GitHub or Twitter.
00:00:44
Last year, Betterment spent six months creating a new brand identity. This meant reimagining the company's mission statement, photography style, logo, color palette, and the entire user interface of our web and mobile apps, online calculators, emails—everything had to change. So, from start to launch, the entire project took six months. It was during the last two-month stretch when we built everything.
00:01:08
Now, this was an aggressive timeline that we imposed on ourselves. The agency said to expect at least 12 months for the brand exercise, but we said, 'How about half that?' We aimed for a Betterment birthday launch at the end of May. We figured this would be a great time because it would be after tax season, so no one would freak out if their tax forms suddenly looked different. It would also be before summer, as no one reads press releases from the beach.
00:01:32
As it happened, we delayed it a little bit. In March, this is what our homepage looked like, but by May, we wanted to completely redesign it with new colors, photography, and new content throughout all of betterment.com. We wanted to apply this new brand identity to our retail app, so this is what our summary page looked like in March, but by May, it needed new navigation and a new wordmark at the top.
00:01:50
Everything had to be tighter, and it was to be laid out on a grid system. Investing involves risk, and past performance does not guarantee future results. But it does look like the user is about to have a really good two months, so we had that going for us.
00:02:08
This is what our portfolio page looked like. Basically, the project requirements were clear: if you see that over there, it can't look like that anymore. If this seems wildly impractical, buckle up! Our mission was to redesign everything in eight weeks— and in secret. We wanted this big reveal where all of our apps would change at once, articles would appear online, and then we would bask in the beauty of our newly designed brand.
00:02:31
Then we thought, since we already have to touch everything anyway, why not make everything responsive? You know, it never waits for the crisis, so we needed to make things work on all sizes.
00:02:54
These designs were never going to work at smaller sizes. It was going to take a lot of work to get from that to this. We had to reconstruct all our CSS and HTML by May. You're probably thinking, 'It will be fine. We'll just whip out our trusty feature flag framework and our design system and get to work.' It is helpful if you already have one, which we do.
00:03:10
If you don't, no worries. Hopefully, this story piques your interest. Big projects are risky, partly because they change a lot of code. One of the best ways to cope with this risk is by slicing your work into tiny pieces and shipping them one at a time. This means sometimes shipping incomplete work, but that’s okay.
00:03:28
You just hide it behind a feature flag and wait until it’s ready. Once it is ready, you flip the switch and turn it on.
00:03:44
A huge advantage of this approach is that you can solve problems one at a time when they pop up. You can see what's working, what's not, and change your mind about things without confusing your users along the way. But a rebrand is so big that it touches everything—how do you put everything behind a feature flag?
00:04:12
At first, we didn't know how, but our engineering principles dictated we had to find a way. The only way to have a worry-free launch day was to not ship any code on that day. Instead, we'd have to get to a point that we were happy with, flip the switch, and turn it on.
00:04:33
Betterment's feature flag framework is called TestTrack. It follows visitors and users across devices. We have clients for Rails and JavaScript and all their mobile apps, and we use it all the time to run experiments and dark deploy features.
00:04:53
If you don't yet use feature toggles in your workflow, you should definitely consider it. It's a game changer. There are a few gems out there that make it easy to get started, but slicing your work doesn’t do much good if each slice ruins the last one, which is often the case when CSS is involved.
00:05:07
So, for this project, especially, we needed to assemble new interfaces on the fly across multiple apps to see what worked and what didn't. The best way—actually, the only way—to do this efficiently and creatively is through a component-oriented design system, which would give us reusable components.
00:05:33
Now, Betterment's design system is called Style Closet. It lets us share patterns and components of Betterment's look and feel across all our apps. If you don't have a design system, don’t worry; we had to rewrite ours entirely as part of this project.
00:05:50
But the important thing to remember is that both TestTrack and Style Closet were already integrated into all of our apps. Otherwise, there is no way that this eight-week project timeline would have been remotely possible. Investing in our systems over the years put us in a good position such that what once would have been deemed wildly impractical actually became the basis of our project plan.
00:06:12
However, we didn't know what that meant at the beginning of the project. We were just pondering, 'How in the world are we going to do this?' Forget Gantt charts and press releases; we just needed a halfway believable way to get this out the door.
00:06:31
We asked ourselves, 'How can we get from here to there at all?' To this, the engineers said, 'I don’t know,' but they also said, 'Just do something. It doesn't matter if your prototype is dumb as long as it's concretely dumb.' This is why I like to ask myself, 'What's the dumbest thing that could possibly work?' Well, here we go.
00:06:55
We had already shipped a design system into each app, so I thought, 'What if we create a rebranded version of the design system?' Each app already has a Sass manifest—what if we made a second one that used the rebranded system behind a feature flag? Finally, we knew that the new responsive designs needed different markup, so what if we had some view logic that understands the feature flag and knows how to keep our world separate—the stuff that's in production and all the new rebrand stuff?
00:07:20
It seemed reasonable enough, so we got to work. I spent about one minute writing the beautiful rebrand CSS you see here. I wanted something garish, so I figured, 'White on blue ought to do!' I also wrote some JavaScript to toggle these styles on in our design system documentation site.
00:07:39
So with the old styles, the typography demo looked like this, but with the new, vastly improved styles, it looked like that. At this point, I thought I had totally nailed it.
00:08:00
I started going to designers one by one, telling them I had started the rebrand project so they'd get really excited. They would ask to see it, and I would show them this. The looks on their faces varied, but we had what we needed: we had a way to do new style things without messing up our old styles, and we shipped this to production.
00:08:27
We actually cut a minor version release of Style Closet. The next step was to try to get this new stuff into an app behind the feature flag. So, we upgraded retail to the latest version of Style Closet and made a new rebrand Sass manifest that pulled in the rebranded system.
00:08:44
This is what we wanted to put behind our feature flag, so in our application layout, we added logic to use the new stylesheet if the TestTrack feature flag 'rebrand enabled' was on. In public, no one was going to see this nonsense; this feature flag was initially off for all users, but engineers working on the project—or anyone else keeping tabs—could use our TestTrack Chrome extension to flip 'rebrand enabled' to true and get the new styles.
00:09:08
We shipped this to production on day one of the project. On launch day, all we had to do was log in to our TestTrack admin site and flip this 'rebrand enabled' flag to true for everybody to go live with the new brand.
00:09:25
Now, our designers, being a serious bunch, had me tone down my colors a bit, but things were holding together so far. For our last trailblazing trick, we proceeded to juggle old and new markup at the same time. I ported the CSS for a few of our cleaner, more modern components—the header, progress bar, and footer that you see here. The next step was to make the demo page work for both the old and new worlds.
00:09:46
That's when things started to get a little weird. Right away, we had CSS selectors that referenced color, and the colors had changed, so we already needed view logic for a pretty silly reason. There were much bigger markup changes coming, starting from this SVG logo and the grid system, but getting hacky and hand-wavy, things were holding together pretty well so far.
00:10:13
So, we decided to push ahead and see if this would work in a real-world situation. An hour later, we had an answer: it did! Not even on the simple pages. Like most Rails apps, we used layouts to share page structure and partials to share chunks of markup, so even a hack this far required a lot of logic in a lot of places.
00:10:30
And so far, this was just a reskin; the actual redesign was going to rearrange all of this content. I found myself gazing at this ginormous SVG, no longer so sure we'd be able to share one set of markup.
00:10:50
Let's recap: we wanted to ship a rebranded design system, and we did it. There was a lot of work to do, but we were off to the races. We wanted to use this new design system in our apps behind a feature flag, and then we did it. You might be thinking, 'Oh, you already did that.' But as for the view logic necessary to set up the new brand? Nope, way too complicated; not going to work.
00:11:26
But the good news is, we’re transitioning out of that terrifying feeling at the start of a project when all we know is there's a bunch of things we don't know, followed by a bunch of things we don’t know we don’t know. This pile of unknowns is best faced head-on. We struck out and blazed a trail; we’d make sure one way or another that we could get from wherever we are to wherever we need to be.
00:11:43
So, we have our styles worked out, but the tangled view logic showed us that what we really need is a separate view layer for this feature flag. Why? Because all our problems will go away if we can just add rebranded versions of views without touching the existing ones and have a bit of logic that just determines which one to show.
00:12:03
Now, this is a specific requirement for Rails, which has a pretty great answer built right in: Action Pack variants. So whether you realize it or not, we’re all familiar with the Rails template resolver. When a controller is rendering a request, it’s the resolver that knows where to find the right view on the file system, like 'controller path/show.html.erb.' What I didn’t know was that after that HTML format, the resolver checks for an optional variant that you can define.
00:12:30
Let’s say that you have a website that serves colorful, high-res pictures of robots. One day, you suddenly realize that you have a lot of traffic from experimental Kindle browsers that can’t see color. You decide to optimize the images for their black-and-white existence.
00:12:58
So, the controller can do a browser check and say, 'Hey, there’s a Kindle making this request. I’m going to set request.variant equal to Kindle.' That way, the resolver will first look for a Kindle template. If it doesn’t find it, it will fall back to the plain HTML.erb it’s been serving to the rest of us all along. This is built right into Rails.
00:13:14
Luckily, when I said out loud what we really need is a separate view layer for this feature flag, a coworker of mine matter-of-factly replied, 'Well, why not use a variant?' All we had to do was move this rebrand enable check from our application layout into our application controller. A few lines of code, and we were in business.
00:13:41
We realized we wouldn’t have to make changes to the existing views for the entire project. Even better, we could just delete them after we launched their replacements. We were excited! It was additive, and therefore completely safe to just make new rebranded versions of everything. We couldn't break anything, even if we tried.
00:13:56
Well, challenge accepted. Obviously, we could—obviously we did. But our bugs ended up being real bugs pertaining to real features, not project config gotchas. So, our focus shifted to all the stuff that needed changing.
00:14:11
We audited our design system and all of our apps and came up with a long list of pages and components that needed to either be reworked or rebuilt entirely. As we thought about how long it would take, something had to give.
00:14:29
Style Closet began life as a way to share patterns across our apps, but it was tough going. The apps weren’t built with this system in mind, and our components never just worked when you dropped them in. There was always a fight.
00:14:53
Perhaps we all had these complicated CSS codebases that few braved and even fewer loved. I included Captain Sully Sullenberger on this slide to assure us it was all going to be okay. But the fact of the matter is, as this peer-reviewed study indicates, CSS is hard.
00:15:11
It gets harder as the team grows and as the codebase grows. Our old system suffered all the usual pitfalls.
00:15:30
Yes, CSS is globally scoped. You introduce a new thing, and something else breaks. So, we compensated by scoping our rules more strictly in a show of strength that doesn't stop anyone else from making their change more important than yours.
00:15:45
Perhaps most insidiously, in reality, you have to use these so-called reusable components at your own risk.
00:16:06
Now, swapping out our entire view layer represented an opportunity to start fresh with new CSS architecture. So, we decided to rewrite our design system. This wasn't entirely out of the blue; we had been working toward this.
00:16:28
We had internal prototypes that drew inspiration from numerous open-source projects and untold numbers of Medium articles. We felt confident enough to make the leap because we believed we could move faster with the new system than continue fighting the old one.
00:16:47
We put together a wishlist: sensible defaults, zero config for an app to get going with the system, default element styles so that semantic markup automatically looked good, and cohesive systems for spacing, typography, grid, breakpoints, and color.
00:17:08
We also wanted the ability to apply these system styles consistently in either Sass or through utility classes in our markup, as well as a CSS naming convention similar to BEM, so our components could defend against inward and outward bleed to achieve true reuse.
00:18:02
Let's dive in. We built a lot—we're going to move through it quickly. We created an exponential spacing scale that increments in multiples of four and used it to create spatial relationships within our components throughout all our layouts.
00:18:25
Every space you perceive could be applied using this spacing function, which was made in a unit-tested Sass function. If you were assembling page markup in an app, we provided corresponding utility classes built with those same Sass functions.
00:18:43
So to apply the same padding, you could use a utility class selector without needing to write CSS in the app. We designed a new color palette with a conventional naming scheme so we had functional access in Sass.
00:19:02
We created color and background color utility classes, with five primary colors for secondary variants of each. In Betterment's complex data visualizations, we created a gradient system with hundreds of color stops for each primary color.
00:19:20
Our previous responsive efforts relied on an ad hoc set of max-width media queries, so we took the opportunity to research and standardize on a much simpler set of min-width media queries. Constraining this set of breakpoints allowed us to create responsive versions of each utility class.
00:19:36
We defined default padding, loosened it for larger viewports, and still didn’t have to write additional CSS in the app. We created a linear scale of typography sizes based on the spacing scale, optimizing font-size and line-height combinations for our new font.
00:19:55
We adjusted line heights and header font sizes responsively, maintaining our goal for semantic markup to look good by default. We created a vertical rhythm system that applies bottom margins to typographic elements, again based on the spacing scale, adjusted responsively.
00:20:13
We built a 12-column grid based on Flexbox with fixed-width gutters, adjusted responsively.
00:20:26
Inspired by the Dropbox paper team, we designed a new icon set in Sketch on conventional art boards. We wrote a script that uses Sketch tools to export SVG files, optimize them, generate view partials, and a corresponding Rails helper.
00:20:44
This means designers can change or add icons by updating Sketch and just opening a pull request, while engineers can use the SC icon helper to pop them onto a page.
00:21:01
We also built an interactive icon builder in our design system documentation site so that engineers can generate the EOB for the icon they’re looking for.
00:21:18
We built all the traditional components—design system classics such as cards—but for the first time, these components were truly reusable. We could drop them in anywhere, and they'd all play nicely with each other, thanks to our CSS naming conventions.
00:21:39
We also built layout components with built-in content slots that served a dual purpose. They made it easy for our teams to start building responsive pages, doing the heavy lifting of taking stacked content slides and putting them on the grid at larger viewport sizes.
00:21:56
But they also served as examples for our teams on how to build these components with our new patterns. We created visualization tools so we could see the underlying content slots and grids.
00:22:10
These are browser screenshots of betterment.com, visualized with our Style Closet Chrome extension. As we built each new piece, we used the visualization tool to check our work.
00:22:31
Suffice it to say, it was an action-packed, productive few weeks with unprecedented levels of collaboration between our engineering and design teams. For the first time, we had designs expressed by a system built with a system, and as teams began to use that system, we immediately noticed a significant change in the pace of our UI development.
00:22:51
Pages that used to take a day would spring to life in about an hour. Engineers finished their demos saying, 'I didn’t write any CSS, and it’s responsive!'
00:23:06
I don't want to paint too rosy a picture; there was a learning curve, and there was a lot of work. But at this point, we knew we had made all the right strategic choices. The only remaining question was, 'Can we hit our deadline? When can we launch the new brand to the world and return to our regularly scheduled programming?'
00:23:24
A fixed date and a fixed scope are not a winning combination. We were stressed because we didn't seem to have a way to punt on anything. A brand isn’t something you can turn partway on; we needed all of our pages to be in on the action.
00:23:43
We had two levers we could pull: the date or the scope. We didn't want to shift the date, and we didn't know how to shift the scope. So, we were stuck.
00:23:59
To lift our spirits, we took a look at everything we had already accomplished. We had used view variants to great effect, and we now had a secret rebrand mode that internal employees were testing behind the feature flag.
00:24:12
While reliving the insight that led to this, we had a moment of clarity or déjà vu, where we realized we had done the exact same thing again and made a lighter-weight reskin mode.
00:24:31
With just a few tweaks to our variant logic, we wired up a switch for teams to flip on to reskin a page. With a single line of code, you’d get a new variant, meaning new colors, new fonts, and some breathing room. It wouldn’t be the end of the world if a page or section wasn’t quite done by the launch deadline.
00:24:48
We could get on-brand enough in the meantime without having to completely solve every last technical problem, and it yielded surprisingly good results. We even chose to leave some of the deprecated systems reskinned permanently until their ultimate demise and deletion from our code base.
00:25:09
But most importantly, we had decoupled done from launched. Constraint really is a wonderful thing.
00:25:25
We were debriefing after the launch, pondering how we would have arrived at these results with normal iterative processes, and we weren't sure we would have done so, especially not as creatively and efficiently.
00:25:43
We shipped more code full of better ideas more quickly than we thought possible, eliminating far more debt than we created.
00:26:03
On launch day, some of us might have been so relaxed that we may or may not have been sound asleep when we flipped that feature flag. Constraints improved our plan and improved our results quantifiably.
00:26:23
There are many reasons to be careful when intensifying effort around project deadlines. By definition, you're subverting the notion of sustainable development pace, and it’s a fantastic way to turn working software into buggy software.
00:26:40
We analyzed pull request statistics during and after the project. Because we were doubling down on the design system, the Style Closet repository saw the most dramatic spike with 247 pull requests.
00:27:05
For perspective, this represented 49 percent of the Style Closet pull requests ever. The first half had been spread over two and a half years, not 54 days. We shipped 44 releases of Style Closet, which means we were releasing every workday.
00:27:28
Then we launched and were immediately back to normal.
00:27:41
Even accounting for recoil and recuperation, there reflects a lack of panicky show-stopping bugs. With veteran.com, you can see a calm before the storm.
00:27:59
We knew all the content was on its way out the door, then you see this huge spike because we built an entirely new WordPress theme with our new design system, and then we turned it on at launch.
00:28:18
Again, we were immediately back to normal.
00:28:28
Retail app is part of a repository that contains other apps, so there's not as clear of a signal here. There’s a delayed spike, partially because we were waiting for the design system, and partially because there was a bit of a rush to make the deadline, but then we’re immediately back below normal.
00:28:41
We told ourselves it’s probably easier to work in the new world, and it was now summer, but we were thrilled. This is what we had hoped to see.
00:29:04
Now let’s take a look at all this CSS that we wrote. CSS rules are assigned a numeric specificity: highest score wins, and in the case of a tie, the last one wins.
00:29:16
Harry Roberts of CSS wizardry discussed this sort of experimental notion of a specificity chart: on the x-axis you plot rules based on their order in the cascade, and on the y-axis you plot their specificity.
00:29:39
Loosely speaking, this line should trend upward: if you write a dodgy rule, some poorer unfortunate rule that comes after it will have to fight back, showing up as a jagged graph indicating developer pain.
00:29:55
On the left, we have Style Closet 1—some of our better CSS—not great, but not bad. On the right, we have Style Closet 2, which is much better, smoother, and has much lower specificity.
00:30:12
It’s really fun to see the graph features. You can see how our CSS reset is flat, and the first bump is a visualization tool. Then we see this tangle of old form styles that we didn’t have time to rewrite.
00:30:27
At the end, you see a bunch of our utility classes. These are important by design, so our apps can freely use them in their markup. This same analysis is much cooler in retail.
00:30:45
On the left is the old CSS that was not fun to work with, and on the right, wait a second, that’s almost entirely Style Closet. The in-app CEO says this is what we expected— it’s all the way over on the right.
00:31:05
The system-to-app ratio is fantastic. It means dramatically less work in the app to build the features with the new system. That’s quality!
00:31:24
Let’s look at quantity. Way down: retail's CSS code base went from about 10,000 lines to about 2,000 lines. That’s on par with what we saw with betterment.com: the old WordPress theme had about 12,000 lines of CSS; the new one has about 2,000 lines of code.
00:31:44
Now remember, we’re not just matching functionality; the new codebase supports a dramatically improved user experience and adds all these components that have made it possible to build all of our application interfaces from scratch as quickly as we did.
00:32:04
So, with all of that behind us, what are we doing with our newfound leisure time?
00:32:22
Well, standardizing the view patterns across all our apps freed up a lot of time and creative energy for a bunch of new stuff. We integrated Webpack pipelines into all of our apps, and we've built a Rails component framework inspired by JavaScript frameworks.
00:33:00
This establishes a clean foundation for a hybrid front-end architecture in which Rails, Stimulus, and React play a part. We’re building form interactions, animation frameworks, stateful visualizations, and lots of features, all based on the system we launched last year, which has been improving by the day.
00:33:20
Listening to a James Corden interview, he relayed something his dad used to say: the difference between doing something and not doing something is doing something. We accomplished impractical things in a seemingly impossible timeframe.
00:33:38
This was all because we made a decision to just do something, to commit to shipping, and to find clever compromises in order to do so. The stories of our shared experience involved dozens of people over eight action-packed, stressful weeks: problems and changes, mistakes and inventions, distractions and debates.
00:33:55
We didn’t lose our cool. We actually had a lot of fun because nothing really beats tackling tricky problems with good people. Thank you.