In spite of all that’s been written on the subject of technical debt, it’s still a common occurrence to see it defined as simply “bad code”. Likewise, it’s still common to see the solution offered being “stop writing bad code”. Technical debt encompasses much more than that simplistic definition, so while “stop writing bad code” is good advice, it’s wholly insufficient to deal with the subject.
Steve McConnell’s definition is much more comprehensive (and, in my opinion, closer to the mark):
A design or construction approach that’s expedient in the short term but that creates a technical context in which the same work will cost more to do later than it would cost to do now (including increased cost over time)
While it’s a better definition, I’d differ with it in three ways. Technical debt may not only incur costs due to rework of the original item, but also by making more difficult changes that are dependent on the original item. Technical debt may also end up costing nothing extra over time (due to a risk not materializing or because the feature associated with the debt is eliminated). Lastly, it should be noted that the cost of technical debt can extend beyond just effort by also affecting customer satisfaction.
In short, I define technical debt as any technical deficit that involves a risk of greater cost and/or end user dissatisfaction.
This definition encompasses debts that are taken on deliberately and rationally, those that are taken on impulsively, and those that are taken on unconsciously.
Code that is brittle, redundant, unnecessary, unclear, insecure, and/or untested is, of course, a type of technical debt. Although Bob Martin argues otherwise, the risk of costs to be paid clearly makes it so. Likewise, aspects of design can be considered technical debt, whether in the form of poor decisions, intentional shortcuts, decisions deferred too long, or architectural “drift” (losing design coherence via new features being added using new technologies/techniques without bringing older components up to date, or failing to evolve the system as the needs of the business change). Deferring bug fixes is a form of technical debt as is deferring automation of recurring support tasks. Dependencies can be a source of technical debt, both in regard to debt they carry and in terms of their fitness to your purpose. The platform that hosts your application is yet another potential source of technical debt if not maintained.
As noted above, the “interest” on technical debt can manifest as the need for rework and/or more effort in implementing changes over time. This increase in effort can come through the proliferation of code to compensate for the effects of unresolved debt or even just through increased time to comprehend the existing code base prior to attempting a change. As Ruth Malan has noted, strategy may drive architecture, but once the initial architecture is in place, it serves to both enable and constrain the strategy of the system going forward (strategies requiring major architectural changes typically must offer extremely high ROI to get approval). Time spent on manual maintenance tasks (e.g. running scripts to add new reference values) can also be a form of interest, considering that time spent there is time that could be spent on other tasks.
Costs associated with technical debt are not always a gradual payback over time as with an ordinary loan. Some can be like a debt to the mob: “they come at night, pointing a gun to your head, and they want their money NOW”. Security issues are a prime example of this type of debt. Obviously, debts that carry the danger of coming due with little or no notice should be considered too risky to take on.
Having proposed a definition for the term “technical debt” and identified the risks that it entails, it remains to discuss what to do about it. The first step is to recognize it when it’s incurred (or as soon as possible thereafter). For debt taken on deliberately, recognition should be trivial going forward. Recognition of existing debt in an established system may require discovery if it has not been cataloged previously. Debt that has been taken on unconsciously will always require effort to discover. In all cases, the goal is to maintain a technical debt backlog that is as comprehensive as possible. Maintaining this backlog provides insight into both the current state of the system and can inform risk assessments for future decisions.
Becoming aware of existing debt is a critical first step, but is insufficient in itself. Taking steps to actively manage the system’s debt portfolio is essential. The first step should be to stop unconsciously taking on new debt. Latent debt tends to fit into the immediate, unexpected payback model mentioned above. Likewise, steps taken to improve the quality up front (unit testing, code review, static analysis, process changes, etc.) should reduce the effort needed for detection and remediation on the back end. Architectural and design practices should also be examined. Too little design can be as counter-productive as too much. Striking the right balance can yield savings over the life of the application.
Deciding whether or not to take on intentional technical debt is less black and white. Often this type of debt is taken on for rational reasons. An example of this is what Ruth Malan characterizes as “…trading technical learning and code design improvement for market learning (getting features into user hands to improve them)”. Other times, the balance between risk and reward (whether time to market or budget) may tilt in favor of taking on a debt. When this is the case, it is critical that the owner(s) of the system make the decision in possession of the best possible information you can provide. An impulsive decision taken on the basis of “feel” rather than information will likely carry more risk.
Retiring old debt should be the final link in the chain. Just as the taking on of new debt should be done in a rational manner, so should the retirement of old debt. Not all debt carries the same risk/reward ratio and efforts that carry more bang for the buck will be an easier sell. Although some may disagree, I firmly believe that better outcomes will result from making those who own the system active partners in its development and evolution.
It’s highly unlikely that a system will be free of technical debt. Perversely, being free of such debt could actually be a liability. That being said, there is a world of difference between the two poles of debt-free and technical anarchy. Effort spent to rationally manage a system’s debt load will free up time to be put to better use.
24 thoughts on “Technical Debt – What it is and what to do about it”
I’ve taken recently to have a running list of things that we add that I consider to be technical debt. The experiment is too new for me to draw conclusions from it, but it’ll be interesting to see how it goes.
Indeed…I look forward to hearing how it works out for you.
@Gene, I agree with almost everything you say in this post, apart from one, very important, thing, in my view.
I believe that “stop writing bad code” is not good advise at all.
I am aware that this is a long response to a small bit of the post. I feel it’s important for me to share.
In a harsh analogy, this is like telling a thief “stop robbing houses” or telling Tony Soprano (RIP) “stop killing people”.
The software industry is notorious for bad programming habits, yet, most programmers believe they write good code. Or so I believe.
This resonates a lot with what you write about unconscious technical debt. Denying that our or my code is not up to scratch.
If you want a thief to stop stealing (assuming you believe stealing is wrong, of course) you should educate that person on the implications of his/her action on oneself and on society; on seeing that the alternatives may be hard to accomplish, but they are not difficult to do; on feeling happy when getting to the desired state of not stealing.
Avoiding technical debt is not that far off: telling a person or a team that they should “stop writing bad code” may (and probably will) be perceived as an insult and judgemental.
Collecting empirical data to demonstrate the effects of existing code may bring awareness.
Suggesting or implementing alternatives in the way of CI and static code analysis and automation (and a plethora of other practices) may incentivise to adopt at least some of these practices.
And reflecting on the satisfaction level is a good way to demonstrate where we are now, and where we can be.
Please don’t get me wrong – the post is, to me, about applying common sense where it should. It’s about attending to the important things with awareness. It’s a very good post.
And, at the same time, the unconscious is playing havoc in our industry, making us believe our code is so fantastic, where, in fact, our app doesn’t even start, or our performance is way too sluggish, or whatever horrific software disaster we are releasing, convinced that “stop writing bad code” does not apply to us.
@Ilan, great comment. You are right that “stop writing bad code” is too cavalier an answer. Great points re: denial/lack of self-awareness when it comes to quality.
Pingback: Real Estate News NYC via Tigho | Technical Debt – What it is and what to do about it - Real Estate News NYC via Tigho
Pingback: Technical Debt – What it is and what to do about it | Boardmad
On Technical Debt, my focus is on the work not yet done and its impact on the product and its users and caretakers whether due to neglect or ignorance or purposeful deferral. Whether work costs less now or more later, work not done is Technical Debt.
A technical debt could be anything that requires you to do more work in the future without getting any gains – it’s just running to stay in place. This can often be a good thing if the future is uncertain and the choice is managed well.
A risk in this analysis is taking technical debt too literally. It can help illustrate the point but we still need to make decisions that fit the risks and capabilities in our projects and not an arbitrary outside model. If you want to really get into finance then options, futures, and derivatives might be a better example 🙂 They are also subject to future uncertainty and can carry large hidden risks.
Indeed. Although I didn’t link to it in this post, Steve Freeman’s “Bad code isn’t Technical Debt, it’s an unhedged Call Option” is a good treatment of the risk-based nature of technical debt.
Pingback: Legacy Systems – Constraints, Conflicts, and Customers | Form Follows Function
Pingback: Technical Debt – What it is and what to do about it | WhiteBox Business Solutions
Pingback: Risky Assumptions and the Assumption of Risk | Form Follows Function
Pingback: Technical Debt & Quality – Binary Thinking in an Analog World | Form Follows Function
Pingback: Technical Debt – What it is and what to do about it | Iasa Global
Pingback: Legacy Systems – Constraints, Conflicts, and Customers | Iasa Global
Pingback: Technical Debt – What it is and what to do about it | IasaGlobal
Pingback: Microservice Mistakes – Complexity as a Service | IasaGlobal
Pingback: Technical Debt and Rolling Re-writes (Who Needs Architects?) | Form Follows Function
Pingback: What We Have Here is a Failure to Communicate | Form Follows Function
Pingback: Dealing with Technical Debt Like We Mean it | Form Follows Function
Pingback: Dealing with “Technical Debt” | IasaGlobal
Pingback: Legacy Systems – Constraints, Conflicts, and Customers | IasaGlobal
Pingback: Stopping Accidental Technical Debt | Form Follows Function