That isn't what technical debt is.

Feb 14

I just read this article about technical debt, and while it is a good article, I really think that it misses a key problem with technical debt that people don't always consider.

First off, let's define technical debt: technical debt is the cost of maintaining and extending code after you've written it. There is no such thing as a debt-free system, because every design and architectural decision you make today costs you something down the road. The only way to have no technical debt is to mothball a system and refuse to fix any problems with it.

What bothers me about Reinertsen's analysis is that it misses this critical point of what technical debt is. In his scenario, he suggests that we compare the costs of bringing the product to market with no technical debt and delaying the launch, versus bringing the product to market faster with technical debt. But there is no way to bring a product to market without technical debt, and there is no way to estimate the technical debt you will incur in new code.

Technical debt is the cost you are paying right now for decisions you made in the past. You will always be paying this cost, the only question is how much. So if you have a part of the system that you can improve in a way that will allow you to create high value features four times faster than you currently are, it makes sense to devote a lot of time to paying that debt. It's going to make sense to spend a month on that fix if at the end of two months you're going to have four new features instead of two, and at the end of three months, twelve instead of three. The benefits to that fix compound very quickly. If it's going to mean that you create five new features in a month versus four, that's a different equation. The benefits still compound, but they don't compound nearly as quickly. A month spent to pay that debt isn't going to start paying dividends for six months. That may not be worth it.

It may be worth it, because the costs of technical debt compound as well. When we build software, we build flawed system on top of flawed system. And the more flawed the original software is, the more flawed the pieces that connect to it inevitably will be. Clean code begets clean code, filthy code begets filthy code. This isn't always the case, code that exists in some untouched legacy part of the system has zero technical debt, regardless of how ugly it may be. But if I have to add code to existing code that is gnarly and poorly written, or if I have to use a fuzzy and complex interface, at least part of my new code is going to be gnarly and ill defined. And now I have more problematic code to interface with.

Which means that it is going to get progressively harder to add new features. It isn't a constant cost, it's an accruing cost. In year one, it's ten percent harder to add software, in year two it's fifteen percent harder, year three thirty percent harder, and so on. Sometimes systems are so hard to fix that the only way to add new features is to scratch the original system entirely and build a new one, because the cost of adding new features starts to outweigh the benefits. That's not a healthy place to be in.

So in the end, as detailed as Reinertsen's analysis is, to me it totally misses the point of analyzing and fixing technical debt. The questions to be asked about technical debt are not questions that you can ask about new features or new code. The relevant questions to ask about technical debt in a system are questions about existing areas of the system. How much is this part of the system costing us right now? How hard would it be to improve it? How much benefit do we expect to realize from this? A part of the system that has a terrible interface that no one is using anymore doesn't cost us anything, and a part of the system with a so-so interface that we use in every new module may be costing us a huge amount.

Another problem I have with this sort of analysis is that it totally misses the mark on what paying down technical debt looks like. Paying down technical debt is an opportunistic endeavor. When you first build a piece of software, it isn't always clear how it's going to used or what you are going to build on it. So, for example, you may make a decision to write something very specific to solve a specific problem. They you might find that you have to write something else very much like the first thing to solve a similar problem. At some point, when you are writing the fourth piece of code that solves a similar problem, you might realize that you have a lot of technical debt that can be paid by taking a little more time to write a general purpose piece of code that solves all four problems, and is going to solve all future problems of the same type. Maybe you know that you have two such problems to solve right now, and so you realize that you can take approximately twice as long to write the better software without losing anything. Or maybe you're expanding a module for the third time and realize that you can improve it to make future expansions very simple. The question of whether it is worth it to make such choices is highly situational. You can't put it (easily) on a spreadsheet because you can't see the future. But you can make educated guesses and work with product to understand the roadmap of the product and focus your attention where you anticipate the greatest need.

The other day, an old colleague of mine wrote me to let me know that a class we wrote about a year ago to solve a very specific problem ended up being flexible enough to solve a new problem more or less instantly, saving the business a great deal of money. We wrote it to be more general purpose because the alternative was just, frankly, aesthetically unnappealing. It probably took an extra three days to write the better code, but not only did I learn a great deal (thus improving my personal productivity and the overall productivity of the team) but the thing ended up being much more testable, maintainable, and ultimately useful. And because we work on software teams, not as individuals, my coworkers were generating new features, some of which were probably pretty quick and dirty, and some of which were built about 4 times faster than they would have been if we hadn't have spent a month fixing a base module.

In other words, I had the luxury to build something of high value the right way because we took the time to pay off some technical debt.