Technical Debt Is Cholera, not Quicksand
Technical debt (TD) – the compromises we make to get stuff out the door – is more often acknowledged and occasionally quantified. We categorize the inefficiency and difficulty working with it—the “interest” on the debt—as an unfortunate cost. Fixing technical debt (paying down the “principle”) competes for resources with new development and tangible defects
The common TD horror story is changing the behaviour of badly-coded modules1. For example, spending hours adding a simple variation to an existing implementation, which has never worked consistently (and never been fully tested – let alone built for extension or reuse), is dangerous as well as frustrating. Without tests or clear documentation, we are never sure what good behaviour is, so extending that behaviour is often an exercise in faith2
In analyzing technical debt, we tend to mythologize it:
- Technical debt is something waiting to trip us
- Technical debt a minefield to be crept through until it’s cleared, if not avoided entirely
- Technical debt is there, and if we don’t go there, it cannot hurt us
- The longer we can avoid fixing technical debt, the less it costs
Poorly-written code and poor design is not just passively bad (causing more work only when we work on or nearby it); it is actively bad as it infects new, unrelated code as well as infecting developers themselves
At the integration level, we are forced to accommodate existing TD by pandering to its non-standard parameters, side effects, etc. when we interact with it. If it does not participate in normal error handling, writing tests and investigation test-failure are likewise infected3. The calling code must be malformed to match the bad interface, and we must document that knowledge for each bad interface or relearn it over and over
At the introductory level (barring specific guidelines and approved examples), starting developers will use bad code as a template as easily as good code because they have no way to know the difference. The bad code uses these developers to reproduce itself the same way viruses use cellular reproduction. As they learn to tell good code from bad, developers will see that their organization tolerates it and know that the next generation of engineers will face the same risks
As an engineering team agrees on coding standards and improves readability and quality, the old code — while just as functional — becomes harder and harder to understand4. In that sense, it degrades
At the systemic level, TD dulls developers. The longer they live with bad code, the more we become attuned to allowing it. We might consciously or subconsciously choose to avoid fixes and enhancements in TD areas because it’s easier and safer. The technical debt areas become unattractive even if they would normally be the best place architecturally. Developers who do not understand the TD, either where it lives or its danger, will think the architect is a fool. Developers who understand the reasoning will grudgingly agree with the previous choices, but not happily
And at the organizational level, TD makes developers frustrated either because they have to work with it (and it makes them feel dirty), or because they are not allowed to fix it
Bad code also decays as familiarity with its (awful) internal and design fades. Even if it is replaced completely, someone must read it to understand what it does5. The longer it remains outside of the idioms and standards of quality of the rest of the system, the farther it will move from comprehension
We cannot fix all technical debt. We need to evaluate the cost of fixing it compared to new features, new products, etc. However, we need to evaluate all its costs, not just the mythological ones
-
Good code is all alike; bad code is bad in its own way ↩
-
or hubris ↩
-
perhaps technical debt is more like zombies ↩
-
Programs must be written for people to read, and only incidentally for machines to execute.- Abelson & Sussman, SICP, preface to the first edition ↩
-
Because the code is often the only documentation ↩