Technical Debt
The notion of technical debt is not well understood, so let’s start there.
Agile is all about fast feedback loops—hours or, at most, a couple days. Deliberately lengthening your feedback loop in pursuit of the chimera of perfection destroys your agility. The cost of that delay is a real cost. It usually exceeds development costs by a large factor. Deliberately delaying a release as you pursue perfect knowledge is IMO irresponsible. It’s literally so expensive that it can destroy the company.
That’s where the notion of “Technical Debt” comes from.
Paradoxically, trading perfection for fast feedback usually gets us closer to perfection (or, at least, very good) faster. By going into debt and then quickly paying that debt off by learning what’s wrong through feedback and then acting on that feedback, we get to a better place in less time.
Tech debt accrues naturally as you write your code. Managed correctly, it works like a credit card. You pay off the card every month. If you don’t do that, the debit increases to the point that you can’t pay it off. You are buried under the interest payments. In Agile, the equivalent of paying off your card is refactoring to incorporate the things you learned by releasing. When you see something that’s wrong based on what you know now, fix it. If you don’t “pay off the debt”—don’t eliminate the hard-to-work-on code that doesn’t do what our users need—you’ll be buried under the “interest.”
Once you learn, you pay off the debt. To quote Ward Cunningham, who invented the notion of technical debt: “[the team changed the code] so it looked like they knew what they were doing to begin with.” (I strongly recommend watching Ward’s video on tech debt.)
Tech debt also accrues because we tend to learn only what’s needed to get the code to work. It is technical debt to learn that you’re not using a newly-learned library the way it’s supposed to be used. That’s not a bug—the code works as you expect, just not as well as it could. That’s another sort of learning problem. Fix it like any other technical debt.
Let me repeat this because it’s important. There shouldn’t be any known bugs at release and you should fix other bugs the instant they appear. Tech debt is not sloppy, badly written code. There is never an excuse for that. It slows you down to the point that agility is impossible. You cannot move fast enough if you can’t trust your code. The notion that you have to write sloppy code to get it out the door faster is a costly myth (to borrow a phrase from Deming). A plethora of bugs is not “tech debt.” It’s incompetence. It destroys programmer effectiveness.
Since bugs are really a separate topic from tech debt, I’ve discussed then in another post.
Whether or not to retire the debt is not always a straightforward decision. Sometimes, it makes sense to carry debt. (You don’t pay off your house loan a month after you get it.)
On the down side, not retiring your debt can bury the company. Nokia died under the weight of its tech debt (a technical architecture that wouldn’t let their developers make changes fast enough). Tech debt leads to things like build times measured in days. Agility is not possible in that sort of environment.
On the flip side of the coin, tech debt is not bugs. The code works. It’s high quality. It could be better, but maybe there’s something more important to do with your time than improving working code. You probably don’t want to put that off forever—changes take longer the more you delay them—but there may be no need to retire the debt immediately.
So, it’s okay to deliver, knowing full well that you’ll learn over time, and that you will need to refactor the code to leverage the things you learn. That’s basic Agile thinking. Technical debit is a good thing. It means that we’re learning.
Conversely, not refactoring—not paying off the debt—is an extreme form of dysfunction. It slows you down too much. That’s not Agile by any definition I know.
Can you give some examples of technical debt to sharpen your definition?
Examples vary from trivial to huge. At the huge end, you may have an N-tier architecture that just doesn’t work in an Agile environment (google Conway’s Law) and the fix is going to a microservice architecture. At the smaller end, you may have gotten a detail of a story wrong, and when you sit down with your users to review the implementation, they either tell you or you see them struggle. Also included in the notion is something like code that works with a library to do something, and you’ve now learned that you’re not actually using the library properly and there’s a better way to do the same thing, so you refactor that code. It’s all about learning.