Recently I’ve been hearing people use the term technical debt to describe all sorts of things that are related to system improvement. However, used properly, technical debt is not a catch-all phrase for system improvement work, but a subset of that work.
What is Technical Debt?
Ward Cunningham first described technical debt in an experience report in 1992:
“Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite… The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt. Entire engineering organizations can be brought to a stand-still under the debt load of an unconsolidated implementation, object-oriented or otherwise.”
To better understand technical debt, let’s explore the analogy to financial debt.
Four out of five people buying a home in the United States in 2013 took out a mortgage to do so. For the most common type of mortgage (30 year fixed with 20% down) on the average home price in the U.S. (~$200k), the total cost of the loan is 175% of the cost of just buying it outright. That’s right, for a $200,000 house, you’d pay about $350,000 over the life of the loan, or $150,000 just in interest. Why would anyone do that? Of course the answer is simple – it might take you years to accumulate the cash before you could own a home, so people make a decision to accumulate debt in order to get the home sooner, knowing full well that they’ll need to pay more in the long run for that early entry. They are trading an advantage (early entry) for a disadvantage (higher total cost).
Technical debt, then, is the conscious choice to get to market faster by skipping some steps required for long term code sustainability. Just like financial debt, we know that in the long term it is more expensive (we’ll need to pay interest on top of the principle), but we do it because the advantage of getting to market sooner outweighs that cost. And just like financial debt, we need to create a budget for paying it back down.
Are All Bugs Technical Debt?
Let’s use three of example defects to answer this question.
- Defect A is an error that was reported by a customer a few months after a release. It happens when a specific workflow is followed that the team didn’t anticipate, and causes the program to freeze. The team was using good automated testing, but we didn’t catch this one due to the unusual customer workflow.
- Defect B is an error that was discovered in a regression test, and was determined to not be a “release-blocking” bug – in other words, we knew about it but decided to release anyway. Now customers are complaining about the bug and the team decides to go ahead and fix it.
- Defect C is a display problem that happens on a new version of a browser that was released after our software was released. It didn’t occur in previous versions of the browser.
In this example, only Defect B is technical debt, Defects A and C are not because the business never made a conscious choice to ship with those defects. In any moderately full featured software product, the level of complexity involved results in some defects making it through to customers. Some of those may have been known prior to shipping and others weren’t. If we didn’t consciously choose to release with the bug, or were consciously skipping some defect prevention steps in order to get out the door sooner, these are just defects, not technical debt.
Is Refactoring Technical Debt Reduction?
As in the case of defects, it all depends on whether we were making conscious trade-offs on architectural and design approaches in order to release sooner. If we know that there is an area of the code that is a mine field, and no one wants to touch it (except for that one coder that’s an expert), but we chose not to refactor due to the amount of time & effort involved, then we have technical debt. If, on the other hand, new code has been added, the system is becoming more complex, and we just need to do some refactoring as part of the standard craft of software development, that type of refactoring is not reducing technical debt, it is simply the good practice of continuous system improvement. Think of it as entropy reduction, not technical debt reduction.
Lack of robust test automation is probably one of the most common instances of technical debt. In the craft of software development, using automated tests is equivalent to a surgeon counting the sponges prior to a surgery to make sure we don’t leave anything in the body of the patient. If we’re not doing it, we are really being irresponsible. Now, I’m not stating that we need 100% code coverage for unit tests, or 100% functional coverage for automated regression tests – there is a point of diminishing returns. Again, it comes down to a case of intention – do we want much better coverage but just don’t have time to do it? If we don’t have time, that is just another way of saying it is not as high a priority as shipping the feature. In other words, we are consciously choosing to get to market faster by skipping automation that the team thinks would be helpful. In such a case, we are creating technical debt.
Technical Debt reduction is one important category of system improvement. To lump all system improvement under the banner of technical debt does us a disservice because it seems to imply that any problems or inefficiencies in the code were just a conscious choice by the team. That is not the case. Sometimes we make decisions to ship a less than optimal product in order to get earlier feedback or a market advantage. Even if we aren’t doing that, there will be ongoing improvement required. Separating out Technical Debt as a specific category helps us acknowledge the prioritization decisions we’re making regarding quality vs. speed, and watering that term down by lumping it together with everything else muddies the waters and can lead to disengagement by the team that didn’t “get it right the first time”, an impossible task in a complex domain.
It is critical that Technical Debt be paid down as soon as possible. It follows the rules of compound interest – the longer we wait to pay it off the more it accrues, and if it’s not paid off in time, eventually it leads to a bankrupt code base. It simply needs to be abandoned and started over from scratch, an extremely costly result. Clarifying when we are making a conscious choice to accumulate technical debt needs to have a payment plan associated with it to avoid these risks. Lumping ongoing system improvement into that category makes the payment plan much more difficult to plan for.