Roadblocks to repaying tech debt

I've found that talking about tech debt is a favourite subject among developers (especially if the developer responsible for introducing that tech debt is no longer on the project 😉). However, often talk is all that happens. As with any potentially ambiguous and daunting task it is easier to talk about it than to actually do it.

In this post I want to explore some of the roadblocks that can come up during these tech debt discussions, what the motivations for those roadblocks could be and how we can overcome them to get on with actually reducing the tech debt in our projects.

In no particular order, let's look at some.

#1. We could be building features instead of refactoring

Motivation

Any time spent addressing tech debt won't directly benefit the end user.

Solution

TL;DR: Keep tech debt refactoring small.

This is a valid concern and one that actually shows that the person raising it cares deeply about the experience the end user has. However it is also flawed as a good tech debt repayment plan involves making frequent small refactoring improvements when the pressure is off rather than big payments when the debt is stopping you from implementing a new feature that the company needs implemented now. This refactoring tasks if undertaken frequently enough will become part of your team's culture and as such tech debt payments will lose some of their importance and begin to look like any other development task. Not only will doing smaller refactoring reduce the stress of the refactoring, it will/can also improve the quality of unwritten code. It does this by setting the overall quality level/bar of the project higher - this is perfectly described by the Broken Window Theory (please note that I'm using the programming version of the Broken Window Theory as my inspiration above - not the actual New York City Police policy). I find it much easier to maintain a certain quality in a project than to try and achieve that quality.

#2. We need to sneak paying back tech debt into feature development

Motivation

That tech debt is solely the responsibility of the developers.

Solution

TL;DR: Promote tech debt repayments to a wider company level so that everyone is aware of the issues and the benefits from refactoring work.

Discuss how tech debt affects the wider company and agree with the other stakeholders in the project that a percentage of the weekly development effort will be spent addressing tech debt (or generally anything the tech feels is important). We may often feel that we work in feature factories but we are also responsible for the quality of project that we work on and if something is important to us we need to ensure that the wider company knows this. It's important to note that when describing these concerns to the wider company that we phrase the problem in a way that affects them - discuss how tech debt slows down development of new features, slows down development of extending features, leads to more bugs and can result in higher developer turn over (compromise for too long and you stop taking pride in your work).

In the past on joining a new team I've insisted on having a Captain for each iteration - this Captain is responsible for answering general project questions, uploading the app, releasing the approved version and resolving general bugs (not related to a feature under development). But the most important aspect of the Captain is that they are not assigned any feature (or Product) work, that way when they are not addressing the tasks listed above the Captain is focused on tackling tech debt with the total agreement of the wider company.

#3. We should only be refactoring feature "X"

Motivation

That paying back tech debt on any other feature than "X" is a waste of time and that everyone should focus on feature '"X".

Of course someone else feels the same way around feature "Y".

Solution

TL;DR: Create objectives for an extended period of time (e.g. a quarter) and use those objectives to guide your tech debt refactoring work.

Since the start of 2017 as a team we've been experimenting with OKRs and have found that having team wide objectives (that are connected to wider company objectives) to be a really useful filter when deciding on which tech debt item to address. As a team we can discuss if refactoring feature "X" or feature "Y" is connected to one of our objectives. If the refactoring isn't connected to an objective we can instantly discard it - it's not that it's not important, it's just that it's in an area of the app that we have chosen not to focus on.

The more cynical among you dear readers will be thinking:

All that does is shift the debate from the tech debt to OKR setting

and my response to that is:

...kind of

Team OKRs should be set based on the wider company objectives which should act as a limiting factor on debate and provide clear boundaries which allow us to better detect when we go off topic. For example if our objective was "Increase revenue by 25%" and we began discussing refactoring our profile screen, it becomes clear that we have moved off topic.

Even if you use OKRs it's still possible to have multiple different possible tech debt payment options. Here each member of the team gets a certain number of votes that they can spend on the possibilities - so if we have 5 possible options each person gets 2 votes. The option with the smallest number of votes is eliminated until we end up with just one option. That way everyone is involved in the decision making process and while they may not agree with output they can't deny that they had a say on what it was we should be doing. Often I find people don't mind not getting their own way but they really want to feel involved in the process and that their views were taken into consideration so the voting system is always great for overall team morale.

#4. We can't refactor feature "X" because it works

Motivation

That re-opening a feature that is stable is unacceptable risk.

Solution

TL;DR: Don't give into the fear of bugs, instead use tech debt refactoring to introduce automated testing into your project.

This one is tricky as it's a very valid concern. However you need to discuss what happens if you don't refactor it and something breaks anyway - would you prefer to refactor a feature when it is impacting your end users and your manager is demanding hourly progress updates or when you can take your time and unit test those features to death? Fear of introducing bugs isn't a valid argument for not tackling tech debt as the environments that we work in don't stand still, the feature may work in one version of the OS but be broken in the version just released. As developers we are solely responsible for introducing bugs into our apps - the only perfect app is the one that exists 100% in your head. A good tech debt repayment should include a form of automated testing to make bugs harder to be introduced in future and to better detail why you chose the solution you did. I believe that a lot of tech debt comes about because the intention of the original developer wasn't adhered to by subsequent developers - unit tests help to better express that intention by acting as a form of living documentation. Having to fix an unexpected failing test forces us to really think if our change makes sense.

#5. We can't refactor feature "X" until we agree on the solution

Motivation

To not create future tech debt by utilising group thinking.

Solution

TL;DR: Accept that every solution we implement will be imperfect and empower the developer to make their own decisions.

It's important in any team situation to try and seek an agreed approach however you also need to move fast and know when to break rules. A good solution today is better than a perfect one tomorrow. So where that agreed common solution can not be found the developer who is actually going to undertake the work has the final say in which solution they implement. The caveat here is that autonomy is only used here where both approaches are better than the current solution but optimise on different principles e.g. solution A on testability and solution B on readability.

#6. We don't have time for that

Motivation

To focus on other development tasks.

Solution

TL;DR: Explain the hidden cost of tech debt and make space for repaying it on the product backlog sooner rather than later.

Committing to repayments

Each day we get better as developers and discover new solutions to problems we have already solved, tech debt is the name that we give to those past decisions. Tech debt is unavoidable in development, it's the cold reminders of our past choices which can lead to very heated debates around future choices. Hopefully the above list of possible issues and how to overcome them will allow you to more quickly move onto making new tech debt for the future rather than just discussing old tech debt.