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.”
Tech Debt does not stem from sloppiness or from a tolerance for bugs. You are delivering the best code that you can deliver, given what you know now. You are not deliberately ignoring things about how the code should work that you do know. The released code is (or, at least, should be) high quality, fully tested, zero-known-defect code. It’s wrong, though, because you don’t know something, and there’s no way to know exactly how it’s wrong. You don’t know what you don’t know. You learn what’s wrong by releasing and getting feedback, and by writing more code and building experience.
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.
Also, not retiring your debt as soon as you can will eventually 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.
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. 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.
What about bugs? They are not tech debt, but the subject is obviously related. Bugs are code that doesn’t behave the way you expect, as compared to tech debt, where the code behaves exactly as you expect, but your expectations are incorrect.
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.
I’d strongly recommend learning more about tech debt by listening to Ward Cunningham, who invented the concept. Watch this video.