Here’s another transcription from a Twitter screed:
I think I’d like to share some thoughts on “technical debt”. I’ll try to say things that I mostly believe.
I believe that the term “technical debt” originated with Ward Cunningham, who described how a system grows in complexity as it grows in capability.
Even if we keep the design as clean as we can manage, our understanding of what the design “should be” grows and deviates from what the design is. Ward described how, sometimes, when we put that improved understanding into the actual design, the system improves.
There should be no surprise here: we’ve mostly all seen systems with designs that don’t support their requirements well, and I hope most of us have seen systems whose design does a great job of supporting the system capabilities.
The difference between what the design actually is, and what it could be now that we know more, is what Ward called technical debt. (Assuming, of course, that I understood him.)
So let’s work with that notion. Consider a design that is “just right” for the system’s current capabilities, versus one that isn’t. Let me give an example. Suppose we created a budgeting or similar financial system, and suppose we represented dollars and cents as floats.
This will work pretty well for a while. Once in a while we have to do some special rounding or bend over backward to get something to work. As the system gets more and more capable, those adjustments become more and more troublesome.
Sooner or later, we realize that it would have been better to have used an exact monetary type that could represent dollars and cents. We might decide that basing that type on a long integer would have been better than using float.
(There would still be interesting adjustments needed with an integer-based type, but there’d be fewer. Just go with me here.)
The difference between our newly-imagined design and our existing one is technical debt.
Every feature we build takes somewhat longer to do, because of the care we have to take with the pennies. Worse yet, often a new feature will require us to make adjustments elsewhere, perhaps adding in a bit of lost round off or something.
If we had our integer type, all our new features would go faster. We want to reduce that “technical debt”. We argue for switching to a new integer-based data type.
Would it be worth it? It’s very hard to say.
There are at least two unanswerable questions here:
- How long will it take to convert to integers? (And how many bugs will it create?)
- How much faster will we go after this is done?
Now, experience tells me a little something about this:
If we have at least encapsulated our floats into a class or type, “Money”, it’ll be easier than if we’ve just been passing pure floats around and using them as money or percentages or other things.
In the latter case we have a huge detection task ahead of us. In the former, we can do most of the work inside the Money class. The conversion is more easily justified by encapsulation. The “technical debt” is less, because we have a generally better design, even flawed.
This example teaches two lessons, in my view. First, we are very likely, as time goes on, to see better ways to have done our work. Second, if we keep the design we have as clean as possible, adjusting to those better ways will be easier.
Now you know, and I know, never to use a float for money. But there is something we don’t know now about the next system we build, and someday that thing will be as obvious to us as “NEVER USE FLOATS FOR MONEY” is today.
So the second lesson – keep the design as clean as possible – is critical. With a clean design, design improvements are generally easy. Modularity works. The more messy our existing design, the less likely we are to be able to improve it even after we learn how.
Unfortunately, today, “technical debt” has often come to mean something like “judiciously skimp on design today, so that we can go faster; we’ll clean it up later”.
In my view, there is no useful meaning for “judiciously skimp on design today”. Every design flaw we leave in the system today will slow us down tomorrow.
I don’t mean “in the future”. I mean, literally, tomorrow.
Our design will inevitably deviate from the best design we can imagine, because we’re learning as we go. Our chances of moving into that better design are much greater if the design we have is as clean as we can make it.
On the other hand, a “quick and dirty” design is mostly dirty and rarely quick. Mind you, that doesn’t mean we can’t just use a simple solution now, and put in a more robust one later. We can do that: but we need to put the simple one in cleanly.
Arguably, our simple solution should be put in more cleanly, because it is more likely to need to be taken out.
To go fast, we have to go clean. Dirty code isn’t technical debt. The surgeon can’t usefully save time by only sterilizing some instruments. We can’t usefully save time by writing sloppy code. That’s not technical debt. That’s sabotage.