Saturday, December 15, 2012

A Tale of Two Bootstraps: Lessons Learned in Maintainable CSS

The Pull Request

346 files changed, 100 commits, 6 authors.

The title of that pull request? "Port to Bootstrap2." Yes, that was how much effort it took to make a change as seemingly simple as upgrading a CSS framework in our codebase.

The Journey

It all started on a rainy train ride, my daily commute back to San Francisco on the Caltrain. I knew that we used Bootstrap 1 in our legacy codebase (which powers, where students watch lectures and take quizzes) and we were using Bootstrap 2 in our "modern" codebase (the one behind, where students find courses to enroll in), and that was making it hard for us to share resources across them, so I thought, hey, I know, I'll just go through and upgrade all our class names in the old codebase. I had upgraded personal projects to Bootstrap 2 in the past, and it hadn't been that hard, especially with the help of the upgrade guide.

As you can guess, it took a wee bit longer than that train ride. Why?

  • Our codebase size: This was only my second foray into this codebase, and so I had very little idea how it worked and how big it actually was. After a few greps for Bootstrap class names, I soon realized that the codebase was about 10x bigger than I eventually realized. I was only familiar with the student side of, but of course, there's an instructor side, and as it turns out, we have a rather comprehensive admin interface. I also discovered that we had multiple versions of some of our interfaces and we're still supporting both of them, until all the admins have time to learn the new interfaces.
  • Our codebase architecture: Our legacy codebase is written in a hand-spun PHP framework, spitting out HTML with inline JS and linked JS files. I expected to find some CSS class names in the JS, used in selectors or DOM creation, but besides finding a lot of that, I also discovered CSS class names outputted by PHP, and the worst, CSS class names in JS outputted by PHP. Yech! It meant that I had to look in every nook and cranny for class names, from the back to the front.
  • The bootstrap class names: Bootstrap - both 1 and 2 - uses very succinct class names, like "alert", "label", "btn". Those short names are great when you're hand typing out your CSS, but oh wow, they are a *bitch* to search for in a codebase of thousands of files. Do you know how often developers use the word "label"? All the freaking time. Bootstrap 1 even had these short add-on class names for component states like "primary", "success", "info", which thankfully Bootstrap 2 changed into the more specific "btn-primary", "btn-success", "btn-info".
  • The bootstrap JS components: At its core, Bootstrap is a CSS framework, but it offers a small number of JS components on top, and we were using a few of them. After upgrading to the new JS components and making them RequireJS-friendly (so that we could use the same JS libraries in our Require-powered codebase as this legacy one), I had to upgrade the relevant HTML and test that the interactivity still worked as expected. Now, I was no longer making just a visual change, I was possibly affecting interactions instead.

All of those factors combined turned my train-ride of a change into a month-long change, where I roped in more of our engineers, held many "Bootstrapv2athons", and worked with a QA team to do a weeklong test of the branch in multiple browsers.

Finally, we were ready to deploy it. Or, more accurately, my colleague convinced me that we should just get the damn thing out and be ready to deal with anything that we missed. As it turns out, yes, we still did miss a few things- a few big things, a few little things, but we responded as quickly as we could to minimize the damage. Now, finally, we have our entire codebase running off the same Bootstrap version and we can start to share our CSS and JS. Man, was that a journey of epic renaming proportions that I did not imagine.

The Lessons Learned

But I really don't think it should have to be that hard to upgrade a CSS framework. Here's what I want to try next time I work in a codebase that's based on Bootstrap:

  • Do not use the Bootstrap class names directly in our code. They are too short, and it's too hard to keep track of where they're being used.
  • Instead, use a CSS pre-processor like Less (which Bootstrap itself is based on) to extend the Bootstrap class names and define longer, more app-specific, more semantic class names. For example, we might extend btn into "coursera-generic-button" and we might extend btn/btn-success into "coursera-save-button".
  • We would then use those custom CSS class names everywhere instead of the Bootstrap class names, especially if we are using them in our JavaScript.

For a longer article that also recommends that technique for semantic more than maintenance reasons, read Stop Embedding Bootstrap Classes in your HTML.

What do you think? I'd love to hear ideas from those of you who rely on Bootstrap or similar frameworks in your codebase, and what you've done to avoid dependence and ease maintenance. I haven't yet had the chance to test out my strategy, so if there's a better way, I want to hear it.

Oh, on a related note, I hear that Bootstrap 3 is almost out... time to start my next epic branch?

No comments: