We’re all familiar with debt, right? It’s that concept where an amount is owed from one person (the borrower) to another (the lender). We often use it to describe financial situations. For example, I borrow money from a bank. I now have debt with them in the amount of how much money they lent me (plus interest!) and expect me to repay it.
Debt isn’t always about money. In fact, we run into it as front-end developers all the time and may not even know it. We call this term technical debt and will be exploring what that means in this post.
Defining Technical Debt
I could pull a fast one on you and link you directly to the Wikipedia entry on technical debt to read yourself, but I think there’s more to it than what’s there.
Technical debt is the sum of compromises we make when writing code during the development process. You might even think of every line of code you write as contributing to technical debt. That’s because there is: the code itself, the results of that code, and also how that code interacts with other code. The consequences of debt arise when change needs to be made. The difficulty of coordinating that change with the desired result and other code’s impact on that result is tied to the degree of technical debt.
Each time we write code (especially if it’s against what might be considered a best practice), we start to see consequences in the final product we ship. Like money, the compromises we make in our code is something that can and often needs to be paid back because, if not, the debt can bankrupt us, in the form of difficulty making changes.
The one thing worth taking away from the Wikipedia entry is that not all technical debt is created equal. In fact, there are four types of debt to consider. Let’s take a look at each one.
Reckless Debt
Theses are the compromises we make in our code intentionally and without regard for the consequences they may have. It’s the equivalent of taking out a few fresh credit cards and maxing them out with no intention of paying off the balances. Not that I have any experience with that or condone it in any way.
Think about the times you have used !important
on a CSS attribute. There is almost always a way to avoid using it and it’s generally a good idea to do so. Still, how many of us sneak one in there every now and then because the trouble of refactoring code seems too daunting in the moment? That’s a compromise we make and will likely have to deal with at some other point in time when the style cascade fails us again in the future.
Sometimes we’re aware we’re being reckless, and sometimes we’re not. Naming is a classic example of where it’s easy to be oblivious of recklessness. Sometimes a name we choose (for a function, class name, whatever) seems perfectly OK at first, only to discover it clashes in an unexpected way in the future. The recklessness here being the absence or non-following of a naming strategy.
Prudent Debt
These are the compromises we make knowingly with an eye on the future. Have you ever borrowed money to make an investment in something that (hopefully) will pay off in the future? This is the same thing. In fact, prudence is often considered a virtue and is exemplified by careful thought and mindfulness on the future.
How do we make prudent compromises with our code? Think about the times you have written anything that was irrelevant at the time, but would make sense later in the future. I’m reminded of CSS3 features several years back. Remember when rounded corners were still unsupported in many browsers, but we all knew they would be supported soon? Using the prefixed and un-prefixed attributes together is a good example of a compromise that produces prudent debt:
.rounded-corners {
-webkit-border-radius: 5px;
-khtml-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
That’s a lot of code to support one small element of design, but we knew that it would play nice with browsers as they adopted the spec in future releases. At that point, we can freely pay back our debt by removing the vendor prefixes.
(Now, of course, we have tools like Autoprefixer to help with things like vendor prefixes.)
This type of thing is still debt. The planning and coding takes thought and effort and produces more code. Maybe things won’t play out just as you thought they would.
Deliberate Debt
We sometimes make intentional compromises with our code and this, in turn, leads to debt. You would never do something like that, right? Well, consider some of the reasons you might:
- Budget limitations
- Deadlines
- HiPPOs
I know that last one has bitten me in the butt at least a few times. The point is that there can be situations where we make compromises that are outside of our control, which results in taking out a technical debt, despite our best intentions.
Inadvertent Debt
This is sort of the result of not knowing what you don’t know. These are the compromises we make because we don’t know any better. It’s not that we mean to. We happened to do it without knowledge of doing so.
Naming things is relevant here, too. For example, we decide the class names used on elements and the way we name them can have adverse consequences on how other parts of the code work. That’s why we’re now seeing a bigger movement to develop naming conventions, like BEM.
Other examples might be…
- You forgot to consider some edge cases in what a function could accept
- You fixed a problem for one specific browser, and didn’t realize it broke another
- You didn’t realize a library you were using had a method for something, so you hand-rolled your own, only to discover yours doesn’t work quite as well
- Your tests omitted a certain possible user state
The Consequences of Technical Debt
I have never heard the word “debt” used to describe something positive. Instead, it seems to be something that carries negative consequences. For code, those consequences can be devastating, in the form of an unstable codebase, and probably a motivation-sucking one.
Then there is the “interest” metaphor. Interest is an amount calculated by a rate on the amount of debt we owe and are expected to pay back in addition to what was borrowed. The more money we have (code we write) and the longer we wait, the higher the interest. One compromise can lead to others and the snowball effect can create interest that becomes too great to pay back in the form of maintenance, performance, or deadlines, among other things.
Dealing with Technical Debt
Technical debt is a lot like financial debt in the sense that we likely want to avoid it as much as possible. Here are a few ways we can do that in addition to dealing with any debt we might have.
Document Everything
Making notes in our code is good practice in general, but can get us out of sticky debt situations as well. If you find yourself in the position of needing to make a compromise, document what it is as comments in the code. In those comments, explain what you did, what impacts it could have, and how to solve it in the future. Your future self will thank you.
Comments are particularly useful when they not only explain the code does, but:
- Why it does it
- Quirks that have come up related to it
- Where other code resides that is related to it
Coding Style Guides
Keep a coding style guide and follow it with diligence. A style guide will provide standards for your team or project, and will often help you make tough decisions, such as naming conventions or code formatting.
Need help with a style guide? Chris has a nice write-up on where style guides fit into the development process in addition to this example of a Sass style guide.
A well-followed style guide can reduce the cognitive load when reasoning about a bit of code you haven’t worked with recently.
Code Reviews
Review your code early and often. This could be a post in and of itself. Oh, it already is.
Be harsh on your code during these reviews. Could you have made better decisions when looking at the code for things such as maintainability, performance, or accessibility? The writer of the code probably isn’t the best judge of all these things.
Shame File
This is one of my all-time favorite recommendations. Harry Roberts floated the idea around as a way of keeping tabs on our deliberate technical debt. Keep all known compromises in this file and use it as a task list of debts that need to be repaid. And, like a large credit card bill, make an effort to at least clear the minimum payment for the month.
It may seem like making a pool for technical debt is a bad idea. But it’s known technical debt – which you could think of as less expensive that scattered, undocumented, insidious technical debt.
Wrapping Up
We covered the concept of technical debt in this post and often described it in terms of money. Do you think technical exists? If so, where do you rack up the most debt and how do you deal with it? If not, is technical debt (and the financial metaphor) a bit much? Share your thoughts in the comments.
shame.css
can also work if it’s used as a staging area, and only gets moved into its other files after review. “Guilty until proven innocent,” in a way.Ha! That’s a great idea.
Another possible use is to use Sass (or some other preprocessor) to not include it when compiling unless it has been reviewed. Sort of the automated double-opt-in approach. :)
That’s a great idea.
Why would you remove the code later on? People on outdated browsers wouldn’t have the prefix benefit anymore.
Great article – however I think it’s worth noting that comments in code are themselves also technical debt. It’s an additional line of code which has to be maintained as changes are made. But I agree it’s important to add them in particular quirky instances.
Nicely explained concept of technical debt. But you forgot to mention SonarQube that provides a nice way to compute technical debt: http://www.sonarqube.org/ with the CSS plugin: https://github.com/SonarCommunity/sonar-css ;-)