Eliminating legacy code and modernizing large CSS codebases.
It’s the small things.
At SmugMug our technology stack consists mainly of PHP for our backend, and YUI and React in our front-end code. Over the years we’ve seen technologies come and go, and some of that is still visible in the codebase. For example, there are small areas of the code that have some dependency on YUI 2, while others are written using modern React code bundled using ES6 syntax. When dealing with larger, older codebases like ours, you accumulate certain amounts of technical debt. This is not unique to us but simply the nature of the beast.
When technical debt like this is isolated, it can be reduced, refactored, and eventually replaced or removed. If they’re not isolated, this technical debt starts to become tricky to deal with.
Until recently we had one bit of technical debt that was the complete opposite of isolated: it was everywhere! Our CSS.
There’s something terribly wrong in that code. What are those PHP tags doing there?!
Customization and SmugMug.
Before we dig in, here’s a bit of background as to why we had CSS with PHP tags. SmugMug allows our customers to personalize their sites to their hearts’ content. We provide them with prebuilt interactive Content Blocks to showcase their beautiful photos for others to see and purchase. They also have the ability to choose their themes, which include fonts, color schemes, opacities, and more.
Themes have 5 main properties: primary color, accent color, text color, header font and body font. Since the majority of the PHP usage is around Colors, let’s focus on those.
Our Theme Editor showing default theme settings.
Theme colors are used to generate a color palette of 32 colors for each color type. This spectrum of colors lets us to work with all kinds of color combinations, allowing us to create customized pages and interactive Content Blocks that match the selected theme.
As developers-with guidance from our designers-we select one of the 32 colors to use in our CSS for the different parts of our site, like our Lightbox, Galleries, and Content Blocks. The algorithmic nature of the color palette guarantees that, regardless of the selected theme, our customers’ sites will look their best. This level of customization has been a key feature of SmugMug since early 2004.
Screenshot of old cobranding examples or interfaces (from archive.org).
How it’s done.
In order to generate CSS code with dynamic color values you need some sort of variable mechanism to substitute values at run time. So naturally, this being a PHP codebase, we added PHP to our CSS! This allowed our CSS to have variables with values that could change on the fly.
Why didn’t we use SCSS, SASS, LESS, or any of the countless other tools available to developers?
Simple. We didn’t have a choice! Back in 2004, when the first signs of this feature were appearing, the set of CSS tools was much more limited. In fact, none of the preprocessors in use today existed. SASS didn’t arrive until two years later. As you can imagine, having PHP in our CSS has caused more than a couple of headaches in the past, sometimes holding us back.
For example, in order to enable CSS minification, we had a workaround where we commented out PHP tags with multiline comments, minified the CSS and undid the comment process to bring PHP tags back. The fact that this worked is half a miracle. This was not a sustainable way forward.
We decided to investigate the tools available now that would be up to the task, but first we needed to understand how our themes were defined and built.
Finding a solution
Of the two most popular tools (SASS and LESS) that could serve our needs, we decided to go with LESS. Both tools could work equally well, but LESS had one minor advantage during the experimentation phase that sealed it as our choice: the
For those unfamiliar with LESS, the
inline import directive allows you to instruct the compiler to treat a certain file as valid CSS regardless of its contents. The contents of this file are placed verbatim into the resulting final CSS produced by the LESS compiler.
This import directive would allow us to do something like
While this in-between state is less than ideal, it would allow us to transition the codebase in small chunks. Whereas modules that are converted to LESS can no longer contain PHP. As people worked on older CSS, we could encourage developers to move the code over to LESS.
With this tool at hand, a proof of concept was built and presented at our quarterly hackathon. This
inline import directive allowed us to skip most of the hard tedious work and focus on the initial challenge: themes and theme colors.
PHP + LESS to the rescue.
For any non-theme CSS code, we can use the standard LESS compiler as part of our regular build process. To help manage some repeated code like media breakpoints and some internal skin code, we created a global
variables.less. This file is imported by all bundles and provides a universal library of useful variables and mixins. For those that did depend on themes, we engineered a solution for compiling LESS code within PHP. A LESS-PHP bridge.
Leveraging a PHP library capable of compiling LESS code, we created a mechanism for generating color variables on the fly. Using a simple template LESS file, we can build a dynamic set of variables for each color and its unique value.
This file can then be appended to the end of any theme-able bundles. Due to CSS/LESS precedence rules, the last declaration of a value takes precedence allowing a global override of any variables; in this case, skin-color values.
With the bridge complete and the ability to
inline import CSS files using PHP without causing issues, we had a way forward. At the end of the hackathon, we were able to serve some pages with a mix of LESS and CSS/PHP.
Taking the plunge.
The demo of a working proof of concept sparked a bit of conversation among the engineering team, and a deeper look was required. Upon closer inspection, one of our engineering leads noticed a pattern in all our PHP in use. This pattern allowed us to easily script a full transition to LESS without the intermediate
inline step. In four days we’d developed a quick Python script that could convert about 90% of all our usage of PHP in CSS. The other 10% was either dead legacy code that could be purged (some IE6 workarounds) or easy enough to change by hand.
In the end it took about six days of scripting, manual fixes and developer validation to get all our CSS/PHP in LESS files. Not only did the scripts eliminate a large amount of tedious work, it also made it possible to eliminate a large set of possible errors. By leveraging automation we could guarantee that if the transition script worked correctly in one case, it would always work the same way elsewhere. All these benefits, with nothing more than a set of simple regular expressions.
Still, in an abundance of caution, we decided to do a full regression test of the site and functionality related to themes. This process revealed some smaller issues that were missed by the manual diff inspections, but overall no major issues surfaced during this test. For a change consisting of over 25k lines of CSS, this was a nice surprise.
For the longest time this had been a part of the code that engineers thought would not be worth the huge effort needed to refactor and update. But in a short amount of time it went from being a small annoyance to a junior engineer to a successful refactoring effort that improved and modernized our codebase. While there were plenty of bumps in the road, the amount of effort required to do this transition went from months to days, all thanks to some clever thinking and scripting. Just goes to show large projects can sometimes be simplified with the tools we use every day.
Looking to the future.
We’re very happy with LESS and the benefits it has brought us, and we’re looking to take advantage of its features in the future, like plug-ins for browser pre-fixing. There’s also room for performance improvements in our themes to provide faster live previews.
Did any of that post get you thinking about a better or different way we could have done this? If any of it sounded interesting to you, please drop us a line at email@example.com. We’re always looking for talented engineers interested in solving problems like these.