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.