I’ll Take a Crack at Defining Tech Debt
Not too long ago, on one of my Chess TDD posts, someone commented asking me to define technical debt because I had tossed the term out while narrating a video in that series. I’ll speak to it a bit because it’s an extremely elegant metaphor, particularly in the context of clarifying things to business stakeholders. In 1992, Ward Cunningham coined the term in a report that he wrote:
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.
The Finance Metaphor Relating to Code
Everyone with an adult comprehension of finance can understand the concept of fiscal debt and interest. Whether it was a bad experience with one’s first credit card or a car or home loan, people understand that someone loaning you a big chunk of money you don’t have comes at a cost… and not just the cost of paying back the loan. The greater the sum and the longer you take to pay it back, the more it costs you. If you let it get out of hand, you can fall into a situation where you’ll never be capable of paying it back and are just struggling to keep up with the interest.
Likewise, just about every developer can understand the idea of a piece of software getting out of hand. The experience of hacking things together, prototyping and creating skunk-works implementations is generally a fun and heady one, but the idea of living for months or years with the resulting code is rather horrifying. Just about every developer I know has some variant of a story where they put something “quick and dirty” together to prove a concept and were later ordered, to their horror, to ship it by some project or line manager somewhere.
Evolution from First Definition
The two parallel concepts make for a rather ingenious metaphor, particularly when you consider that the kinds of people for whom we employ metaphors are typically financially savvy (“business people”). They don’t understand “we’re delivering features more slowly because of the massive amount of nasty global state in the system” but they do understand “we made some quality sacrifices up front on the project in order to go faster, and now we’re paying the ‘interest’ on those, so we either need to suck it up and fix it or keep going slowly.”
Since the original coining of this term, there’s been a good bit of variance in what people mean by it. Some people mean the generalized concept of “things that aren’t good in the code” while others look at specifics like refactoring-oriented requirements/stories that are about improving the health of the code base. There’s even a small group of people that take the metaphor really, really far and start trying to introduce complex financial concepts like credit swaps and securities portfolios to the conversation (it always seems to me that these folks have an enviable combination of lively imaginations and surplus spare time).
My Take on Defining Tech Debt
I can’t offer you any kind of official definition, but I can offer my own take. I think of technical debt as any existing liability in the code base (or in tooling around the code base such as build tools, CI setup, etc) that hampers development. It could be code duplication that increases the volume of code to maintain and the likelihood of bugs. It could be a nasty, tangled method that people are hesitant to touch and thus relates in oddball workarounds. Or it could be a massive God class that creates endless merge conflicts. Whatever it is, you can recognize it by virtue of the fact that its continued existence hurts the development effort.
I don’t like to get too carried away trying to make technical debt line up perfectly with financial debt. Financial debt is (usually) centered around some form of collateral and/or credit, and there’s really no comparable construct in a code base. You incur financial debt to buy a house right now and offer reciprocity in the form of interest payments and ownership of the house as collateral for default.
The tradeoff is incredibly transparent and measured, with fixed parameters and a set end time. You know up front that to get a house for $200,000, you’ll be paying $1000 per month for 30 years and wind up ‘wasting’ $175,000 in interest. (Numbers made up out of thin air and probably not remotely realistic).
Tech debt just doesn’t work this way. There’s nothing firm about it; never do you say “if I introduce this global variable instead of reconsidering my object graph, it will cost me 6 hours of development time per week until May of 2017, when I’ll spend 22 hours rethinking the object graph.” Instead, you get lazy, introduce the global variable, and then, six months later, find yourself thinking, “man, this global state is killing me! Instead of spending all of my time adding new features, I’m spending 20 hours per week hunting down subtle defects.”
While fiscal debt is tracked with tidy, future-oriented considerations like payoff quotes and asset values, technical debt is all about making visible the amount of time you’re losing to prior bad decisions or duct-tape fixes (and sometimes to drive home the point that making the wrong decision right now will result in even more slowness sometime in the future).
Code as a Liability
So in my world, what is tech debt? Quite simply, it’s the amount of wasted time that liabilities in your code base are causing you or the amount of wasted time that you anticipate they will cause you. And, by the way, it’s entirely unavoidable. Every line of code that you write is a potential liability because every line of code that you write is something that will, potentially, need to be read, understood, and modified. The only way to eliminate tech debt is to solve people’s problems without writing code and, assuming since you’re probably in the wrong line of work not to write code, the best you can do is make an ongoing, concerted, and tireless effort to minimize it.
(And please don’t take away from this, “Erik thinks writing code is bad.” Each line of code is an incremental liability, sure, but as a software developer, writing no code is a much larger, more immediate liability. It’s about getting the job done as efficiently and with as little liability as you can.)
An excellent description of something very valuable to use when discussing project progress (or lack of it) to stakeholders. Although it is impossible to put empirical numbers to technical debt I have found that, in practice, the scope of the code incurring debt imposes a higher principal value for that debt and a higher rate of interest as well. In short, code should be reviewed with increasing urgency according to its scope in order to avoid things like “this global scope is killing me”. Technical debt can/should be used as a yardstick to guide developers towards making early decisions about… Read more »
The last bit makes me think of seeing a “this should never happen” message box in some production application. Oops.
If I understand what you mean about correlating urgency and scope, would the ultimate example of non-urgent, “low interest” technical debt be some module that chugged along somewhere with virtually no chance of being modified?
As I consider the interest on technical debt to be compound, yes. The longer the module is left alone the greater the debt and the cost of maintaining it increases.
This could be seen as a theoretical measure of code smell too.
I like the analogy of putting off doing your washing/laundry. It’ll just build up on you.
That’s an interesting one. I might add too that it’s like throwing damp clothes in the hamper and leaving them. The longer they’re in there, the nastier it tends to be when you finally get around to it.