Taming Technical Debt

·9 min read·Graham Mace
Taming Technical Debt

(Originally drafted in October 2017 and August 2018 during my tenure at Xero. Republished here in October 2026 with updated context and removed proprietary references)

Code is not what’s going to kill your business – technical debt is.

Originally coined as a financial term, technical debt effectively means “borrowing from the future.” In other words, it’s a debt that needs to be paid back at some point – and the longer it’s left unchecked, the higher the interest.

My experience has taught me this is generally discovered through incidents: substandard code leads to substandard outcomes, which lead to unexpected problems. Spending time fixing those problems – paying back the debt – therefore increases the overall cost of ownership for a system.

Whilst code can now be produced at a rate of knots using LLMs, not all code is created equal. Therefore, not all technical debt is equal. And Chesterton’s Fence teaches us that you shouldn’t remove something (like a fence) until you understand why it was put up – because quick changes cause unintended problems if you don’t know the reason behind the existing system.

I’m not going to lecture you on what technical debt is; you can do that all by yourself through a quick Google search. Instead, I want to walk through two things I’ve found genuinely effective over the years when it comes to living with the stuff: how to capture it in a way that works for both engineers and product people, and how to keep it on the radar so it doesn’t quietly rot into something much worse.

What debt?

First, some context. Any long-lived codebase accumulates scar tissue - decisions that were made early on, often for very good reasons at the time (customer needs shifting, time-to-market pressure, requirements nobody saw coming), that come back to bite you years later.

If you have ever performed data migrations, then you’ll know that data has meaning, and that databases are the hardest part of a system to change. Code, on the other hand, is incredibly easy to change. However, unless you understand the domain, you won’t understand the meaning – which is exactly why debt accumulates the way it does, and why it’s rarely as simple as rewriting the offending class.

The teams I’ve worked in talked about these issues constantly, informally, in passing – and every time, the same topics would come up. So we decided to actually start capturing them somewhere, even if we didn’t currently have the time to sort them out right there and then.

For us, technical debt includes things such as:

  • Performance issues, like needlessly hitting the database with multiple queries where one would suffice
  • Concerns around security practices, including the sheer number of third-party NuGet packages that are installed and can’t be updated due to coupling
  • Classes that are overly complex and should either be cut down or replaced altogether with well-tested third-party libraries that achieve the same or similar functionality
  • Classes that are doing too many things, or classes that aren’t what they say they are
  • Functional areas that could be grouped together to achieve the Microservices Dream™
  • Architectural decisions made to work around a third-party library’s quirks (and some that were just bad, including hitting the database directly from the BFF layer)
  • Classes being used across more layers than they should be
  • Multiple libraries being used to achieve the same functionality
  • Classes that are stateful when they shouldn’t be
  • Or, frankly, things that just flat out annoy the team

Meeting in the middle

Here’s the underlying problem in most teams:

  • Developers are mostly technical. They have deep, low-level knowledge of the issues.
  • Product Owners are mostly customer-focused. They need to understand the impact of the issues at a high level.

So there’s a great deal of knowledge about technical debt locked in the heads of developers that never gets formalised. Where it has been captured, it’s often so technically-focused that a Product Owner can’t reasonably compare a tech debt item against a feature story, and so the value of fixing the debt stays invisible.

Meanwhile, the questions a Product Owner naturally asks - who does this affect? how many users? what’s the impact of not fixing it? how large is it? - go unanswered.

The trick is to find a way to meet in the middle.

Step one: Cranial scraping

Since this is technical debt, we wanted it stored somewhere visible at a business level (so nobody forgot about it) without drowning business readers in the technical detail.

Start small: a Trello board (or any lightweight kanban board) with a template card along these lines:

Problem

  • Abstract: a description of the problem from a high level
  • Details: a description of the problem from a lower level

Solution

  • Abstract: a description of the proposed solution from a high level
  • Details: a description of the proposed solution from a lower level

NFRs addressed: a checklist of the non-functional requirements that fixing this issue would improve or affect

Trello card

Open the board as a free-for-all to the entire team. Anyone and everyone adds items as they wish, and leave it open as long as needed for the cranial scraping to take place. It’s not massively important that every card is completely filled out – but the more information added, the better. Organise the columns however suits the team: by product area, by type of debt, by status.

Step two: turning the invisible into user stories

Once the board has a good pile of items on it, the fun part: a workshop where the team takes each item and turns it into a customer-facing backlog story. It really helps to have the Product Owner in the room for this, to make sure the voice of the customer is actually heard.

While converting items, keep asking customer-facing questions:

  • What? What’s the issue from the customer’s perspective? What’s the impact on them?
  • Why? Why is this an issue for the customer? Why is it important to fix?
  • How? How does it manifest for the customer? How long would it take to fix?
  • Who? Who in the customer base does this affect?
  • Where? Where in the product is this an issue?

In one workshop I ran, the backlog had accumulated around thirty-six items, so the team split into six groups and spent roughly an hour working through six items each. Stories went straight into the backlog, linked back to the original cards.

Something lovely starts to happen here: developers start thinking with a customer-focused lens, and Product Owners gain a shared understanding of how the debt is affecting real users.

Overall process

A concrete example

Here’s the transformation that convinced me. One team had this item on its backlog for months:

“Our logging library is not working properly”

It sat there, chilling in the background, not getting much love. The Product Owner thought that not logging errors within the system wasn’t such a bad thing. After all, it’s not customer-facing, right? What’s the point in fixing it?

Then it got rewritten as:

“We are not logging certain failures in our system, so we have no visibility on how many customers are impacted by particular issues.”

Suddenly the Product Owner realised this was a much bigger deal than they’d assumed. Sensing the urgency, the item was prioritised straight to the top and remediated a sprint later. The developers finally got buy-in to fix an issue that had plagued the system for months, and the Product Owner got a better idea of which customers were affected by which issues. Win-win.

But what about the big, hairy ones?

Some issues are so rooted in the foundations of the product that fixing them wholesale could potentially screw over the software and bring down production.

Treat those like any other big, hairy problem: create an epic and go from there. During backlog refinement, break it down into smaller, more manageable chunks. Over time, bring those chunks into sprints and chip away until the overall issue is fixed.

Time is of the essence

None of this matters if there’s no capacity allocated to actually paying the debt down. The agreement I’ve found works best: allocate a fixed percentage of the team’s time each sprint to tackle debt items from the backlog. We used 20%, and having properly classified stories in the backlog made it trivial to pull one or two in at a time.

If an item isn’t finished in that sprint, that’s fine – either carry it through to the next sprint where it’s (hopefully) finished, or remove it from the sprint entirely. Some debt is long-running by nature (improving unit test coverage, for example), and the register at least gives you a record of what’s been achieved and what’s still to go.

Challenges you’ll hit anyway

A few things to watch out for:

  • “It annoys me, therefore it’s urgent.” Technical people are more sensitive to issues that impact their day-to-day work, but they may have no way to make that visible to the Product Owner. This is an exercise in evidence-gathering: if the issue is performance, capture numbers showing how slow the application is, and use them to help prioritise.
  • Story phrasing. If a story is phrased by a non-technical person, it tends to be easier to understand. With the customer at the forefront of their mind, they’re probably best placed to ask “who is impacted?” and “how?” – so pair a developer and a product person when writing these.

Wrapping up

The point of all of this is the translations:

  • Translate the debt out of developers’ heads and into durable, retrievable form
  • Translate the technical description into a customer-facing one, so it can compete fairly with features
  • Translate the “we should really do something about that someday” into committed sprint capacity

Debt you can see is debt you can argue about; debt you can argue about is debt you can eventually pay down. And with LLMs making it cheaper than ever to take on debt, the discipline of paying it down matters more, not less. The alternative is the kind that quietly compounds until the interest payments come due on a Friday afternoon in production – and if you need a reminder of what unchecked debt looks like when the bill arrives, ask yourself what Chesterton would say about that fence you just removed.

Related Posts