As Dijkstra once said in his famous "radical novelty" talk, analogies and metaphors don't always seem to work for software. (Actually, it's fair to say his opinion was somewhat stronger than that, but we'll leave it there.) Although very useful, analogies and metaphors can break down. This is generally true but seems to be especially true with software.
Is creating software like building bridges? Is it like designing a city? Is it like running a restaurant? Maybe it is like all of these things in some respects. However, there's something very unique about software which is hard to pin down - and which makes it hard to pin metaphors to.
I love analogies and metaphors and use them all the time. They are a great way to reason about fundamental aspects of software systems, and the creation of those systems. I also appreciate it when people who write and talk about software use them. Anything that helps humans grasp the difficult concepts and intricacies inherent in software can surely only be a good thing.
But they can also be dangerous. This is at least partly what Dijkstra was getting at.
Take the widely used metaphor of "technical debt". Great metaphor, easily understood, and really conveys the issue. Or does it? Can it be possible that this is misleading, or misunderstood, or perhaps both?
This metaphor, in what seems to be the most prevalent understanding of it, says that the kind of "cruft" (as Martin Fowler calls it) which builds up in a software system can be thought of as debt.
Martin Fowler defines cruft as "deficiencies in internal quality that make it harder than it would ideally be to modify and extend the system further".
The crux of the metaphor is that this cruft is debt that must at some point be repaid if we want to keep the cost of adding new features or maintaining the code low. If the debt isn't repaid, the cost (meaning the effort) remains high - and the difference is essentially the "interest" on this debt.
At least, this is the crux of it according to what seems to be the common understanding of "technical debt".
Does this metaphor make sense? Is this even how it was originally intended?
Many people would think of cruft as bad code. Messy code. Perhaps code that is hacked in, or sloppy. Never mind that the definition above doesn't say that exactly - this is what generally springs to mind.
Organisations that suffer from code mess will sometimes comfort themselves by calling it "technical debt". By thinking of it as a debt, it seems like something that can just be paid off at some point in the future, and it doesn't really affect the day to day. After all, in real life a debt is often due to an investment which makes your life better. For example, a new house. And even while you have the debt, your environment can be absolutely fine. The conditions in which you have to exist are totally OK. Yes, you have a debt. But you also have a nice house, and you're paying it off. Meanwhile everything is rosy.
So, with this kind of thinking, you might say "well, we have some technical debt now because we really rushed that feature. We’ll have to pay it off some day". And the danger is that it just gets deferred and deferred. The point that is often missed is that, if the "technical debt" is actually just messy code, then the interest element is essentially due to the deteriorating environment (the codebase) in which the software engineers need to work. It's a codebase starting to disintegrate. Features will take longer to put in. When they are in, they invariably have bugs - more bugs than there would have been if the codebase was clean and well written. Regressions in other parts of the system are more common, because it is so entangled. Further tickets need to be put into sprints to address these issues, which are wasted on fixing the inevitable problems rather than adding more features and more value. Releases can even end up going out with problems which aren't picked up.
Software engineers suffer when having to deal with this sort of situation. It's not really like a debt. It's certainly not like living with the sort of debt where your immediate environment is totally unaffected, and where the only way you really know you have a debt is some uncomfortably high numbers in red on your bank statements.
This kind of situation, I would argue, is more like an unsafe structure. A hazardous work environment. It's like a building site littered with broken tools, broken scaffolding, and maybe water logged for good measure. In short, it's not good to work in, and it's not good for productivity and profit.
The original "Technical Debt" metaphor
The thing is, this simple idea of "messy code which we know needs cleaning up" is actually not what was meant by "technical debt" in the first place.
The metaphor was first coined by Ward Cunningham in 1992.
As far as I can tell, messy code was far from his mind when he came up with this idea. As I interpret it, it is a way to describe or justify why, when designing and delivering a first incremental release of the system, certain design decisions were known to not really be sustainable. Rather than delaying release, reworking the design and fixing these flaws was deferred to later.
This, to me, is the essence of true "technical debt". It's a deliberate decision to invest by getting something released while knowing full well that the design is not quite right. It's the same kind of advantage as when you take on a loan to buy a house. In that case you get to live it in sooner than you would otherwise be able to. And in the software case, you get to release it sooner because you incur the debt of needing to sort it out afterwards. It is not about sloppy code, but about issues with the current iteration of the design. Issues which you're fully aware of.
As Cunningham himself said "I’m never in favor of writing code poorly, but I am in favor of writing code to reflect your current understanding of a problem even if that understanding is partial."
After delivering that first (or second, or third) incremental release the team knew that, at the very least refactoring would be necessary, and almost certainly some redesign. If this was not done then the cost of further work would stay high. This, in the analogy, is the paying down of the debt.
This seems to be to be a totally different ball game. It's deliberate, and thoughtful. It's a tactic by which software can be put into the hands of users earlier, perhaps to get valuable feedback or to gain an advantage on competition. It's not accidental complexity and mess due to continually rushing work on an ongoing basis.
It's an acceptance that your first stab at a solution will be imperfect, and is a considered and thoughtful way of getting a system out of the door in a reasonable timeframe with the caveat that some subsequent rework or refactoring will need to be applied to keep it all going.
And in contrast to the popular idea of "technical debt" the code is still good, and well written. Concerns are separated, tests are in place. The code itself is a "habitable place" as Dave Farley likes to say. The only problem is that the fundamentals of the design don't quite sit right. That's the debt.
"Technical Debt" from another angle
To visualise this idea, here's a simple graph.
The benefit is that productivity should increase whenever you pay down the debt. The costs of putting new features in decreases, because the new insights gained from the first iteration have been applied to the design.
I think another way of looking at this is to think of it as regular investments.
The investment in applying design changes and refactoring with the benefit of the knowledge gained helps increase work throughput and the overall quality. Regular investments maintain this quality and keep the design aligned with the actual problem being solved. These investments cost time and effort, but pay dividends because of the increase in productivity, quality and throughput.
The basic principle doesn't change, but the analogy is different. Thinking of it as an "investment" emphasizes the benefits and is, I believe, a much more positive way of looking at it.
Whichever way you look at this though, it certainly isn't allowing the creation of an unsafe or hazardous environment, i.e., a total codebase mess, which can make a software engineers life very unpleasant and be very bad for business.
The trouble with the "technical debt" metaphor is that it seems to have taken on a life of its own. It can be used to justify living with code mess, and big balls of mud, because in the real world we can live with debt. As I've tried to argue, this isn't "technical debt". It's more like a "technical wasteland". The misappropriation of a metaphor leads to consequences. You can live with debt for some time, but who would want to live in a wasteland? And how can you make progress in it?
The original "technical debt" idea is good, but I do think it's worth considering flipping things around and seeing it instead as "technical investment". Either way it is potentially a very valid strategy, but under the name "technical debt" it's so easily misconstrued.
I like to think that this is the kind of problem Dijkstra had in mind when he gave that talk all those years ago.