Technical debt is usually seen as a negative factor in a development process. While having too much technical debt is indeed a good indicator for a project gone bad, technical debt is not always a bad thing.
What is technical debt?
When you start writing code you usually have a choice: either do it quick and messy or do it, as we developers tend to call it, “the right way”. The quick way is obviously better for the business as it delivers value earlier.
From a business point of view, no one really cares in what state the code is in. If it works, the business is happy. So then, considering that a healthy code base takes more time and is thus more expensive, why should the business have to pay more for a healthy code base, when it doesn’t really concern them?
Although a bad or untested code base may deliver business value, if it’s left uncontrolled, it’s not necessarily good in the long run. An unhealthy code base is hard to maintain and will develop stability issues. This will affect future efforts to add more business value.
In lay man’s terms, technical debt is the amount of mess left behind in a code base from quick fixes.
Benefits of technical debt
From the previous description, it might seem obvious that technical debt is a bad thing and should be avoided at all costs. That’s not entirely true though. Sometimes the cost of accruing technical debt is less than the cost of having to release later. Debt in the financial world has obvious advantages, and if managed correctly it can be a tool to improve yourself.
Taking a mortgage on a house when you have studied how to pay it back, can be a good investment. Waiting until you have saved the entire amount would be impossible for most people and thus you have a cost of opportunity. On the other hand, using a credit card like it’s a free for all pass and buying anything that you desire is probably not a good way of managing your financial situation.
In software development, the same can be said. When you need to push out that release before Christmas, it’s possibly a good idea to implement something quickly so it’s there when the opportunity is big. On the other hand, pushing out all features as soon as possible without regards to code quality, architecture and tests will sooner or later result in a slower process and reduced quality of the application.
Managing technical debt
As with financial debt, technical debt can beneficial. And also in line with how we think about financial debt, the key to balancing cost and advantages is to make sure you have a good strategy for controlling debt. Here are a few ways I found have helped me in keeping technical debt under control.
Default to avoiding technical debt
The default way of writing code in your organization should be to write well-factored, flexible code with decent test coverage. Acquiring debt is not a decision that should be taken lightly, therefore, when it’s taken it should be taken deliberately and not by coincidence because someone didn’t feel like writing good code today.
Communicate the consequences
There’s a typical conversation between a developer and a manager. If you are a developer, I’m sure you have heard it as well. It goes something like this:
*Manager*: I need feature X. How much time do you think it will take you? *Developer*: 1 week *Manager*: Hmm, it needs to go online in two days though, as big event X is in two days *Developer*: OK, I’ll see what I can do *Manager*: OK *Developer*: OK
Familiar? I thought so. This happens all the time and the problem here is not bad standards or bad employees. The problem is communication. Here is the same conversation, but with the thoughts of both in brackets:
*Manager*: I need feature X. How much time do you think it will take you? *Developer*: 1 week (*1 day of thinking, 2 days coding and testing, 1 refactoring, 1 days extra testing*) *Manager*: Hmm, it needs to go online in two days though, as big event X is in two days *Developer*: OK, I’ll see what I can do (*I’ll cut down on the thinking and testing*) *Manager*: OK (*I’m a great manager, I just managed to get something done in 2 days which normally takes a week*) *Developer*: OK (*ugh, always the same, we just can’t write decent code here*)
It’s important to realize that various factors are at play here:
- The manager wants feature X in two days, not because he’s a tyrant, but for a good reason: it earns more money if implemented earlier.
- The manager walks away with idea that it can be done in two days. If this happens often, he will realize that pushing developers works, after all, in two days he’s going to see that it works indeed. He doesn’t know what happens in the code base, so it’s normal that the next time around he’ll try to negotiate the estimate.
- The developer wants to satisfy the need of the business, but feels incapable of doing so in both short and long term
As a developer we have an obligation to communicate better to the business. Here’s my improved version of this conversation:
*Manager*: I need feature X. How much time do you think it will take you? *Developer*: 1 week **Manager**: Hmm, it needs to go online in two days though, as big event X is in two days *Developer*: It’s impossible to do this feature well in 2 days, it needs a week to be done properly. *Manager*: OK, but we don’t have a week. If it takes a week, there’s no point as we won’t earn as much money from it. *Developer*: What I can do is take a shortcut and do it quickly. That would require me to rearrange some things and I need to go back later to fix it. *Manager*: OK, that sounds reasonable (*great, I’m going to get it done in time*) *Developer*: OK (*great, I will make sure the feature gets implemented soon and then I’ll need to go back to make sure nothing gets left behind that can cause trouble in the future*)
In this conversation, a middle ground is found. The feature will be implemented, and the technical debt is accounted for and managed. The consequences are well understood by the business and can be dealt with accordingly.
Just as with financial debt, you want to know what debt you have and how long it would take you to get rid of it (even if you’re never going to get rid of all of it). In the above story, it would be wise to create a task for cleaning up the code base and writing tests after the feature was implemented. This way, it’s visible to everyone that there was technical debt acquired.
The way you track technical debt depends on your process. In an agile process, we have created a new type of story before. Apart from user stories, tasks and bugs we’d have another story type called “technical debt”. This allows us to see in a quick view how much technical debt we have and whether it’s becoming a problem.
Apart from tracking technical debt as and when you create it, it’s sometimes also necessary to track technical debt that you spot. This could be either legacy code or it could be some big refactoring that you and the team feel is necessary for the code base to be flexible towards future developments.
Depending on what situation you’re in, you could incorporate a certain percentage of the time to working on technical debt stories. make sure the business is aware of this and approves.
Repay your debt
Communication and tracking don’t serve any purpose if you don’t repay your debt. You have to make sure that technical debt stories are dealt with on a regular basis. When deciding which stories to tackle you need to factor in a few properties:
- Age: Just as with financial debt, technical debt comes with an interest. The longer you leave code in a bad state, the bigger the impact it will have: it’ll create more bugs and people will forget why and how something was implemented (remember, it’s bad code, so it’s probably obscure by nature)
- Impact: There’s a difference between a class that has some formatting issues and an entire subsystem that doesn’t have any tests. Tackle those issues that have the biggest impact first. (to continue the analogy: pay of the credit card with 20% interest rate before you pay of the one with 2% interest rate)
Apart from the need to repay your debt, there are also different ways you can choose to repay it:
- Repay it completely: replace the code or refactor it to a good solution
- Partially repay it: Instead of implementing a good solution, implement a different solution that has less interest
- Don’t repay it at all: just deal with the “interest”. This can be a good option if the cost is minimal, the code is hardly ever changed and replacing it would be very costly
Dealing with legacy code
Considering that we default to writing good code and all technical debt is communicated and dealt with appropriately, a normal project should never have so much technical debt that it impacts the business. However, there are situations where we don’t have these values from the beginning of the project: legacy projects.
Dealing with legacy projects is a totally different ball game. Often it’s difficult to identify the good code (or worse, there is no good code). Furthermore, it’s difficult to identify which parts of the code are causing most problems and how to solve them (aka: it’s difficult to estimate the interest).
To deal with this situation it’s best to create a metaphorical fork in the road, from which point you default to writing good code. All legacy code should be isolated as much as possible. Once legacy code is isolated, you can then decide that all modifications to that legacy should leave the code in a better state. A good book to read on this topic is Working Effectively with Legacy Code by Michael Feathers.
Technical debt is inevitable in software projects. Instead of trying to avoid it, we should try to manage it as effectively as possible. When managed correctly, technical debt can be a powerful tool to help your business grow faster without impacting the long term goals.