Stop saying “technical debt”
We were supposed to release this feature three weeks ago.
One developer got caught in a framework update. Another got stuck reorganizing the feature flags. A third needed to spelunk a long-abandoned repository to initiate the database changes. The team is underwater. Every feature release will feel like this until we get a few weeks to dig ourselves out of tech debt. We have no idea how to even get the business to consider that.
Does this sound familiar? It’s a disheartening conversation.
But we often predispose ourselves to this situation. How? We try to get businesspeople, designers, product managers, and engineers onto the same page by using the phrase “tech debt.” But that phrase puts us on completely different pages.
Ask someone in tech if they’ve heard of tech debt, and they’re likely to respond with a knowing sigh. Now ask them what it is. Do this ten times, I dare you. How many different answers do you get? Three? Four? Seven?
Everybody associates the term with a feeling—frustration, usually—but they don’t have a precise idea of where that feeling comes from. So they slap the term onto whatever happens to bother or frighten them. Designers say it means the design can’t look the way they planned it. Product folks lament how it means they lose three weeks and get no features out of the deal. Engineers? Their answers vary the most, but often they’ve got something to say about “bad code.” We’ll return to why “tech debt equals bad code” is such a scourge, but first we have to address the effect of a bunch of different people defining the same term differently in the first place.
Here’s the effect: the minute we trot out the term “tech debt,” everyone is upset but no one is listening. Each conversant assumes they know what we’re all talking about, but their individual pictures differ quite a bit. It sounds to the business like the engineers are asking for three weeks free from the obligation to release any features. They remember the last time they granted those weeks: within a month the team was underwater again. When businesspeople don’t want to grant a “tech debt week” because they saw with their own eyeballs that the last one improved the team’s capacity zero percent, how can we expect them to grant us another one with alacrity?
We, the engineers, have to examine our terminology. And we can find that terminology by dissecting what we mean when we say “tech debt.”
Tech debt is more than just bad code
Equating tech debt to bad code allows us to fall into traps. It allows us to assume that the prior developers just sucked at their jobs—which is uncharitable, but fine, until we realize that there was actually a constraint we didn’t know about. This constraint explains the loathsome characteristics of this code, and it also prevents us from doing our own genius solution.
I once worked on a team that complained ad infinitum that customer information required a query that drew from two different tables. The team assumed that the structure remained in place because of inertia or because changing the database structure had backward compatibility implications. After spending a non-negligible amount of time bashing the database design and dreaming up ways to fix it, the team learned that their plan was…illegal. For privacy reasons in their industry, it’s illegal to store these two particular pieces of personally identifying data in the same table. Luckily, a product manager happened to mention the situation to a lawyer at the company before the engineering team got very far, or it might have been a showstopping compliance issue.
Equating tech debt to bad code also allows us to believe that if we just write good enough code, we won’t have tech debt. So we don’t spend time eliminating any. There’s no need to revisit, document, or test this code; it’s just that good. A year later, we’re back where we started. Whoops.
Equating tech debt to bad code also allows us to conflate “this code doesn’t match my personal preferences” with “this code is a problem”—which, again, is fine, until we’re under a time constraint. We spend “tech debt week” doing our pet refactors instead of actually fixing anything. Engineers love tech debt week because they get to chase down their personal bugaboos. 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, so no one besides the refactorer knows it as well anymore. Fantastic. A+. No notes.
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.
To fix these problems, choose something measurable to evaluate the quality of the system. My recommendation: maintenance load. How much time and effort are developers spending on tasks that are not adding features or removing features? We can talk to folks outside the engineering team about that number. If we have six developers but half of our work is maintenance work, then our feature plan can only assume three developers. Business people think of engineers as expensive, so this framing motivates them to help us decrease our maintenance load.
We can also track that number and determine how fast it grows over time. The faster our maintenance load grows, the more frustrations we can expect. Zero growth means that we can always maintain the system with the same proportion of our engineering team.
Reclaiming your time
How do we minimize maintenance load growth? With good code stewardship practices. We rarely reward, recognize, or teach code stewardship the way that we do feature development skills. But code stewardship skills—documenting systems, recovering context from code, and designing for future changes—make the difference between a team that hums along for a decade or more and a team that repeatedly mires itself in declarations of code bankruptcy, rewrites, and despair.
The Holy Grail? Negative maintenance load growth: the kind of growth that makes our code more maintainable over time instead of less. The Grail requires even more of the team than a healthy quotidian code stewardship routine. It requires us to look at individual maintenance tasks, track their origins, and address those problems at the source. These chores, backed by empirical evidence, give us something concrete to discuss in meetings about tech debt.
Are we performing lots of routine library or framework updates right now? Maybe we need to explicitly set aside time on a recurring basis to complete those. The more these pile up, the harder it becomes to debug the interactions between releases of different libraries. And the less programmers perform these, the more out of practice they remain—which makes the update rockier and more painful at the last possible second, when the update becomes mandatory.
Are we reaching into abandoned repositories and figuring out how to make a change? Maybe we need to devote effort to recapturing information about how those repositories work. It’s common for development to become much harder after some seminal team member leaves because it turns out they knew a lot of critical information that wasn’t written down or organized anywhere. I call this a context loss event, and we have no idea how maintainable a code base really is until it survives one of these. After a context loss event, developers need to proactively assess and repair the damage done to the team’s shared knowledge before unfamiliar parts of the code base become dark and scary.
Are we constantly working around an architectural choice from the past based on assumptions about our domain that are no longer true? Maybe we need to create and prioritize a ticket for rearchitecting that. A resilient code design considers what types of changes the team expects to make in the future, and it allocates flexibility in those parts of the code. As with any task that involves predicting the future, sometimes we get it wrong. In those cases, we might need to change our design. That may require a dedicated effort.
How do we identify and prioritize chores like these? I have a whole self-paced online course about that, but even focusing on maintenance load in units of specific chores, rather than a unitary towering thundercloud of “tech debt,” gives us a better place to start addressing it.
We want feature development to feel smooth and effortless. The longer we put off maintenance work, the less smooth and effortless feature development will be. Rather than sweep all of those tasks under a rug called “tech debt” and then occasionally ask for time to deal with it as one unit, 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, and then negotiate their completion as individual tasks with attractive outcomes for developer capacity. We’re no longer framing them as an opaque and uncertain cost. We’re instead framing them as clearly circumscribed investments in our ability to produce impactful features. That conversation puts folks on the same page. It also increases the likelihood that:
- Engineers can allocate specific time to do the maintenance work
- Engineers will even be recognized for doing the maintenance work
- The maintenance work, having been selected from the real reasons that feature development slowed down, will actually improve the feature development experience for the future
And that makes the conversation about tech debt a lot less disheartening; It might even make it hopeful.
Tags: code maintenance, technical debt
41 Comments
I’ve never heard or seen this phrase before this blog article. I think I’m going to start saying it now.
Nice analysis. I dont know about this concepts.
I’ve always used ‘Technical Debt’ to mean dependencies on technology that are obsolete or deprecated. I’ve never heard it to mean ‘bad code’. It could mean old code doesn’t handle new use cases. Maybe some good practices can minimize the amount of rework required to update to new technologies. Eliminating it altogether seems akin to predicting the future.
Handling technical debt is required for complex systems that work over long periods of time, if those systems are not to become obsolete themselves. You can stop using the phrase but that won’t fix any problems on the ground.
yeah, how about people just stop using it wrong. and stop using it when talking to non programmers, the same way you woulnd’t start telling them about OOP and asynchronous functions.
It’s called technical debt because it’s analogous to financial debt. Business owners need to understand and reckon with it.
Exactly, technical debt, whatever name you would give it, actually exists, and ‘bad code’ perhaps is a phrase we shouldn’t use, but everything ever developed is circumstantial and contextual.
For urgent release reasons, some shortcuts in the code were willingly added, and ‘should be addressed’ in the future for example. Or, as you mentioned, quite some dependencies on libraries are pretty outdated and more often than not, this might bring its fair share of effort needed to put in updating your code.
Twice I did and upgrade of ‘outdated’ .NET 4.5 code to the newer .NET Core 3. for a huge business application this took us 4 FTE’s more than 6 months before it was working fine. But also sometimes a ‘simple’ dependency, and in its slipstream a whole lot transitive dependencies might bring a whole lot of reworking.
But this can’t be prevented in any thinkable way, this is just part of the job and technology.
If this should be addressed in a ‘3-week tech debt’ period on the other hand, is also something more mature tech companies don’t tend to do (anymore).
They just identify these ‘problem areas’ and place it as stories on the backlog, so it can be estimated and planned in.
They’re just new work items needing to be addressed, nothing to be ashamed of, nothing to be disappointed in, just part of the nature of development.
Technical debt is neither bad code nor maintenance generally nor outdated dependencies. It’s the summed results of trading immediate convenience over sustainable design: https://en.wikipedia.org/wiki/Technical_debt
Thank you for definitively addressing the phrase. The author never really defined it, but did talk about what it was not, which was useful.
same.. we use it when referring to chores related to updating dependencies and only that.
Same. When I hear technical debt, it means your code base is written in COBOL running on OS/400. American banking systems have a lot of technical debt because their cores were written in the 70s/80s and have not been migrated to newer technology. The IRS/Fed has a lot of technical debt for the same reasons. When the technology your company relies on to operate is mentioned as a historical line item in college text books, you have technical debt. When your base of programmers are all at retirement age because you can’t find anyone under 50 who has experience, you have technical debt.
That Cobol code from the 70s still works, whereas the Python code from 4 years ago that your overambitious programmer wrote while importing every modern library they could find and using its most obscure features is hopelessly broken and will need to be abandoned soon. Modern programming practice is inherently flawed due to unnecessary over reliance on third party packages.
It’s not working, for the simple reason that it’s impossible to add features or to fix bugs. It’s not working. Simple as that.
I agree with Ropes, and (respectfully) disagree with Old and Cranky.
A system isn’t tech-debt simply because of its age and lack of cloud computing. tech-debt is more so related to the amount of maintenance a system needs, and how close in the proximity the code is to the intended (target) features.
In my view, the mainframes running the US banking industry since the 70s likely have less tech-debt than any other system I’ve worked on.
The phrase “Technical Debt” should be the start of a conversation, not the end of one.
I am reminded of the phrase “premature optimization,” sometimes used as an excuse for refusing to consider performance in your software designs. Like “technical debt,” this phrase is meant to start a conversation, not end it. Despite these phrases having the potential for abuse, there are genuine reasons why you might want to invoke them.
The considerate software engineer, having used one of these phrases in a conversation, must then explain specifically what they mean, starting with the legitimate premise that the conditions described by either phrase can cost the organization time and money if they are not managed properly.
The phrase “code smell” doesn’t have any technical meaning at all, but everyone who uses it knows what it means: a sense of uneasiness. The problems arise when inexperienced developers ascribe more meaning to these phrases than they should, or confer more authority to these phrases than they really have. There’s no “official” basis for a “code smell,” any more than you can definitively identify “technical debt” or “premature optimization” without more analysis.
“Technical debt” has fairly clear meaning in established software / data engineering teams. *Weak testing *Weak CI/CD *Limited commenting *Limited or no design/other technical docs *Refactoring should be done (eg violating DRY). Apparently in other front-end or other types of teams it is more nebulous.
No. Don’t stop using the term. It is not a meaningless, hackneyed phrase. It is a technical term. Just, as you always should, consider your audience before you use any technical term. And, of course, understand it yourself. http://wiki.c2.com/?WardExplainsDebtMetaphor
“technical debt” is a synonym for “unresolved problems”.
If you’re a runner, and you keep tripping and falling during races because your shoes are untied, you’re suffering from technical debt. The solution is to tie your shoes, ideally before the next race begins. 🙂
What constitutes a real problem (vs a personal pet peeve, or just a misunderstanding of how the code is supposed to work), and what (if anything) to do about it, is a whole different discussion, and that discussion will remain necessary regardless of whether you continue to use the term “technical debt” or not.
Saying “Technical Debt” considered harmful
Better idea: stop playing name games.
Totally agree.
Technical debts are things the TO DOs scattered around the code. If there is no TO DO and the intent is just to make it more elegant, then it is seldom worth the trouble. However, most of times, those TO DOs are things that slow down the development team, limits the growth of the business, rises costs and some of them are even timed-bomb waiting to be either defused or detonated.
This frequently have to do with bad code, but it is much more than that, and it is often to growth pains or technology changes and requirements changes. Although technical debts frequently involves bad code, equating them is just incorrect.
Moreover, having to always pay the technical debt and having the impression that we eventually are all back to square one is part of the development process. It is disheartening, but it is unavoidable and even necessary. As I say, “Technical debts are billed with high interest fees” and refusing to pay them only makes them growing larger and larger until they eventually become unpayable.
If the business people don’t understand that things left incomplete, undone or no longer satisfactory needs to be maintained, then the business itself is doomed. I already saw several business go bankrupt due to their inability to improve their legacy code because the focus was just to add feature over feature over feature without ever revisiting what was hastly done. The reason is because eventually the technical debts become so huge that no feature could be added without introducing a lot of bugs that degrades all the other features and even the very one being included. Eventually the system enters into a state that nobody in the development team is able to work and be productive with it anymore and neither the refactorings nor the new features are implemented. All the effort goes into trying to kill hydra-bugs (cut one head, two new heads born in its place) and the business people only complains that no feature ever gets implemented. Then, the next step is the collapse. All of this could be prevented if people understood that effort and time MUST be spent into paying the technical debts and that new features can’t be implemented decently if that debt is not controlled.
Releasing new features without a solid code base to support them is the same as building another store in the building without reinforcing the basement and the pillars of all the stores below. Business people who can’t understand this simply are not worth of their business.
Everyone will always work underwater because the only dry land is a desert wasteland. And if being underwater is bad, once you are in dry land, you are out of the business. Business people and developers must know that there are only three options: to swim, to drown or to die in the desert wasteland. Three weeks of refactor isn’t enough and will never be. No amount of time and effort is enough, but this is not a reason to not invest any effort or time, quite the opposite actually.
Technical debts even have something to do with the second law of thermodynamics: The entropy of an isolated system only grows. To keep the entropy under control, the only way is to transfer it to somewhere else, and that somewhere else is the work, effort and time of the development team.
I think “maintenance load” provides an interesting way to preserve the original intent of Technical Debt (per Ward Cunningham) without obscuring the meaning too much. Part of the problem seems to lie in this: people forget that Technical Debt is an unavoidable consequence of the choice to build and deliver features incrementally: we allow design to be simpler now in order to defer the part of the investment in design that just isn’t needed until we build that feature later. I see Technical Debt as a choice and a strategy, but “maintenance load” seems to include both Technical Debt and Cutting Corners.
Sadly, the Euphemism Treadmill always wins, so if “maintenance load” becomes popular, someone will write an article about why we need to stop saying it…. I predict around mid-2027. Maybe earlier.
Same as it ever was. Don’t give up!
Kevlin has talked repeatedly about technical debt, e.g. : https://youtu.be/piUesxuZkIQ?t=163 (Refactoring Is Not Just Clickbait – Kevlin Henney – NDC Oslo 2022)
We went as far as renaming it “business debt” in a previous team to remove the stigma from the phrase. Technical debt is about the conscious, collective choice between a tactical solution and a slower but more robust approach. The whole team makes the choice based on a number of factors, including time or commercial pressures, technical knowledge, value placed on stability/performance/security/etc. Everyone owns this trade-off, and if the tactical option is chosen, it’s on the promise to repay this debt. There’s a responsibility to be “better lenders” and not always choose the tactical option, but that responsibility never sits solely with the technical members of the team.
Interesting… most of the people I talk to have pretty similar definition of technical debt. For example, we would like to refactor the design of the database to take advantage of new capabilities (say, many-to-many relationship in Entity Framework or Time Series tables in SQL Server) – but we have interfaces or reports that will make it prohibitively expensive.
But there is one statement here that makes me question credibility of the author. “For privacy reasons in their industry, it’s illegal to store these two particular pieces of personally identifying data in the same table”. I worked with many standards (HIPPAA, GDPR, most recently, CJIS) – and I am yet to see any that specifies anything about the *tables*. When you make up stuff like that, it makes me question other premises of the post!
I was curious about that one, too. That seems really strange for a legal standard to say anything about what data items can reside in the same table. Or for a lawyer to know what database tables even are in the first place. But I wouldn’t rule it out as a possibility, as it’s not so uncommon for lawmakers or regulators to make up rules about things they don’t understand at all. I remember several years ago when ACM had to send a letter to the U.S. Senate Majority Leader pointing out that the thing he was then trying to legally require was mathematically impossible.
Though if it’s true, what does it say about the state of law and technology? The drafters of the legislation had heard of “tables” but not of “views”?
Sheesh.
Stop trying to normalize mediocrity.
Thank you!
technical debt is mostly used from people who are not earning their living with programming but with telling the bosses that everything is wrong and they could help to make it better. But never ever by doing it better.
Or from tools which are telling everybody how bad the code is, just to sell.
technical debt is from ***holes to show them as the gift from heaven to the world of programming.
Way back in the late ’80s, I created the term “Kludge Limit” — i.e., the number of times I’d make a quick patch to some problematic code before I decided it needed to be re-written properly. I think that’s a better term for the concept.
Tech Debit is wrong to use because it’s Product Debt and product people should care about it even more, because ultimately it hampers their ability to get feedback from customers.
I thought a lot about this topic lately
> Releasing new features without a solid code base to support them is the same as…
What founders/POs should know here is that you cannot have the cake and eat it at the same time – I’m with you here. You need a solid foundation for the next features.
What people often overlook is that you can also take away features that are not used. Deleting code is not magic, and it makes things more maintainable (because of less code lines, which have less bugs).
> The longer we put off maintenance work, the less smooth and effortless feature development will be.
Agree. There can be a tipping point reached where there’s no way of refactoring out of it. The best choice is to build new (point 10 of my article). You can have a look at the slides as well, it’s linked up in my article..
Thanks for writing this, Chelsea
Never knew this was supposed to be a contentious word to use. In a programming field I use it (and my collegues use it) and it means a very specific thing. Using a package, framework or other piece of code that you know is not an optimal solution since it will eventually cause issues in the future and will need to be refactored and spent more time on. Hence, accruing a “technological dept”. You use a piece of code now, because it is conveniant, or faster, or you don’t have time to do it properly right now. KNOWING it might (or will certainly) force you to spend more time on it in the future when issues come to light.
Not a ‘technical debt’, but an excessive complexity that allows people to make them important and keep their places )
I think it is a very good point to stop the “stabilizing sprint” or “cool down week”. One thing is the mentioned bugaboos but also the post-feature refactoring is a bad habit.
A situation where I find the discussion of “tech debt” relevant is when a new feature can be straightforwardly implemented in a legacy system (another term that is somewhat dubious) but adding to the complexity in an unfortunate way and making future decommissioning more cumbersome, i.e. building “tech debt”. Maybe implementing it the “right” way takes longer but here engineers should be the only relevant decision makers and in that setting “tech debt” is used as a prospective term.
I would encourage the author to go read this article before ‘renaming’ a term that everyone on the technical side of the isle has been using for many, many years now.
https://medium.com/s/story/technical-debt-is-like-tetris-168f64d8b700
I would also remind the author that the business needs change constantly as well as the systems. Some things in a legacy system could be from moving from one database system to another or one cloud vendor to another for cost savings.
Another common item in mobile is you get pushed on you every year MAJOR OS changes and if you don’t refactor and improve you app could very well get pulled off the store. That’s a common tech debt pushed on all of mobile that you have to address and work with every year.
I would encourage any software team, with a large code base to set aside a week every quarter for maintenance and tech debt task like upgrading tools, build systems, libraries and making sure everyone is all using the same versions across the build pipeline.
The author shouldn’t confuse re-write with refactor. Refactoring is good. Doing a re-write because you can’t read the code is never an excuse. Refactor so you can read it BEFORE you change it. Trust me on that one. 😉
It’s funny, because I started using this term when I saw an advice from stack overflow. The question was something like “how to politely, professionally talk about poor, obsolete crappy practices during a job interview?” The answer was to use the term “technical debt”.
Probably code base maintenance covers better what is needed than technical debt. There are many reasons for suboptimal code. A company may hire external developers, that do not know the system architecture well. I have seen loads of duplicated code due to this. Or there may have been very urgent changes, resulting in a quick fix. Or, business may think you do not need to update the underlying software like OS, database engine. At some point this will go wrong.
As a product owner I have always claimed 25 of all working time for the team. With one team, doing so it resulted into 30% increase in effectiveness in a year (which was provable). Another 25% goes to maintenance tasks like fixing bugs or small improvements requested by the users. The last 50% is for bigger innovations. These values are not constant, but for the 25% for improvements I never asked the business stakeholders for permission. I just told them we were doing this and the team was in the lead for setting priorities.
I’ve been IT, consult, and etc, for more than 30 years and know and knew exactly what the verbiage meant from the first time I read it. I would say 99.99% of people with degrees have technical debt in their fields.
I liked the article; good food for thought and spot on in many ways. But the title is just click-bate. The term is fine. It doesn’t have a grandfather who was in the KKK, It hasn’t been charged in a drug smuggling ring, or been accused in the “me too” movement. There’s no need to change it, or stop saying it.
Just like any other kind of “debt”, Technical Debt is simply “stuff you eventually have to pay for (i.e. fix).” Bad code (you can all argue about what that means), bad design, outdated code, bad process, deprecated sub systems, you name it. Many dev groups will weigh what they know versus what they should know, and develop solutions based more on time to delivery than anything else.
What we know right now, can always get the job done faster. What we should know, and how we should do it, often gets overridden by those asking for our solution in a timeline that doesn’t allow us to do it right, only to do it now. So, no matter the reason – the minute we start doing something we know we shouldn’t over doing it the way we know we should == Technical Debt.