Friday, January 4, 2013

Xtreme Makeover: Legacy Codebase Edition

At Coursera, we have two codebases that power our user-facing experience - the one behind www.coursera.org, where students browse and enroll for courses, and the one behind class.coursera.org, where students actually take classes. The latter is our oldest codebase, and is based on the code that powered the first version of Coursera (before we even had a name or a company), the machine learning class. It's built on PHP/mySQL and a custom routing/templating/ORM framework.

In the beginning, it probably had very little CSS and JavaScript, so the creators understandably didn't put thought into how to write the CSS and JS in a way that would be maintainable and scalable to a large codebase and team. Well, 50 classes and countless features later, it became a hodge-podge of PHP, HTML, CSS, and JS all entangled together with no conventions in sight.

But, it worked — so we didn't want to touch it. When we touch it, we risk introducing bugs or un-introducing "features" that we were unknowingly supporting before, and when students grades and instructors happiness are on the line, we opt for doing the safe thing.

We couldn't leave it unchanged forever, though. We had many usabilty issues (meaningless icons everywhere, hard-to-navigate menus) and we also wanted to improve the university branding. So our designer proposed a redesign of the user-facing interface, and when I set about implementing that redesign in the codebase, I realized that I could not bring myself to do the user-facing improvements without making some developer-facing improvements too.

Why? Well, as a developer myself, I find it frustrating to work in an environment that doesn't use modern tools (like CSS pre-processors) and best practices (like avoiding global variables). And, as I know we will be bringing on more developers soon, I want them to be surrounded by good examples of what our codebase should look like, and not think "Oh, I guess this is just how we do things around here." I also want them to enjoy working in any of our codebases and expect that they can apply the same best practices to all of them.

So, I set about on the behind-the-scenes redesign, and thanks to help from the rest of the frontend team, we were able to make a lot of significant improvements. Here's a round up:

JS Code quality:

  • We moved all of the JavaScript into separate files (much of it was in PHP templates, and some of it was even PHP-generated, ick).
  • We ran JSHint over all of our JavaScript files (not third party libraries) and fixed any issues that came up. (We had to guess with situations like " == 0" vs " === 0" what it was they actually wanted - which is why developers should use JSHint from the beginning!).
  • We stopped using third party JavaScript libraries that were adding unnecessary bloat and were difficult to customize - like fancybox, which we replaced with our own much smaller modal library.

JS Performance:

  • We made sure all of our JavaScript executed after the document ready event.
  • We moved all of the <script> tags into the bottom of the page.
  • We wrote a build task to compress all of the JS files.
  • We wrote a build task to create a single compressed file of all the JS libraries that are used across most of the pages.

CSS Code Quality:

  • We stopped using CSS class names that were short and ambiguous, like "title", and instead moved to prefixed, explicit names, like "css-forum-thread-title". We also only use IDs when needed for accessibility reasons, and instead prefer classes everywhere.
  • We ported all of our CSS to Stylus. That means we can now take advantage of variables (like the URLs for our assets, which we can now serve off CloudFront), automatic vendor prefix generation (which will improve our cross-browser support), and extending (which means I can extend Bootstrap classes and avoid directly using them in our HTML).
  • We started using box-sizing:border-box, which makes our height and padding calculations easier, but did require overriding the CSS for a few third party libraries.

CSS Performance:

  • We created separate Stylus files for each functional area of the site, and we have one file that imports all of them, so that we can work in small files but still only serve one big file.
  • We wrote a build task that compresses the generated CSS, so it's not actually that big.

226 commits later...! Now that we have our codebase in a better state, we can enforce our conventions on any changes that developers make in the future (via code reviews), and maybe future developers won't even realize that it was ever our legacy codebase. Maybe. :-)

No comments: