When Ward Cunningham coined the technical debt metaphor, he needed a way to discuss the decisions made early in a project that were coming back to haunt his engineers as they continued building down the line. He was at a firm doing financial software, so a fiscal metaphor was in order. The technical decisions they made early on for the sake of getting a product to market might not have been the decisions they needed now, and unless they fixed them, the team’s productivity would suffer and features would be slower to release.
This metaphor has caught on and become so prevalent as to barely need explaining. Plenty of successful companies used tech debt to get off the ground, only to pay it off later. For example, Facebook was originally written in PHP. The first implementation worked for what it was, but as they added features, complexity, and scale, they needed to pay off the significant debt that was PHP. It’s worth noting that tech debt doesn’t necessarily mean your original choice was flawed. Writing the site in PHP wasn’t a bad decision at the beginning—this wasn’t a case of bad code biting them later. It was a fine language that they outgrew.
In fact,the thing holding back anyone trying to upgrade from an outdated framework or library isn’t a technical issue. Like the metaphor, the main hurdle is financial. I’ve seen plenty of upgrades put off for years because of the burden required to install, convert code, and test the effects of the upgrade. Meanwhile, the unpatched security holes could be leaving you vulnerable, and the new features you’re not getting could leave your application at a disadvantage in the market. Someday, though, that bill will come due.
Obviously, tech debt is more than language and tool updates. It can be good-faith coding decisions done for an immediate benefit. The sooner you can address your debts, the better. But addressing those debts requires you further dig into the tech debt metaphor and quantify exactly how much debt your engineering team is carrying.
Cost of maintenance time
While developers love to solve problems, that love doesn’t always extend to bug hunting and replacing outdated tech. Some folks, though, would be happy to refactor code all day and create perfectly clean code. Regardless of your personal feelings, it’s management that determines which epics and tickets end up on the next sprint. And management thinks differently than an average developer.
Devs may seek the pinnacle of elegant code, but a manager (and the upper-level managers above them) serves the business, and the business often focuses on profit and loss. To pay off tech debt, you first need to put it in terms of costs and benefits to fit into today’s data-driven and project-based development cycle.
The economic impact of tech debt is real. Stripe ran a study in 2018 that attempted to measure the impact of tech debt and other “bad code” effects. They found that the average developer spent 13.5 hours on tech debt and almost another four hours on “bad code.” If you multiply that out based on a developer’s pay, then you’ve got a pretty good cost to attach to tech debt.
Chelsea Troy wrote about the imprecision of the term tech debt, and offered “maintenance load” as an alternative. Outdated tech and sacrifices made in the name of efficiency have qualitative effects; that is, they feel bad at some point. But the amount of time that a team spends on non-feature code? That’s a metric you can hang a budget on.
As Some Guy said on the Software Engineering Stack Engineering site, “Poor-quality code often takes significantly longer to maintain due to complexity, and also typically has a high bug count. Thus, poor quality will eventually severely impact things managers value.” Most companies already have some sort of issue tracking system in use, so devs who want to make their case can piggyback on that. It just requires discipline in tracking tickets, estimating story points, and proving out time spent on maintaining tech debt. Whether you roll all your debt up into a single epic depends on your organization’s needs; the tracking of specific tech debt-related actions is more important.
That applies to actions that you know are part of your maintenance load and existing debt. To identify the effects of specific areas of debt, look to the code. Andy Dent suggested on Stack Overflow that you track which pieces of code are getting the most commits. “Doing such data mining would allow you to also identify clustering of when code is being maintained and compare that to bug completion in the tracking system,” he wrote.
You could just ask your team, though. Roberta Arcoverde, director of engineering here at Stack Overflow, said, "It may sound naïve but there's actual data and science behind why that usually works as well as other more sophisticated techniques: if the team recognizes something as important, they probably either spent some time in it recently—which is why it's top of mind—or the code is so bad they remember it promptly."
Of course, these are just the costs of writing code to fix and maintain your debt. Another important metric is the code you didn’t write.
If developers are spending nearly half their time on tech debt and bad code, that means they aren’t shipping new features out to customers. For a software industry that has recently become obsessed with development velocity, this is a big deal. The faster you can ship new features, the better you can satisfy customer requirements, and the greater value you can add to your product. Conversely, the more time it takes to get features and fixes to market, the farther you fall behind competitors.
Much of development velocity focuses on the DevOps side of the software development lifecycle (SDLC): CI/CD, code reviews, build processes, and so on. The DORA metrics are a pretty good guide for this. But any tech debt will also affect how long it takes to write the code that you’re trying to deliver, especially if you’re writing workarounds that increase the number of paths through code—cyclomatic complexity—in order to avoid tackling the tech debt slowing you down. That complexity itself becomes a piece of debt that you may need to resolve at some point.
There is absolutely a connection between this complexity and developer productivity. As code becomes harder to maintain, new features take longer to ship. The more places in the codebase that need to be touched for a change to work, the more time it takes. That’s why motivational acronyms like DRY and KISS gain traction: other people need to read this code, so the easier it is for them to do that, the better. And the easier it is to read, the easier it is to change.
Sometimes tech debt comes from good-faith technical decisions that are outdated or based on previous versions of software. Updating those may require a bigger rewrite than a simple refactoring. In his answer, Michael Meadows identifies a few items that can mean a rewrite is needed, including:
- The software in question has been deprecated or will be deprecated soon, or there is a better piece available.
- You can’t fully automate deployment.
- Testing takes too long, can’t be automated, or can’t cover important features.
While the DORA metrics can help, you can also compare your current velocity to your best story point to effort sprint, which is often near the beginning of a piece of software’s lifespan. Doug Knesek wrote that this can be considered the interest paid on technical debt. As debt grows, the payments to service the interest become increasingly onerous, and overtime may outstrip the principal. In a software organization, as tech debt grows, so does the time and effort that goes into paying down this interest. The slower your overall development velocity, the fewer new features get shipped to customers.
Of course, the costs in tech debt don’t just apply to code. They also affect the humans who write the code.
The human cost
Software companies (and these days, almost every company is a software company) have been competing fiercely for top technical talent. Finding and onboarding a new hire can cost up to six to nine months of their salary. It’s no wonder so many companies are focusing on developer experience: hiring a new dev is expensive. Technical debt affects those technical professionals at every step within a role, starting from the moment they sit down at their new keyboards.
Onboarding is the first and most important part of an employee’s experience at a company. It can take around one to two months to get a new hire up to speed. A bad onboarding experience can sour a developer on a company; 20% of developers leave within 45 days of taking a role. So a smooth onboarding experience can mean the difference between a happy software engineer and another job posting.
The amount of time that it takes to onboard a dev will depend both on the documentation and processes that you have in place and the complexity and readability of your codebase. That lack of documentation for processes is a form of debt itself. You know you’ll need it at some point, but in order to get going fast, you skipped it. Any new hires are going to have to ask around and take up senior engineering time in order to onboard themselves.
Once the developer is onboard and ready to commit code, they may find themselves frustrated by the mountains of janky spaghetti code they face. I’ve heard of friends baffled by idiosyncratic coding methods that make code difficult to parse, like concatenating all parameters into a single variable and passing it to a function. That becomes frustrating and demoralizing as you continually spend time just trying to figure out what’s going on with this code.
Obviously, tech debt is not just code that needs refactoring to align with best practices—outdated or inefficient tools and dependencies can drive a dev to despair. Imagine writing Java code for an older version and finding a confusing workaround. “I thought that was fixed in version Y?” “Yeah, but we’re on version X still. The upgrade hasn’t been approved.”
When you’re not using the best tools for the job, your skills can atrophy. In our survey of why developers choose to stay at or leave a job, we found that 35-40% (depending on region) of people said that they were looking for new jobs to use new tech, and another 35-40% cited growth opportunities.
These all add up to a resignation. If your company suffers from frequent churn or loses people within the first year, then that’s an opportunity to see if technical debt is the problem. Make sure you have exit interviews and track all that data they give you. If those debts are costing you good employees, that’s a good reason to prioritize them on the next sprint.
Count your debts
Technical debt thrived as a metaphor because it translated engineering concerns to business language. If you’re looking to pay down those debts, then you need to make that case in the business’s language. What’s the cost? How does it hurt or benefit? With any business decision, it’s always a tradeoff.
Like financial debts, unpaid technical debts can haunt you as you spend more and more of your time addressing its effects. Bad code begets bad code, and the interest accrues to the principal. You’ll need to pay your debts sometime, but the better case you can make for taking care of them now, the more breathing room you’ll have to write business logic code that you can be proud of.