After decades of software engineering, I came to the professional conclusion that technical debt doesn’t exist.
Oh I have seen software rewritten countless times under the pretense of technical debt, doesn’t mean there was technical debt unless “technical debt” is defined as “any existing code”.
Software is unique in the way that everything that was written will be mercilessly rewritten (#refactored) by the next person who inherits it.
It’s very visible and begins as soon as your first project, every intern and young graduate joining an existing codebase immediately feels the urge to rewrite it. It’s a rite of passage in the industry, declare that the existing is shit and the only reasonable course of action is to rewrite it! Codename “technical debt”.
This is meant literally. Reading existing code is difficult -more difficult than writing code- thus it is a genuine take from the developer that throwing everything away and starting over from scratch is “easier” than continuing on the existing. [Ergo this is flawed for non-small projects because the new development will never catch up with the existing during the tenure of the developer(s).]
A Perception Problem in the Industry
One analogy to how software is seen is a mine pit. Every line of code is digging a little deeper.
Eventually the software developer will look back at the project only to be horrified at the gigantic hole he dug [for himself].
Then he will resign to escape from this hell hole… only to start digging up a new one nearby that will become the same soon enough.
Bigger is Better
The process for a mine is conceptually simple:
- Mine deeper
- Reinforce foundations
- Mine deeper
- Reinforce foundations
- Mine deeper
The bigger, the better. While size is not the goal by itself, it is inseparable from the activity. You could look at it in retrospect and size up how much has been done so far.
If it were a pyramid project you certainly wouldn’t think it’s too big, only that it’s remarkable. For reference building Microsoft Windows 7 took more work than building the Great Pyramid.
Mining understands what it’s about. A good chunk of the work is rightfully to maintain the mine in operating conditions -maintain and extend the foundations-, in order to extract more minerals and extract from deeper.
Make yourself a favor and call that maintenance.
Software do not have a technical debt problem, software simply requires maintenance.
The world is changing fast and there can be a lot of maintenance to do. True.
Python 3 changed all the string handling, have to make adjustments. The build is too slow, should make it faster. Security vulnerability found in one library, time to update libraries. Users have moved to using phones rather than desktops, should adjust the site to make it readable. Emails sent to gmail addresses are going to spam, gotta look into that.
That’s a forever stream of maintenance, from the environment, from customers and from every component the software is built upon. There are endless adjustments to make. I’ve written before on the life cycle of software or why products are eventually shutdown.
There is no such thing as technical debt. There is work to do, that we can agree on, but it’s not debt payment.
FAQ: What If?
What if the build doesn’t work and the software is not well tested…
Well, to get the software to compile on current gcc and to add tests can be considered maintenance. It’s a very normal thing to do as far as the development cycle is concerned. Definitely not a case of technical debt.
What if there was no source code…
Indeed it can be challenging to maintain software when the source code (and everything around the project) was lost. That sounds more like it was lost or it was never done in the first place, rather than technical debt. Definitely nothing to do with debt. [Half jokingly, debt imply to be given something and have to repay it, you can hardly be in debt if you never had anything :D].
Real world corollary here. There really are projects whose source code was never stored in the first place, no documentation, no build script, nothing. The word you’re looking for is “throwaway project” because it was meant to be thrown away (very common in contracting). Just one thing, don’t point the finger at your predecessor for it, it takes all parties to tango, neither the company cared to collect any deliverable or to have source control nor the developers cared to submit any work or to setup source control.
To make another analogy, when you bring your car to the mechanic to replace breaks after 80 000 kilometers, he doesn’t proceed to warn that the previous mechanics has left debt’ed break pads all over the place and to question the person’s qualifications. It is regular maintenance really. [However if there were no breaks, the mechanic may question how come there are no breaks and hope you didn’t pay for the car in full –the car was clearly incomplete and non-functional?-, that is still maintenance regardless, gotta (re)do the breaks.]
To conclude on a final word: Maintenance.
All projects begin from scratch and end in maintenance.
Pun intended because maintenance never ends! 😉
15 thoughts on “Technical Debt Doesn’t Exist”
And if you have that code of a throwaway project, but as you might expect it was rushed with lowest quality. Is this debt?
Or just a poor quality on a normal project?
I think someone owes the current developers something. If this is maintenance and not debt, at least from the company, than this would be normal and nothing has to change.
I think if you have software with source code that can be build and run, that’s a fairly normal software project.
If the software is slow and buggy and crashing twice a day, then it could objectively be (very) low quality and not fulfill its purposes, but that’s still not debt. It’s just s*** software, hope the company didn’t pay too much for that.
Whether something will change is up to the company and the developer. Do they want to improve it (maintain), or throw it away, or rewrite it, or anything else.
To make an analogy, when you call the plumber to fix a leak, you don’t expect him to say that your pipes are full of technical debt? That’s nonsense. They are leaky, sure, that’s why the plumber is here in the first place.
There is a thing that exists that many (or at least some) people call “technical debt.” That’s probably not a good term for it, though, because you often don’t know when, or even if, you need to pay it back.
Code encodes someone’s understanding of a problem, and it’s used not only by the computer, but by any developers that need to change that code, whether that be to fix a bug or add a new feature. Code that poorly expresses the problem will make it hard to understand the problem and be hard to change in a reliable way (i.e., without breaking something it does).
From this we can see that poor code in and of itself is not a problem; if it works at the moment, and you don’t need to change it, it’s not doing any harm.
But as a developer, when you dive into poorly organized code, much of your work is going to be to understand that code and the problem it’s trying to model. In the long run you keep only some of that knowledge: a week or a month or a year later you won’t remember everything you learned about it when you were in the midst of hacking on it and you’ll need to re-learn what you’ve forgotten. And of course another developer who is not familiar with your changes, or perhaps even the original code, will have more learning to do.
When you come to an understanding of code that’s not initially clear, often you can change the code to more clearly express the problems and solutions it reifies. It’s cheaper and more effective to do that right at that moment when you have your best understanding of it than to wait until you’ve forgotten some of that and come back to it later, and it’s certainly cheaper to use your understanding now than to have someone without that understanding clarify the code, since that person would have to spend time again gaining that understanding. That’s why we refactor code, and that’s why we refactor it when you have the understanding, not when you don’t. And even doing this is a risk, or more a bet: if nobody ever needs to change or even look at that code ever again, your work is wasted. (This is why I ban “refactoring stories” in my projects; do as much or as little refactoring as is necessary as part of every feature or bugfix story.)
So those cases where someone had to work hard to understand the code, could have embedded that knowledge better in the code, but didn’t, causing someone else to have to re-do that work to understand it later: that extra work the second person (or the same person later) had to do is where your “technical debt” is costing you money.
Not in my experience. Sometimes that happens, sure, but as often as not code is considered sacred and to to be touched, so the next developer writes “around” the existing code, usually repeating some of what the existing code does but with different bugs.
Sure, and often it’s worth letting them do that, in a controlled way. You can learn something about code from reading it, but you learn a lot more by playing with it: try to change it to be organized this way rather than that and discover whether that new organization really captures the problem and solution better than the old, or whether it was going in the wrong direction. I very often, when dealing with new code, immediately start refactoring it. My changes may or may not ever make it off the branch I’m playing on (often they don’t), but the mere act of manipulating it and pushing it around teaches me much better what’s really going on there than simply reading the code.
But of course it’s important to make sure you’re always making small changes. If you can’t produce a useful change to go into the current production system within a day or two, you really need to rethink your approach to what you’re doing. “Scrap it all and rewrite” is a very high risk approach, and you’re almost invariably better off trying to break down what you want to change into smaller, lower-risk pieces. That can be very hard to do in an existing project, especially if it’s one with poor tests, but figuring out how to do that in those situations is where a developer shows some real skill.
For the things you talk about I agree: that is not technical debt. I do think there’s such a thing though: things like quick hacks, shoddy maintenance etc where the (sub)conscious decision was made to write something you know isn’t good enough and to postpone doing it right. Usually because of time pressure.
That is actual debt: taking a shortcut you know will cause more work in the long run.
While I agree with most of this post, this isn’t what “technical debt” is about, and therefore disagree that technical debt doesn’t exist. Technical debt is about a conscious decision in favor of short term gains in exchange for an increased maintenance cost later on, and this is certainly something that happens all the time.
Work coming from changing requirements, better understanding of the problem, better understanding of the tools, etc. is just regular maintenance work, that’s true. Work coming from a conscious choice to cut corners is not. Now, cutting corners is always necessary for any real-world project to have any chance of succeeding, so one can say that technical debt is always present to some degree. It’s unavoidable.
And maybe, like real debt, it’s not always a bad thing. If cutting some corners now, knowing you’re going to pay for it later, lets you get a feature out a month earlier, and that extra month earns you enough over not having the feature for that month, then you come out ahead, don’t you?
I have forbidden in my product manager times the refactoring to make it maintanble after a project/feature etc is done. It just was rewriting the same stuff and re-adding bugs.
The developers where only allowed to touch the code again when they actually had to add a new feature, or there where actually new bugs discovered. Then they could rearrange things and most of the times nobody thought it wise then to redo everything.
Except when enough time had passed and we actually had to throw out everything anyway because the framework changed, the API provider changed…. Etc.
In short… Don’t fix it if it ain’t broke.
If you went to the mechanic and asked him to change the battery, but your previous mechanic soldered the connectors because that was faster than tightening 3 screws, I think they would question the previous mechanic’s qualification…
So yes, technical debt might be a poor choice of words, but “crazy unprofessional hack that might strike back” is a bit too long and “naked call option¹” is a bit too involved. Would you rather call it “botched job” then?
1 – https://www.higherorderlogic.com/2010/07/23/bad-code-isnt-technical-debt-its-an-unhedged-call-option/
LikeLiked by 1 person
Yes, I would rather you call it a botched job and not pay the mechanic for that.
Now, how often do you see this sort of things happening? It’s quite rare.
The common situation is more akin to the first mechanic put torx screws but the second mechanic expected hex screws or maybe flat screws.
By the way I think that sort of comparison is exactly why “technical debt” would better not exist as a term. It’s only creating confusion between small shortcuts, personal developer preferences and botched jobs.
A botched job has nothing to do with debt, it is destructive work that requires to be repaired/undone and done again properly, there is no excuse for that.
LikeLiked by 1 person
But whether it’s “botched job” or not depends on the context. If it’s a car you are pretty sure you’re going to be sending to the crusher in a month, and you just need it working for that month, doing the job quickly in a way that would cost more next time you touch that component might be a sensible risk to take.
LikeLiked by 1 person
What if the original developer had an option to implement code that is easier to maintain but will take some more time to design or do it quickly to meet a target. The developer chooses to do the latter thinking, I’ll get back to this later. Every time a new developer comes in to maintain this code they spend a lot more time now trying to understand the original intent of the code flow before modifying it (Interest paid in every maintenance cycle). This time/ technical effort saved, which needs to be paid to make the code more maintainable has never been paid still. Somehow the phrase to explain this situation escapes me, must be some sort of debt I’m guessing 🤔😋
I like the nomer “maintenance”, because “technical debt” always has a feel of absoluteness to me. In the sense that if something is seen as not fitting requirements anymore, it’s labeled as technical debt. While at the same time, it should be more dynamic: let’s say that a system is storing pictures as png’s instead of jpg’s. Then this is labeled as technical debt and should be rewritten to use jpg’s, since that uses way less disk.
However, let’s say requirements change and some client of the system absolutely needs png’s to be delivered. Then at that point this technical debt suddenly changes into a feature.
TL;DR: The label ‘technical debt’ is too absolute.
As various others have already suggested, I don’t think what you describe above fits the intent of the Technical Debt metaphor. Sadly the term has often become analogous with shoddy workmanship instead of being a conscious decision to take a shortcut for a short-term gain. The following blog post was my attempt to document a real world example to highlight the conscious element in the term:
Technical Debt – Conscious Competence
No, I think that there are plenty of cases where technical debt was not consciously chosen for some good reason but is simply shoddy workmanship. A typical example I saw comes to mind.
We had a system that did some web-based human-driven input and output in a grid format, with a form with labeled cells that the user would fill in and submit. A requirement came along to do something similar via Excel spreadsheets for the same data. The developer who built it didn’t like or didn’t understand the “grid/labels/etc.” parts of the current code in the back-end, and so basically wrote a complete second grid/labels/etc. system in the back-end for use with the Excel part of things. So we ended up with _two_ sets of grid/labels/etc. code, both handling forms and input for the same system, each with different sets of tests (where it was tested at all) and its own set of behaviourial quirks and bugs.
I don’t know how much that ended up slowing down overall productivity over the subsequent months and years, but it’s a clear case of where someone should have taken the effort to clean things up, and instead left an even messier situation that wasted programming time down the road.
You’re right that replacing brake pads isn’t technical debt. Technical debt would be what happens after you *don’t* replace your brake pads when you’re supposed to, and then also don’t replace them before they start to damage the rotors, and then also don’t replace them once they start to impact braking performance. It’s the liability that comes with all those decisions. This is something that certainly exists in software development as well.
LikeLiked by 1 person