Refactoring Legacy CSS

As projects grow, they can become unruly. Kasey shares when it’s a good time to reign in the chaos and start refactoring.

We’ve already learned all about why you should refactor your CSS, and it’s an easy practice to start when a project is new or small, but how do you refactor those old legacy projects that have been hacked together over the span of many years? It might sound like the thing nightmares are made of, but it doesn’t have to be.

Recognize When It’s Time to Refactor

The technologies and methodologies we use to build the web are constantly changing and evolving, and we’ve got to keep up by experimenting with new ways to build better, more organized websites. Finding the right method comes with a lot of trial and error. Sometimes when starting a project, you find a method that works well, but as the project grows, it can be difficult to maintain. That is when it’s a good time to start refactoring.

I recently worked on an older project that organized SCSS by media query breakpoints. All styles for the smallest screen size lived in a partial called _smallest.scss, all styles for 600px screen sizes lived in a _600.scss partial, and so on. When it came time to redesign all the tables on the website, we had moved on to organize our stylesheets in a more modular way. Trying to redesign the tables with the breakpoint partials in their current state would have been cumbersome, but treating the tables as a stand-alone module would make future edits and debugging much easier. That’s when we recognized it was time to refactor our legacy code and bring it up-to-date to our current standards.

Work in Small Chunks

It can be tempting to refactor everything all at once, and if you are working on a small project, that may be the best route. But if you are refactoring a large site with a lot of moving pieces, it is best to work in small, manageable chunks.

For us, redesigning the tables was the perfect starting point for our refactoring. We were able to focus on one component on the site while still improving the entire code base. We combed the breakpoint partials and pulled out anything and everything that had to do with a table and created a new _tables.scss partial. We were still left with all the original breakpoint files, but they were starting to get smaller in size. At that point, we were able to focus on the original problem at hand, redesigning those tables, but the code was much less overwhelming because it all lives in one place.

Don’t Delete Things Just Yet

It can be tempting to start removing old code left and right, but don’t get delete happy just yet. Large websites can be full of surprises and hacky code, but that hacky code is usually there for a reason. It’s making something, somewhere work.

Our large website was filled with pages with tables. Just when I thought I knew where they all were, another one would appear out of thin air. As tempting as it was to just update the styles and cross my fingers that nothing would break, it was safer to leave the old styles and create a new set of styles for the redesigned table. This gave me the option of updating tables as I came across them without the risk of breaking undiscovered tables. Eventually, with the help of a project-wide search, I was able to find all instances of the tables, but some of them weren’t included in the scope of this round of refactoring. By leaving the old set of styles in place until they were all updated, I was confident everything would continue to work until I was able to make all the updates, and only then could I finally delete the old code.

Documentation and Comments

Comments and documentation are everyone’s best friend. The goal of refactoring is to make the code better and easier to use by anyone who picks it up. Legacy code can be difficult to work with as it is, but legacy code with partially refactored code can be just as confusing, if not more.

Our new table partial was filled with comments letting whoever worked on the project next know which code was being refactored out and what was there to stay. We also included documentation in the project’s README file that outlined the new code guidelines and naming conventions we were converting to. We wanted anyone to be able to pick up the project and understand what was going on.

Example README Documentation:

Refactoring Stylesheets

To make the stylesheets easier to maintain we will transition away from the current media query breakpoint organization and content based class names. This will be done gradually as we create/modify a feature.

  • When working on an existing feature, refactor the CSS by pulling all current styles from mq breakpoint partials and creating new feature partials.
    • If needed, create "sub-partials"
      • _table.scss
      • _table-striped.scss
  • When creating/renaming classes:
    • Don't delete old class names until it is safe to do so. Leave them in place and include new class names.
    • Avoid content based class names, use functional class names instead.
    • BEM naming methodology:
      • Block component .table { }
      • Modifier .table--striped { }
      • Child element .table__cell { }

Test and Commit Often

Anyone who has had a computer crash in the middle of working on something knows you should save often. Well, the same thing goes for commits. Don’t wait until you have modified 50 files and rebuilt entire components to make a commit. Just like you should refactor in small chunks, you should make commits in small chunks too. When is a good time to commit? Any time you make a change and test that it’s working, make a commit.

On the surface, the table redesign seemed like it was only affecting one component when it actually affected an entire website. We had to make frequent commits so that at anytime, if something broke, we had a previous working state to go back to without losing a bunch of work in the process. After we removed the table styles from the old breakpoint partials and tested, we made a commit. After we added the new table styles and tested, we made a commit. After we updated and tested a table, we made a commit. If you hate the idea of all those small commits littering your history, you can always go back and squash them together once you’re finished refactoring and everything has been tested.

Time Well Spent

Refactoring a large project with legacy code can be frustrating and overwhelming when getting started. You’ll invest a lot of extra time in the beginning just reorganizing existing code, and you might wonder if it’s worth your time and the client’s budget. But eventually, you will get to the point where editing your project isn’t a chore and making edits or enhancements will be much quicker than if you were still fighting with legacy code.