The Debt Metaphor

Technical debt is bandied about the meeting rooms and corridors of tech startups and large enterprises, used to describe everything from messy code to lax security practices. And yet, its origins and pernicious effects are misunderstood.

Ward Cunningham first used “the debt metaphor” to explain the need to rewrite parts of the WyCash application to his boss.

In this video uploaded to his YouTube channel, Cunningham describes the need to explain investing time in modifying working software that would yield no new features nor fix any known defects.

Cunningham had rushed the process of building software with his limited knowledge of the future, and in the process, learnt more about the nature of the problem.

The Debt Metaphor cleverly explains the ongoing cost of using improper abstractions. If you don’t update your abstractions, every time you (mis)use them, you have interest to pay.

If we failed to make our program align with what we then understood to be the proper way to think about our financial objects, then we were going to continually stumble over that disagreement, and that would slow us down, which was like paying interest on a loan.

Cunningham goes on to say that this has nothing to do with writing poor code:

I’m never in favour of writing code poorly, but I am in favour of writing code to reflect your current understanding of a problem, even if that understanding is partial.

Metaphors are approximations, useful in explanation by association. They can be over extended, at which point, their value depreciates.

Labelling anything other than refactoring code as you go is potentially legitimising deeper issues.

Sharing passwords in plaintext via your internal chat tool is not an example of technical debt by my definition. Nor is working software sans its original author.

In Cunningham’s case, he wasn’t bodging the job. He was doing his best given his current understanding. Doing anything less than our best because of poor planning or unrealistic deadlines, is not tech debt. It’s a communication issue.

When looking up the definition of debt, a feeling of gratitude for a service or favour comes up. Compassion for the people working with a system is something all leaders need, and investing in useful software is money well spent.

The Cost of Misusing the Metaphor

When we mislabel poor practices as technical debt, we normalize dysfunction. Security vulnerabilities aren’t debt—they’re liabilities. Missing documentation isn’t debt—it’s negligence. Rushed, poorly-written code isn’t debt—it’s a choice to prioritize speed over craft.

The debt metaphor works precisely because debt, when used wisely, is a tool for growth. You borrow against future earnings to invest in opportunities today. Similarly, you might ship code that doesn’t perfectly model your domain because getting to market matters more than getting it right on the first try.

But just as financial debt requires a plan for repayment, technical debt demands continuous refactoring as understanding deepens. The interest manifests as friction—every feature takes longer to build, every bug becomes harder to fix, every new team member takes longer to onboard.

A Better Way Forward

Instead of accumulating a backlog of “technical debt” tickets that never get prioritized, consider these practices:

  1. Refactor as you go: When you touch code that doesn’t align with current understanding, update it. This is paying down the principal.

  2. Name things accurately: Call security issues what they are. Call missing tests what they are. Reserve “technical debt” for genuine misalignment between code and understanding.

  3. Invest in understanding: The root cause of legitimate technical debt is incomplete understanding. User research, domain modeling sessions, and collaborative design reduce the debt you’ll accrue.

  4. Make the implicit explicit: Document not just what the code does, but why it does it that way. Future developers (including yourself) need context to make good decisions.

The debt metaphor remains powerful when used correctly. It gives us language to discuss the temporal dynamics of software development—how decisions made today affect velocity tomorrow. But like any powerful tool, it can cause damage when misapplied.

When we hear “technical debt” invoked in meetings and standups, it’s worth pausing to consider what’s really being communicated. Sometimes it’s genuine—code that no longer reflects our evolved understanding of the domain. Often it’s something else: a budget that didn’t account for testing, a timeline that assumed perfection, or simply the accumulated friction of a system that’s grown beyond its original boundaries.

The distinction matters not for ideological purity, but because different problems demand different solutions. Misaligned abstractions need refactoring. Security vulnerabilities need immediate patches. Knowledge gaps need documentation. Communication failures need process changes. By recognizing what we’re actually dealing with, we can respond with precision rather than adding another ticket to an ever-growing “tech debt” backlog that serves primarily to assuage guilt about the distance between our aspirations and our reality.