TL;DR I keep seeing the term "Technical Debt" used as some sort of justification for bad engineering: "we had to pick our battles", "it will be fixed when it's time", "it's what happens to all apps with time, live with that". This is not correct though: there's a very clear difference (of awareness & in-depth understanding of consequences) between these two & what is more interesting - there's also a huge gap between both consequences & price to pay if you want to fix them.
Technical Debt (TD) seems to be a very well known term in software engineering community, but I dare to say that not everyone succeeds in catching the real essence of what Technical Debt is. Speaking plainly - TD is NOT just poor quality architecture / code, it's not an effect of misconception, lack of understanding / knowledge, missing technical leadership, laziness or insufficient experience. All these are just different aspects of bad engineering & result in building profoundly bad products (which is far worse than just having some TD).
Whereas TD is at least partially conscious (though not always voluntary) decision of choosing shortcuts & doing things "quick & dirty", hence speeding up short-term, but quickly accumulating higher inertia & increasing unit cost of making changes long-term.
Of course TD is not always "active". "Passive" TD is about NOT doing something (but again: consciously) for more or less valid reason (usually because there's something that seems more urgent ...), while at least partially understanding the consequences of our passivity.
There's even a nice citation on that, but unfortunately I can't recall either source or author, so the wording may not be 100% accurate:
"Architecture & technical debt contribute to overall engineering quality exactly as unknown unknowns & known unknowns contribute to overall risk of endeavour."
(Collateral) damage control
This way or another, the result is a faulty product. Whatever were the reasons (conscious TD or just bad engineering), it's the same work to fix the shit, ain't it? Nope, IMHO there's a very significant, practical difference:
I like to think about TD as some sort of spot corrosion & about managing TD as a corrosion prevention care. It will appear here or there, sooner or later, you can address it pretty much immediately (in continuous maintenance process) or wait until problem becomes more substantial (& likely - more costly to fix). But as it's supposed to be a conscious decision, you control its SCOPE/RANGE (& quite likely foresee the consequences) - you're not helping it to develop, you're not eliminating it completely, you're just deciding where you absolutely don't want it & where you can contain some.
Yes, managing TD is just about damage control. Moving fast in highly competitive conditions means almost always some collateral damage, so the goal is to have it where you can control it, make sure it doesn't spread freely & address at your convenience.
Bad engineering scales well
Bad engineering, on the other hand, is very different - there's no control, there's no trade: you may not be getting anything substantial for the price you'll be paying in future.
Thoughtless coding acts like a slowly acting poison - initially there are no negative effects, everyone is happy as you're getting the shit done, all kind of consequences can be easily mitigated, worked around, etc. But it's only a matter of time & each successful product has to face the effects of scale. Beware, scale is a much wider term than you may think here:
- it may be about the usage & demand (transactions per sec, registered users, data volume, etc.)
- it may be about required velocity of the change (number of people who work on the product in parallel)
- it may be about product's domain size (demand for more & more functionality - modules, features, etc.)
- or at last but not least - it may be about the increasing complexity of functionality already built in (more variants, scenarios, parallel versions)
Why are the effects of scale so important? Because bad (/careless /thoughtless) engineering tends to scale superbly as well. If you're smart & future-thinking, you can isolate/encapsulate TD within some clear boundaries, so it can be fixed by single, surgical, precise operation. But bad engineering applied across all your functionality may require tremendous effort to fix, e.g. manual changes in ALL your controllers, services, repositories, workflows, activities or whatever you have in your codebase. No-one was thinking about removing it when it was introduced, as there was no awareness then.
The more coupled your code (& bad engineering usually manifests in some sort of coupling) the higher the connascence gets, the more effort you have to put in to have anything fixed.
Concluding: TD is something natural, it appears automatically for every living solution - letting it grow faster for some time may be a perfectly valid choice & can be compensated (with the suitable effort) later, BUT the same rule doesn't apply for bad engineering / bad architecture! If foundation is corrupted - or worse: gets more & more corrupted by continuous application of bad decisions and practices, the price to pay for fixing it may be several orders of magnitude larger.
Pic: © johndwilliams - Fotolia.com