Technical Debt: Here Be Dragons
We all have notions about what technical debt is, and we all know that it’s something that we should be concerned about. Development teams encounter it on a regular basis, and it can have a profoundly negative impact on their long term productivity. Unfortunately, it’s extremely difficult to prevent technical debt from accumulating for most software development efforts. In this post I’ll cover some of the reasons why I believe that we regularly underestimate the impact of technical debt, and then, almost invariably, fail to mitigate it once it has crept into our projects.
Components of technical debt
When considering the concept of debt, our primary interest focuses on two components:
- The principal amount that we’ll need to repay at a later time
- The interest that we pay on the amount owed up until the point that the debt is settled. The interest, and possible compounding thereof, makes the total amount that we’ll have to repay larger (sometimes significantly) than the amount borrowed.
Just like financial debt is borrowing money from your future self, technical debt can be thought of as borrowing productivity from your future team.
Technical debt accrues from a number of sources. Sometimes it’s simply due to lack of experience with the technology or incomplete knowledge about the problem that results in necessary remediation steps. Perhaps the chosen solution has a negative impact on the project’s future. Often, immediate time pressures lead to making changes without considering the negative impacts on future productivity. As technical debt exchanges time gained today for time lost in the future, it might superficially appear to make sense to ‘finish it quickly now, clean it up later’. The problem is that it’s not an even exchange. We pay the debt back with interest!
Underestimating technical debt
A number of factors negatively impact the trade-off between present convenience and long term cost. We’ll examine some of those that have the greatest impact on long term productivity in the following sections:
Undervaluing productivity in the future
We all procrastinate on occasion. Sure, it sounds appealing: “I’ll relax for half an hour longer and finish my chores later.” If we valued our future free time as highly as we do our present free time this exchange would not make sense. Yet we often choose to do something ‘later’ because we think we can get more value from our current available time. We’ll throw caution to the wind in order to finish up a feature quickly today so that we can get on to more important things, while mentally noting that we’ll need to come back to fix it up later. Right…
Underestimating the overhead
Even when we correctly put off some maintenance tasks to focus on current priorities we often underestimate the extra work that we will need to do later. If a quick-and-dirty fix introduces 30 minutes of overhead for future releases, that’s 5 hours for the next 10 releases. If this is slightly underestimated, say we have to explain it to a new team member, and maybe forget to do it once, that additional overhead can quickly add up to 40 or 50 minutes. That’s an additional 3 hours on top of the original 5. Now, even if we did make the right call for 5 hours, does that still feel right if it turns out to be 8 instead?
Is it worth the time? (source: XKCD.com)
XKCD shared an illustration of how improving a task by a certain amount of time can result in significant cost savings over time. You get the point. Technical debt IS EXACTLY THE OPPOSITE. Introducing a minimal amount of overhead up front, can have a very negative impact on future productivity. This “interest payment” drag adds up quickly.
Pay some now, or pay more later?
Once a project has started incurring technical debt, the immediate question is when to start servicing the debt. Since the technical debt often requires rework to correct it, another trade-off must be made. Either start paying the servicing costs (interest) now and defer truly corrective action until some point in the future, or bite the bullet immediately and fix it properly, now. This is very similar to the reasoning that introduced the technical debt in the first place. We’re unlikely to come to a different conclusion when faced with similar circumstances. Thus, the technical debt will likely persist for longer than originally expected.
The factors that we’ve discussed combine with a myriad of others to make the consequences of our choices much more severe in terms of the total cost of a deferred task. Unfortunately, we tend to discount future productivity relative to the allure of accomplishing something today. In addition, the extra work is often underestimated and persists in the system for far longer than expected. In my experience this results in technical debt sometimes never being resolved. I’ve seen teams lose as much as an incremental 15% of productivity each year due to maintenance overhead. That’s right, 15% additional loss each passing year. That means that after 7 years, 100% of the capacity will be spent on avoidable maintenance costs. Sound familiar? This is the graveyard of brittle systems.
Fixing technical debt
There’s little doubt that we inevitably underestimate both the magnitude and the long term cost of technical debt. Of course, an “easy” solution would be to just not settle for inferior solutions. Sadly, that’s not always possible. Some of the tactics that my teams employ to successfully manage the level of technical debt include:
- When first making the conscious decision to cut corners, we don’t ask ourselves how much time we can save today. Rather, we consider the worst case scenario if we postpone the proper solution. More often than not, the actual cost of delaying the proper solution doesn’t outweigh the cost of technical debt. If we can’t delay, it’s probably worth doing right.
- If we can’t delay, but also can’t afford to invest the time to do it properly, we take appropriate measures to ensure that we don’t let our biases minimize the maintenance overhead. We log both the decision and the expected impact. Then we keep track of what it actually costs the team. We use this information to learn from these decisions and to make more well informed decisions the next time that we encounter similar circumstances. In our current team, we simply record marks on a sticky note for every fifteen minutes that someone was affected by a specific problem area. During our biweekly retrospectives we discuss all of these problem areas to see if the overhead is still acceptable.
- To prevent technical debt from accumulating in a project, we commit to budget for the cost of fixing any outstanding issues as soon as possible – preferably before the technical debt is introduced. We make it very clear to all stakeholders that this is just part of the investment required to implement the change. We estimate the cost required to fix it properly and agree on that up front. Then, when the time comes to stabilize the code, everyone is on the same page.
While steps like these require some project team discipline, they will minimize the build up of technical debt and mitigate the burden. It’s often tempting to let technical debt build in order to address immediate priorities. If you’re choosing to do this, just remember the old saying:
Pay me now, or pay me later…
P.S. Who knew that the commonly accepted “Here Be Dragons” artifice was just wrong?