Stop Saying “Technical Debt”
stackoverflow.blog
stackoverflow.blog
Calling it “bad code” definitely misses the mark. IMO, it’s code that constrains you from doing things you want to do. Tech debt is almost always code that is tightly coupled to other code, which is why getting rid of it or working in codebases with a lot of it can be difficult.
The key is that (again, like financial debt) you need a plan to pay technical debt down. If there’s too much, you start drowning in it. And not all of it is a choice — sometimes things unexpectedly change and current code becomes a liability. But framing technical debt in architectural decisions as a tool and a choice has helped me understand better how to accomplish business objectives as an engineer.
There is always the urge to build something "the right way" where it's fully modular and extensible, but if you never need to extend something the additional layers of abstraction end up being more of a hindrance than a help.
There is a certain level of efficiency in building things the cheap and easy way by default and only rewriting the parts where it was actually a problem.
I've seen developers reduce technical debt in a way that others consider to be an increase in technical debt.
One developer can add abstractions, modularity and extensibility to a code to reduce technical debt.
Another developer can strip out unneeded abstractions, modularity and extensibility to reduce technical debt.
Often noone does
Defaults exist. You don't always have to pay financial debt, and the case you described for technical debt is one of the main cases when people don't pay financial debts either.
IMO the biggest difference is that the carrying costs of technical debt are quite random. But people say that all the time already, and it's well known that they behave more like generic liabilities instead of debts in particular.
Every database, application, and workflow in the company has to deal with possibly 8 different part numbers when referring to a release of software. From the mainframe, to the PDM to the ERP to every single ad-hoc Excel spreadsheet used by every department to help manage the complexity.
Most of the parts are shared across various builds, with 2 or 3 of the "subfiles" being different. So, in a release of hundreds of various custom softwares for a particular hardware product, there are hundreds-times-5-to-8 subfiles released. Each software must be broken apart, checked for commonality, and rebuilt using the common files, and then checked against the base build to make sure that it rebuilt correctly. Each release of software requires LITERALLY DAYS of work to do this, even with Excel macros.
I wrote software to automate all of this. It took a half an hour, with NO manual intervention. It was strangled in the crib.
WHY do we do this? Before WAN's became de rigueur, we took our software to the computer on the production lines that programs the embedded computers on floppy disks, and the software had to be broken up into 8 parts so that it would... fit on a floppy. We operate with a built-in technical debt that hasn't been needed for 30 years which would literally break the company to address. It's virtually unseen at this point.
If there's a worse example of "technical debt," I'd love to hear it.
> I wrote software to automate all of this. It took a half an hour, with NO manual intervention. It was strangled in the crib.
Can you say any more about why management at your company decided to strangle this project in the crib? What exactly happened? If you tried to pushback, how did they shut you down?
If you have time I'd also be curious to hear an outline of how your solution fixed things.
I wrote an application (in engineering) that did a thing that everyone loved. It had a thousand internal users. Another group (in IT) thought my project was their domain, and tried to kill it. When they couldn't, they staffed up and wrote their own version. I wrote mine in a year and a half; it took them 10 people and 3 years. Their version sucked so hard, no one would use it. They hired Microsoft to help them make it better; they failed.
In frustration with the situation, the IT manager managed to replace my boss' boss with someone he had mentored into the company, and lo and behold, we were told to stop working on my project. We made a patch to fix a bug, and they feigned outrage, and pulled the plug and made us hand over my application, which has now run for about 6 years without update, while they try to force more people to use their piece of junk. I hear they've gotten most people transferred now.
Right about the time the management change was happening, my boss and I were starting to go off in several directions of other useful things we could do with the data. One of them was the function I mentioned. It was a very manual process, but very scriptable. I created a message queue in Azure, to "release" the job, and had another machine (with a proprietary program this process needed) pick up the message and just crank through it. I had just gotten that done when they pulled the plug, and it was never used in anger.
A lot of times, people tell war stories like this, and you think, aww, come on; it couldn't have been THAT bad. But this bad enough that the CIO called the IT manager to a special meeting about it, and he was reassigned to a lead a new group. You're free to speculate if that was a promotion or a chastisement. My boss thinks it was the former. I'm not so sure.
It reminds me of William Binney and ThinThread (https://en.wikipedia.org/wiki/ThinThread). (ThinThread was a system developed by a small team at the NSA that basically performed more "targeted" intelligence gathering, rather than the vast data hoarding that was described in the Snowden leaks. Naturally, bureaucratic inertia favoured the less efficient, more expensive, more privacy-invading system.)
Is organizational or technical debt good for the company? No. Is it good for the individuals creating it? Oftentimes yes, they never have to pay it back, they get to hire someone to deal with the mess instead.
While technical debt in itself is not a good thing, choosing to take on tech debt in return for delivering quicker (getting faster feedback/not investing in an implementation too early/not over-architecting before you have real-world impact) can certainly be the right choice for the organization as a whole.
I don't buy the argument that tech debt is purely a selfish choice for personal benefit.
If startups were staffed only by experienced workers, some aspects would go more smoothly, but costs would be much higher. Arguably YC's value prop to downstream investors is its ability to find superb teams early and invest when doing so is cheap.
It's really surprising how often the “quick” approach turns out to be slower than the “right” approach.
Now sure, there's such a thing as focusing too much on non-market relevant aspects of a project, or going down rabbit holes, or designing and coding for a future that will almost certainly never come, but it still continues to surprise me how often when the choice is between "quick" or "right", that "quick" actually turns surprisingly soon into slow.
There's a saying apparently in use in the military "slow is smooth, smooth is fast". That's the kind of wisdom we need to aim for in software engineering. "More haste, less speed".
Oh yeah, the other difference is that there's no external force that will take you to court if you don't pay down technical debt, so in fact most organisations are terrible at paying it down. If you don't literally have a plan on the calendar with names and dates committed to improve the code, you're most likely fooling yourself by calling it technical debt. The true metaphor is much closer to 'Superfund site'.
It depends on what is done fast and what is done more thoughtfully. For example, if the general architecture is thought through carefully, individual modules can be built more hastily and later rewritten. On the other hand if the architecture is unsuitable for the problem, then it matters little how well tested or high quality any of the code is, and overdoing code quality will be a waste of time.
It comes down to judgment and tradeoffs (like everything in engineering). I like the concept of tech debt because it is a way of describing a class of tradeoffs.
And it's really surprising how few people realise that, even if the "quick" approach is slower in the long term, time is not always worth the same!
Time just before release is precious. Just after release, instead, you are both tired anyway and still have your code in your head, so that is the right time to refactor.
In the end, technical debt, code quality, and whatever code metric... they only make sense for code that has been shipped. The code you are refining is worth nothing if users are not already using it.
I have often seen developers weigh up doing it 'right' or doing it 'quick', and the 'quick' solution turns out to have more complexity than originally anticipated, and take a lot longer than expected. Sometimes the reason the 'quick' way seemed quick was just because the problems with it were less understood.
The 'right' solution generally is harder, but the 'right' solution is better understood, and people can be surprisingly productive on things they understand and believe in when they knuckle down and get to it.
The best “quick” vs. “right” decisions I’ve made (and this is not a binary) have involved 1) complete plans for both implementations, 2) a plan to migrate from “quick” to “right” and sometimes 3) a plan to remove the “quick” implementation if the feature turns out to be unneeded. It involves more upfront planning work, not less. 3 is particularly useful because it forces you to come up with the loosest possible coupling between the new feature and the existing system.
That really only applies to "we have to launch it next week". In vast majority of projects longer than a hackathon doing even slightly better (let's say extra 20%) pays off very quickly. Because there is plenty more shades between "let's do that in immediately simplest way with zero thinking" vs "let's build our own DB engine to handle the problem". With exception being maybe "we're just prototyping few approaches to the problem"
The "but we need to be agile and launch fast" is more often excuse than legitimate reason.
It can potentially apply to any project with a fixed budget, whether that's internal or external. And time is money, sometimes quite literally if you are working in the agency world. Customers will choose a project with a $50k budget versus a $80k budget even if smaller budget will be significantly worse, as long as it meets one of their basic needs (or even if it doesn't, if that's all the budget they have to work with).
For example our call center needed staff availability surfaced in the CRM for routing calls. This required data from the HRMS and the individual's work calendar. We knew the HRMS system was being replaced in Q4 of this year, but the project timeline dictated this feature would release in Q2. Thus we have a tech debt item added to the backlog to refactor this integration when the new HRMS system is launched.
Another example that comes to mind happened a few years back. We had a product using a framework that was falling out of support. That itself was naturally emergent tech debt.
When the tech debt item was finally added to a sprint, the upgrade options were to a LTS version or an STS version after that. The feature that had delayed the tech debt for so long had been refactored again for the STS. The decision was made to skip the LTS version in favor of the STS version because it would save a huge refactor in the future. However being an STS version, it necessitated another tech debt item to upgrade to an LTS version in a year.
Your first one is just a new requirement (to integrate with a new system) that you can see coming down the road. You didn't make a decision that took on any 'technical debt' there.
Your second one is that the framework you were using fell out of support. Unless you had previously decided to use an old version because it would allow you to release earlier, you didn't take on tech debt here, you just needed to do some work to stay on a supported version of the framework.
Your last example where you chose to go to an STS version instead of an LTS version might be a good example, since you made a decision that forced you to do some upgrade work in the future sooner than you would otherwise have had to. On the other hand, it doesn't seem that you took on technical debt in making that decision, if anything, your tech is probably better and less indebted than if you hadn't. Perhaps I'd say you took on schedule debt or something else there.
I suspect that you work for an organisation where anything that isn't a customer requested feature is considered 'tech debt', but I think that's a too wide use of the term.
I'm defining tech debt as a decision that expedites short term delivery but creates a need for future work.
The first case was deciding to deliver a product sooner while knowing that decision would create necessary work in the near future. The second wasn't properly clarified but the version of the framework in use was STS.
I suppose it's up for debate if routing maintenance is tech debt or not. I'm of the opinion that it is.
> I suspect that you work for an organisation where anything that isn't a customer requested feature is considered 'tech debt', but I think that's a too wide use of the term.
Yarp.
I think it's a great term.
This is why we adopted the concept of technical debt, the term encompasses much, more more than just "bad code."
So I say strongly - use the term. Don't let it be a synonym for bad engineering, but for choices.
Financial debt would be just as much of a mess if you couldn't count it.
Somehow people think that if only we could explain what it was to people who don't get it the problem would be solved.
Nope, just need to be able to measure it and stick a price on it (even if it's not much better than a guess) and let it be put it in a spreadsheet with all the other liabilities.
The perpetual attempts at redefinition "so they get it", on the other hand, are all gonna get ignored.
Alternatively we have 'best practices' (ie someone with an important title somewhere wrote a blog post about it) and 'code smells' (so ... the code makes your tummy feel bad when you look at it, yeah I'm sure that's objective).
There are a few corners of the web where people have been trying things, but I don't know if I'm convinced. It is however better than nothing.
https://www.sonarsource.com/docs/CognitiveComplexity.pdf
https://www.progsbase.com/blog/flow-charts-of-programming-la...
I don't think our industry is going to make any headway into a healthy way to deal with technical debt until we can talk objectively about the coding constructs that we're building. There's too many situations where people are conflating familiarity with objective quality. And even when you get past that the politics of having your coding philosophy win out means you can't trust enough people to honestly approach the issue.
EDIT:
Additionally, you have to worry about the external politics involved. If vendor A says the feature takes 3 months, but vendor B does the feature in 3 weeks, then it it's pretty clear who the customer is going to want to go with. That is until three days after launch when the module vendor B wrote keeps formatting end users' hard drives.
OR MAYBE vendor A are just really bad at their jobs and their 3 month version would be the one formatting hard drives. Those who want software written do not have good metrics for how hard anything actually is.
Man, I wish this didn't resonate. I once worked as the lead at a company that decided they wanted to build a mobile app. This wasn't something I or the team had done before, but after researching and considering the features I gave an estimate of 6 months for us to get up to speed and deliver a working app. They brought in a contractor who said 2 months, working beta in 1. I pushed pretty hard against this but lost. 2 YEARS later the contractor finally delivered an app they declared "done". It technically worked but was slow as molasses and crashed constantly. My team triaged the worst of it and got it to a semi-usable state before getting approval to build a replacement. It took about 6 months.
The differences that were mentioned to "types of tech debt" are similar to the parable of the blind men describing an elephant. [1]
A group of blind men heard that a strange animal, called
an elephant, had been brought to the town, but none of
them were aware of its shape and form. Out of curiosity,
they said: "We must inspect and know it by touch, of which
we are capable". So, they sought it out, and when they
found it they groped about it. The first person, whose
hand landed on the trunk, said, "This being is like a
thick snake". For another one whose hand reached its ear,
it seemed like a kind of fan. As for another person,
whose hand was upon its leg, said, the elephant is a
pillar like a tree-trunk. The blind man who placed his
hand upon its side said the elephant, "is a wall".
Another who felt its tail, described it as a rope. The
last felt its tusk, stating the elephant is that which
is hard, smooth and like a spear.
[1] https://en.wikipedia.org/wiki/Blind_men_and_an_elephantThe thing I like about the term is that unlike general "bad code," a company chooses to incur tech debt. The quintessential example is that a deadline looms, and doing a thing "right" would mean pushing the deadline, but taking a shortcut would mean hitting it. As an organization, there is a choice to be made, in much the same way people generally have a choice when incurring financial debt.
Of course, it's often not a simple choice, which is why both financial and technical debt are so common. But in both cases, the choice was made to gain benefits now in exchange for a higher cost later.
It’s a useful reminder that it’s there to use as one of the many tools in our toolbox.
How about you don't tell me what to do.
Convince me.
Show, don't tell.
The same can be said for "stand ups" in which most people sit down, and for "agile" which usually doesn't involve the practices that produce agility on a team.
For me I like the term "technical debt" because it succinctly describes a complexity cost that accrues interest over time. It doesn't simply mean "bad code". You can work with that cost to prioritize more important work however eventually if you don't pay it down, it will catch up with you and you will be spending your time and money on paying off the interest rather than investing in your features.
Another term I like to use in tandem with "technical debt" is "technical investment". Technical investment is spending time and effort on things that will return a benefit over time, either reducing technical debt, or enabling more features.
Which is I think why most people who have been in the industry for a while have a “good enough” feeling towards getting something done. Yes, you should put in an hour of work now to not have to put in ten hours of work tomorrow. But there’s no point in trying to make something perfect and debt-free for reasons beyond time efficiency. The march of time will cause a maintenance debt to accrue regardless, the difference between debt free and good enough disappears with time.
The latter does allow progress, the former inhibits it.
Perhaps then managers will learn ...
Failing to do so creates code that is very difficult to maintain or for someone new to the codebase to understand.
However, time and effort may not be what the organization wants to pay for, and individuals may view their own incomprehensible code as something like job security, as they can't be replaced by someone else easily.
As an example of complicated code that's still well-documented, the open-source sqlite code is a good example, about 1/4 of the B-tree file is comments, every time a variable is defined there's a short note explaining what it's used for, every function has a comment header that's comprehensive, such that someone new to the codebase could construct a map of how it all works fairly quickly. It's a good model for how to avoid the problem:
It is most often used in my experience not to hurt the feelings of Vice Presidents, while conveying the problem to those in the know. (/s)
For a first pass approximation it is.
The post is a soup of manager-speak and blogger-speak. I might be too hungover to decipher it properly, but I'm pretty sure it's saying: go back and fix the places you scarified quality in the name of development speed, but only if fixing it will unlock more development speed. (And don't let those shifty fucks sneak in a refactor that doesn't bring any development speed RIO).
1. Tech Debt is a meaningless term
2A. Product/Management don't know the platform intimately, so sometimes developers lie and call things with no ROI tech-debt, or make up issues to explain missing deadlines
3AA. These engineers are intentionally dishonest have an antagonistic relationship with Product/Management and see them as task-masters.
4AA. Product often positions themselves as though they are "in charge of" engineers, and that they exclusively "own" the project.
5AA. Everybody likes to see themselves as the most important part of the company. Engineers think they built everything, product thinks they built everything, founders think they built everything, sales think they made all the money.
3AB. These engineers don't feel like they have an ownership stake in the product anymore, and take little pride in the product itself instead of pay/politics.
2B. Product/Management don't trust developers to accurately/honestly assess what is genuine time-saving tech-debt.
3BA. Some developers do lie (see 3AA)
> The thing is, those bugaboos rarely intersect with the code’s most pressing maintenance challenges. So when each engineer finishes their gang-of-four-fueled refactoring bender, the code is no easier to work in than it was before: it’s just different
This gets rehashed a lot in various forms that amount to "there is no right or wrong way of doing something as long as it works, it's all just personal preferences". While I agree there are many ways to do something, you sometimes (not even rarely) cross paths with hot piles of untested tightly coupled unscalable non-monitored non commented convenience-biased nonsensical garbage, that are obviously wrong.
> In all seriousness, this is a huge reason that spending three weeks paying down tech debt, carte blanche, often does little or nothing for the team’s velocity after those weeks have ended.
I think what's meant there is that refactoring for the sake of refactoring is pointless, which I couldn't agree more. What I don't agree with is that this is an argument for discarding the notion of technical debt. A strategy I found more efficient in that line is to tackle it while building features. Rather than playing alongside or worse, around the idiosyncrasies of something broken, fix it.
> we can track what specific elements of the system force feature development to take longer, measure them in terms of the amount of developer effort that they require
On paper, sounds like a great idea, but also like a lotbof bureaucracy added to a team who ironically probably already feels a lot of pressure because of said technical challenges. The other thing is that, it's rarely clear-cut, you rarely have entire tasks you wouldn't have done because of debt, rather it's just taking longer because of that debt. You're still left with estimating the refactored side
Overall what I take of the article is that the main point is not doing refactoring for its own sake. I don't actually gey the argument for why the term tech debt should not be used
Product management asks for wildly unrelated, inconsistent, and redundant changes on a sloppy schedule with absolutely no regard for its effects to the integrity of the product. This happens when they are still useless middlemen who just pass messages and get used as punching bags.
Developers poorly reinvent existing solutions in ignorance and use every task as an opportunity to learn instead of just doing what's expected.
This isn't necessarily "bad", but it is where it comes from and reason to slow down a project if the code is expected to endure.
Maybe someone should ask the person that coined the term what it supposed to mean? And we did. It's here:
https://www.agilealliance.org/wp-content/uploads/2016/05/Int...
Go ahead and say Technical Debt. If you want to check if someone is listening, use its French counterpart and say "Dette Technique"
Technical debt is fine for talking to other technical people, we've all learned what you mean. And of course you can break it down by urgency, block percentage, etc.
Maintenance Load sounds good for communicating to the people with the money, it explains why you're doing things they didn't ask you to do; and of course they can easily say no, just like we do in real life to infrastructure maintainers.
This changes the conversation to where tech debt is good, but so is paying it down. Good engineers will better anticipate the possible future changes in requirements and design for them and will incur less “tech debt” to pay back when product adds a feature.
Working from the same definition helps this.
And I don’t know why tech debt would even be coming up in discussions with sales or non-developer people unless you’ve got serious problems. And I’m that case, don’t focus on a phrase like “technical debt” - instead, use the phrase for what you’ve got: “serious problems” or whatever else the debt has manifested itself into.
1. Switched to microservices or a new language or anything more modern in 1 part of the codebase? Yea everyone now calls anything not that tech debt.
2. Have a performance problem and want to add more features? "Tech debt"
3. The code is just bad - "tech debt"
In all of these cases I feel like clean-up should just be constant and mostly opaque. The campfire rule is so great for these things - "just make the campground a little better than when you got there". It often does not take more time to just clean-up as you go. Then once you have your barings on a certain part of the codebase bigger changes will seem smaller and you can fix those up as well OR buy yourself that 2 weeks with a specific plan/ask. Eng cultures that worked this way seemed to work the best.
Also this:
> In all of these cases I feel like clean-up should just be constant and mostly opaque.
This is true for almost all kinds of liability. People only insist on complete transparency for financial ones.
But any kind of debt can enable leverage when it is done intentionally and with an understanding of the short term vs long term tradeoffs and risks.
Double down on your tech debt until it becomes a standard.
- before implementation, it’s a “milestone project management decided to ignore.”
- after implementation, it’s a “project management decision to implement in the next phase.”
I've always preferred that to technical debt.
Maintenance better implies time, employees and therefore money.
Obviously now when ChatGPT regenerates your entire codebase based on an ever growing detailed description then the idea of code maintenance and technical debt, rather than code readability, will need a rethink.
Getting rid of it implies a check to see if you need it and some work to do that (while you can wholesale throw away things, sometimes you'll find that you've thrown away some important functionality).