How to deal with technical debt

Robert van Lieshout
6 min readDec 1, 2018

In a recent discussion about technical debt and what to do about it, someone suggested that we shouldn’t fix technical debt. Sounds crazy, right? Well, maybe not. To be honest, at first I couldn’t believe this was a serious suggestion. It was. And it was backed up with an interesting line of reasoning. Below is my attempt at explaining that reasoning and expanding on it.

Photo by Irvan Smith on Unsplash

I’m going to assume you already know about technical debt. If you need a refresher, I suggest you look at one of the following:
Ward Cunningham on Tech Debt
Uncle Bob on “A mess is not tech debt”
Martin Fowler’s Technical Debt Quadrant
Henrik Kniberg’s take on “Good and Bad Technical Debt”
Ron Jeffries thoughts about Tech Debt

Based on a photo by Annie Spratt

Don’t make fixing technical debt a separate thing

Going out to find technical debt doesn’t in itself add value. Neither does keeping records of technical debt. Even fixing technical debt doesn’t necessarily provide value. For example, imagine you have a component which has a lot of messy old code but it works okay. Based on the product roadmap, you expect this code isn’t going to change in the next half year. What is the value of refactoring this code? Sure, after refactoring the code is in better shape. However, if left as is the messiness isn’t directly hurting your development effort since you probably won’t have to change it.

By contrast, there is value in fixing technical debt in code that often needs to be changed, or that needs to be changed soon. However, by making this a separate thing (for example a separate product backlog item or a capacity allocation for technical debt), you’re likely to get discussion about the priority and the value relative to business features. That could even lead to the decision to not fix this technical debt.

Extreme boy scout rule

I think a better approach is to clean up code whenever you touch it, and to rigorously apply good engineering practices to any new code. I call this the “Extreme boy scout rule”, mostly because many engineers feel they already apply the boy scout rule, but don’t do it nearly as aggressively as I recommend. Martin Fowler talks about “Opportunistic Refactoring”.

If you’re changing some code for a reason, the technical debt in that part of the code base is hurting you right at that moment by making it more difficult to implement the change. Also you’re more likely to inadvertently introduce new defects (e.g. due to not understanding the code). It’s too late to prevent the technical debt slowing you down now, but you can still limit the chance of new defects by removing the technical debt, and reduce how much it will hurt future changes. What’s more, there is clear and immediate value in doing this! Cleaning up as you go can often pay back immediately through reduced read or test time.

This “cleaning up as you go” doesn’t need separate stories, budgets or approval. You as an engineer can and should just do it as part of your regular work. In fact, your Definition of “Done” likely already mandates this. Either way, engineers have the duty to apply good engineering practices such as TDD, BDD, Clean Code, CI/CD and pair programming — to name just a few of my favorites. All that is needed is the courage to take this into account during refinement and planning, and the discipline to apply it rigorously. If features or stories take longer than previously, it is not because you are slowing down — it is because you are improving quality, and this will save time in the long run.

As many people have pointed out, there is a genuine danger that one refactor leads to finding more technical debt, which leads to more refactoring, which leads to finding even more technical debt. In the end you never get round to building new functionality. While that danger is real, I find that the opposite is far more common: under pressure to finish things quickly, we leave a lot of bad code as is, solemnly promising ourselves to fix it next time we have a spare moment. But next time there is something else that needs to be finished quickly.

Photo by Nathan Lemon on Unsplash

A change in mindset

It’s easy to say “all that is needed…”, but quite hard to do this. When you’ve been conditioned to deliver fast with a focus on the short term, and when many agile practices are counter-intuitive, how do you learn to stick to it and take the extra time now to save more time later? I don’t have the answer to that (and I don’t believe there is a definitive answer). It requires a change in mindset, and I have a hunch that using the code review process could be an excellent point of leverage. The best we can do is Probe, Sense and Respond.

Dealing with big scale changes

The extreme boy scout rule works on small to medium refactors. Good slicing of stories can help to push a lot of work into this category. Nevertheless, there may still be structural changes that are too big to handle this way. I had some thoughts on that not so long ago, but more and more I believe you should avoid doing even these large changes separately. So, how do you avoid doing these separately?

I think this requires that we work with Product Owners or Product Managers (or whatever these folks are called in your organisation) to understand the product roadmap. Where architectural changes or significant technology transitions are needed to support the roadmap, make sure this work is included as part of the features. What’s more, be rigorous in bringing the code completely in line with the new architecture. Just as you can slice the work for the features, you can also slice the architectural changes. Just be sure that all of it gets done as part of the feature and that none of it is considered optional.

Forced short-cuts

That still leaves the case when we’re forced to make short-cuts to meet critical business commitments. In such cases where we consciously decide to add tech debt, we have to treat the newly added tech debt as a separate backlog item. Removing this tech debt must be given the highest priority (after meeting those critical commitments). If the organisation is likely to favor other features, I would consider making the tech debt removal part of the feature that caused it. That feature is then considered not finished. Although it is only an administrative trick, it may help keep the focus on removing the tech debt.

Summary

I guess what I’m trying to say just boils down to two things: constantly deliver the best quality code you can (including rigorous refactoring), and don’t go chasing and solving technical debt as a separate activity.

Finding the right balance isn’t easy, but a worthwhile goal to pursue. My impression is that we err too much on the side of short-term gratification and should focus more on long-term sustainability.

You only talk about fixing technical debt if it is a separate activity, and it shouldn’t be. Don’t fix technical debt. Just make sure with each check-in you deliver the best quality code you can.

--

--

Robert van Lieshout

Father, learner, coach & agilist. I'm passionate about nature and helping people become their best selves.