Fig. 16 A Guide for Developers

How To Work With Technical Debt

Manage technical debt in a mature codebase without making it worse.

Written by Ryan Buttrey September 22, 2016

Technical Debt is a thing.

Technical debt is a harsh mistress. As soon as a single line of code is written, debt begins to accumulate. Sometimes debt comes from something small like forgetting to document a magic number, or something large like adding a JavaScript library dependency no longer in active development.

In both cases, Technical Debt occurs when shortcuts or compromises (either deliberate or otherwise) are made within the product design or development which eventually makes it difficult or impossible to update or maintain.

The only way to cut out the debt entirely is to refactor all CSS, JavaScript, and Markup… Or, basically the entire front-end of the application. There are several problems with this:

  • When the product needs to ship new features quickly, this may have priority over fixing code. There’s always the next sprint. Amirite?
  • This is not a trivial task. There are many moving parts which, when poked, may not ever be righted again within a sane amount of time.
  • Bug testing is even more important now. Regression testing is even EVEN more important.
  • How long will this take? Will you be the only person spearheading the operation? Where do you start? Oh god.
  • You don’t get paid to sob in the corner.

If you’re spending more time fighting with your old code than actually launching new features, there’s a problem. If these issues are not addressed, it will become increasingly difficult to keep pace with customer demand or market changes. More substandard code is shipped for time compensation, the can is kicked down the road, and the cycle begins anew.

Your product’s development workflow is basically upside down–the cost of technical debt is infringing on the value of any new feature or update.

What’s more, project timeline estimates become increasingly fuzzy. How can a developer accurately estimate the amount of time or effort going into any development task? It may sound easy, but it could involve hacks or workarounds which stretch development time.

How do you work with Technical Debt?

Technical debt is always present, and it’s always on the cusp of getting worse. Thankfully, there are ways to work around technical debt (while fixing some issues along the way) or take preventative measures to keep things clean for as long as possible. Over the past few years, we’ve encountered a few common situations that have been overcome with a few simple tweaks to how we develop.

Situation 1: CSS is all over the place and completely unorganized.

You’re asked to work on a mature product and whoever was working on it before either didn’t care or didn’t know what they were doing. Maybe there were just too many people working on it. Either way, what you’re left with is an epic mess.

Overloaded classes.

Classes stuck at the end because it wasn’t clear where the should actually go.

Overrides on overrides.

Where do you even start without adding to the problem?

Before starting anything, read the CSS in its entirety.

This may sound like wasted time but I promise you it’s not. How are you going to understand the effect of the cascade unless you read it first? Unless you’ve written every bit of CSS (and sometimes even then), you can’t understand it unless you first analyze it and make sense of it. Take a day if you need to and acclimate yourself to what you’ll be working in. It will be worth the time and your code will be better off for it.

Spend time organizing and commenting the previously written code

So you’ve read the CSS. You hopefully have a good idea of how things are working (or should work). Start putting things in the right place and the right order. This is especially important if you’re going to be spending a significant amount of time in the app. The relatively small amount of time you spend restructuring the existing code will absolutely pay off in the end. (This doesn’t mean re-write anything. Only reorganizing.) This makes things much more readable and manageable later on.

Situation 2: Removing a class from an element causes JavaScript to stop working.

Here’s an example: for consistency’s sake, you’re asked to make changes to the nav button on mobile so it looks like the rest of the site’s buttons.

<button class="button button--nav-menu" type="button">Menu</button>

You investigate the CSS and are confident that all the styles included in the button–nav-menu class are unnecessary at this point. So you remove the class altogether.

<button class="button" type="button">Menu</button>

Perfect. The button looks exactly how you want. But you find out that the click behavior is broken because the JavaScript function was tied to that CSS class.

Decouple JavaScript from styles.

Attach JavaScript to a js- prefixed class. You, and anyone else working on this later, will know that this class is only used for JavaScript.

<button class="button js-nav-menu" type="button">Menu</button>

Situation 3: Styles you are writing are causing regressions.

My goal in writing CSS is to keep things as clean and neat as possible - avoiding overrides and nested styles whenever possible (Andy wrote about our standards when starting a project from scratch). Whoever was developing before me may not have had the same standards. If the current system you’re building in is full of nested styles and littered with overrides, being abundantly specific is crucial.

Things I’m writing are getting overwritten

I’ve been in situations where I can’t be specific enough. Everything I do is being overwritten by something.

I write something like this:

<button class="nav-button">Menu</button>
.nav-button {
    display: inline-block;
    padding: .15em .35em;

Those are the styles for the new design, but the button is overwritten by a nested class already existing in the CSS.

.module .button {
    display: block;

In this case, being clean and avoiding nesting might not be the best solution. Build new features with the same style and standard already established.

Things I’m writing are overwriting other things

I’ve also been in situations where I’m so generic for the app that I’m overwriting existing things. Unless you’re obnoxiously creative with your class names, there’s a chance you could be using an already existing class name. The simple solution here is to namespace your new CSS classes.

CSS pre-processors make this really simple:

.ns- {
    &thing1 {}
    &thing2 {}
<div class="ns-thing1"></div>

.thing1 could exist already and defining styles for that class could cause problems to existing elements on the site. Namespacing ensures that your class names are specific enough not to touch anything you don’t want it to.

Playing well with others is hard.

There are certain steps we take when starting a new project to avoid some of these issues, but the fact remains that a code project can never truly be free of technical debt. There’s always a balance that needs to be maintained in order for the team to be able to manage a codebase, with full understanding of the quirks built in, and still be able to quickly adapt and evolve the code moving forwards. A talented and versatile front-end developer is someone who can work well with others and inside the constraints of a project. When possible, implement some of these key practices when working with an established codebase.

It’ll be worth it, I promise. And future you’s will thank you.