Technical debt is one of the most overused phrases in software development. Engineers reach for it when something is messy. Managers hear it as an engineering complaint dressed up in financial language. Neither framing gets you anywhere useful.

The actual definition matters. Technical debt is the accumulated cost of decisions that made sense at the time but now make the system harder to change. Not bad code for the sake of bad code. Not code written by someone who didn't know better. Decisions — deliberate or not — that the system is now paying interest on with every change you try to make.

That framing shifts what you're looking for and how you talk about it.

What You're Actually Looking For

Most technical debt doesn't announce itself. You find it indirectly, through the behaviour of the people working on the system.

The clearest indicator is what engineers say when a new feature lands on the backlog. If the first response to a straightforward request is "that's going to be complicated" — without a clear explanation of why — you have debt. Complexity that can't be explained is complexity that's been absorbed into the system's structure and is no longer visible to the people working in it.

Watch the shape of your bug reports too. One-off bugs are normal. Bugs that cluster around the same area of the codebase, sprint after sprint, suggest a part of the system that nobody fully understands anymore. Every fix there is provisional — patching around a structural problem that nobody has the confidence or the time to address directly.

Other signals worth tracking:

  • Pull requests that grow unexpectedly large. A small feature touches ten files. The PR description apologises for the scope.
  • Onboarding takes longer than it should. New engineers need months, not weeks, to ship independently. The codebase doesn't explain itself.
  • Areas of the system that the team avoids touching. "Nobody really knows how that works" is a phrase that should concern you every time you hear it.
  • Tests that slow development instead of enabling it. Either there aren't enough tests to catch regressions, or the tests are so brittle that any refactor breaks them.

None of these are definitive on their own. Together, they map the parts of your system where debt has become structural — where the cost isn't theoretical anymore, it's showing up in your team's output every sprint.

The Measurement Problem

The instinct is to quantify. How much debt do we have? What's the score? There are tools that will give you numbers — cyclomatic complexity, code coverage, duplication ratios. They're worth looking at, but they're not the answer to the question you're actually trying to answer.

A function with high cyclomatic complexity might be a problem or it might be a mature, battle-hardened piece of business logic that's been stable for five years. A file with low test coverage might be trivial glue code or a critical calculation that nobody has ever tested properly. The numbers don't tell you which.

What you're trying to understand is whether the debt in your system is incidental or structural. Incidental debt — untidy code, missing tests, inconsistent naming — is annoying but manageable. You clean it up as you go. Structural debt is different. It's baked into the architecture: the data model, the coupling between components, the abstractions that were built for the first version of the product and never revisited as the product changed. That's the debt that degrades velocity over time and doesn't respond to incremental cleanup.

Measurement is most useful here not as an absolute score but as a trend. If your cycle time for shipping a feature is increasing quarter on quarter, and the feature complexity hasn't changed, you have structural debt accumulating. That trend is the number worth tracking.

Why the Fix Never Gets Prioritised

Engineers know the debt is there. They bring it up in retrospectives, in planning sessions, in Slack. Nothing happens. This pattern is so common it's practically a law of software development.

The reason isn't that decision-makers are short-sighted or don't care about engineering quality. It's that debt doesn't appear on any report they look at. Revenue does. Feature delivery does. Uptime does. Technical debt shows up as friction, and friction is easy to normalise.

Every sprint is a little slower than it should be. Every estimate is a little padded to account for unexpected complications. Every feature takes a bit longer to test because touching one thing always risks breaking another. None of these look alarming in isolation. They're just how software development works, right?

That normalisation is the trap. The compounding effect of accumulated debt is invisible until it isn't — until the quarter where your team shipped half of what they committed to, and the post-mortem points to the codebase every single time.

Making the Business Case

The mistake engineers make when arguing for debt reduction is framing it as a quality argument. "The code is messy." "We should do this properly." These arguments lose because they sound like personal preferences, and they compete against features that have a clear business owner and a clear deliverable.

The business case for fixing technical debt is a velocity argument, and velocity has a cost.

Start with your cycle time trend. If features that used to take two weeks now take four, you've lost half your team's output to friction. At whatever your fully-loaded engineering cost per hour is, that's a real number. Not an estimate. Not a theoretical future cost. A current, ongoing loss.

Add the risk dimension. Which parts of your codebase, if they broke unexpectedly, would cause significant customer impact or data loss? If the answer includes areas that nobody fully understands and that have never had a proper incident response exercised on them, that's a risk that belongs on the same register as your other operational risks — not buried in an engineering backlog.

The question to put to decision-makers isn't "can we fix the code?" It's "how much is the current state of the code costing us per sprint, and is that a better use of money than fixing it?"

That framing changes the conversation. It stops being a request for engineering time and becomes a business trade-off with a quantifiable cost on one side. Decision-makers can work with that.

One more lever worth using: new features. If there's a feature on the roadmap that everyone agrees is high value, and an honest estimate of the work includes a significant chunk of time paying down debt that's in the way — that's the moment to make the case. Attach the debt work to a deliverable that already has business sponsorship. The debt doesn't get funded in the abstract; it gets funded because it's blocking something concrete.

What Getting It Fixed Actually Looks Like

Debt reduction rarely works as a dedicated project. A two-month engineering pause to "fix the codebase" is hard to sell, and even harder to execute — the temptation to scope-creep it into a full rewrite is almost irresistible once you're in there.

What works is a sustained allocation. Twenty percent of every sprint goes to debt reduction — not negotiable, not traded away when the backlog gets heavy. This isn't glamorous, but it compounds. A codebase that gets consistent, intentional attention gets better. One that only gets cleaned up when there's a crisis gets worse.

The specific work matters too. Not all debt reduction is equal. Cleaning up old variable names feels productive and barely moves the needle. Addressing structural debt — simplifying a data model, breaking apart a tightly coupled module, replacing an abstraction that's been fighting you for two years — is harder, takes longer, and has a disproportionate effect on velocity.

That distinction requires engineering judgment. Someone has to be able to look at the system and identify where the structural problems actually are, not just where the code is untidy. Which is why architecture-level thinking matters at this stage — the debt that's slowing you down most isn't usually in the files that look the worst. It's in the decisions underneath them.

The Question Under the Question

When a CTO or technical founder asks "how bad is our technical debt?", the question they're often actually asking is "should we rewrite this, or can we keep building on what we have?"

Debt on its own doesn't answer that. What matters is whether the debt is incidental — cleanable over time with the right habits — or structural, meaning it's in the foundations of the system and will constrain everything built on top of it regardless of how diligently the team tidies around it.

Most systems have both. The ratio between them, and where the structural debt sits relative to where the product needs to go, is what actually determines whether you clean up or start over. That diagnosis is harder than it sounds, and getting it wrong in either direction is expensive.

Armin Marxer writes at zeroclue.dev.

If you're trying to get a clear picture of where your technical debt actually sits — and whether it's worth addressing before the next build phase — we're happy to look at it with you.

Start a conversation →