Paying off Technical Debt one step at a time

Technical debt grows in any codebase. Sometimes you want to pay that debt, but due to the lack of time or new features requests from stakeholders, you can’t get your hands on it. So, how can you do it?

Paying off Technical Debt one step at a time

No matter if you’re working on legacy code or in a codebase of your own, technical debt always arises. You can either solve it by means of refactoring or leave it as is, understanding that doing the latter will have a negative impact on your future productivity.

The thing is, sometimes you want to pay off that debt, but you find it hard to make time in the sprint, as there's always something else that seems more important. Or maybe you don’t know how you can convince your Product Owner or any other non-technical stakeholder that you’re benefiting the project by doing so.

Identifying the real problem

Some of the questions you need to ask yourself are:

  • Why is this refactor necessary?
  • What is its purpose? What am I trying to achieve?

If you don’t have answers to these questions, you won’t be able to convince other people of its importance. This task becomes more challenging when you have to justify the refactor to a Product Owner, or other non-technical stakeholders.

In those cases, the metaphor of technical debt itself is a good place to start. It allows you to convey more simply the risk of not fixing a problem by comparing it with a traditional debt, that is, that delaying its solution will make it harder to fix in the future and that in the meantime you’ll have a harder time maintaining the code and adding new functionality.

But if you really want to convince them you need to be able to be clear on what value such a refactor adds to the business.

Seize opportunities

The software development process can be divided into two different stages: one of expansion, where new requirements are accepted and integrated into the application, and one of refactoring, where these changes are consolidated and the code is simplified.

The need for expansion can be a good reason to refactor the code in advance. Otherwise, the new changes may be difficult to implement, or the resulting code may be too hard to understand. It is also an opportunity to take advantage of the technical and domain-specific knowledge you have obtained since you originally wrote it.

One thing I like to keep in mind is that, though in this case the expansion motivates the refactoring, these are two different stages that have different goals and, therefore, should not be performed simultaneously. Focusing on one at a time helps me to work better towards each goal and avoid context switching. It also makes it easier for my teammates to review the changes afterwards, as it is clear which ones are improving the existent code and which ones are adding new functionality.

Bug fixing can also lead to refactoring. Instead of fixing a bug with a band-aid solution, refactoring can help reduce the complexity of the code, making it easier to understand and, then, to fix.

What if I can’t think of anything?

Analyzing the real problem can be helpful by itself. If you are struggling to find a business-side justification, you may realize such refactor might not be that important, or that it does not really add value to the client.

That is the moment you have to learn to live with some technical debt. After all, clients pay you to solve their needs, therefore, refactors only make sense when they increase your ability to deliver functionality in the future and when the opportunity cost of the other tasks you leave aside is not that high. Some refactors just aren’t worth the effort. Nevertheless, this shouldn’t be taken as an excuse for lowering the quality of your code. On the contrary, you should be mindful when purposefully contracting technical debt, as you may not be able to cancel it in the future.

Following with the idea of opportunity cost mentioned before, there is a rule coined by Uncle Bob I find very useful: the boy scout rule (see below). There are some changes you could make on your code (like renaming a variable, extracting a method, or getting rid of a magic number) that are really cheap. They won’t take you more than a few seconds, but they can really make a difference the next time that code is read. Maybe you won’t be able to get rid of bad design decisions made on your application, but you can definitely make it more understandable.

The boy scout rule
"Always leave the code you're editing a little better than you found it" - Robert C. Martin (Uncle Bob)

Summing up

Refactoring is obviously an essential part of any project, and there are many reasons to perform it. But we need to remember that it is a mean and not an end in itself. The fact that refactors are not always justified is, for me, one of the key differences between developing software for the industry and doing it in other contexts (i.e. your own personal projects).

Developers play an indispensable role in safeguarding code quality, and certainly give it more importance than other roles in the team, as we are the ones that are in direct contact with the code. But we shouldn’t let that bias our judgments when prioritizing these kinds of tasks over others that will also contribute to the quality of our product.