You heard it before: "Yeah, that's some old hack we have to fix later." Sounds familiar? Maybe too familiar? Some old code, some "hack", some comment "Do not touch!".
I would argue, what we keep calling "technical debt" is a misleading metaphor.
What we call "technical debt"
Debt is static. You borrow, you owe, you repay. You can structure it, refinance it, even ignore it for a while. The important part: the amount is known, and repayment (refactoring) reduces it.
Most "technical debt" does not behave like that. In practice, technical debt usually refers to:
- Code that is hard to understand or modify
- Missing tests or weak guarantees
- Leaky abstractions or inconsistent patterns
- Outdated dependencies or architecture decisions
- Performance shortcuts that no longer hold
We label it "debt" because it slows us down later. We only pay the "minimum fee" for now. But that framing hides the real problem.
Why it’s not "debt"
Debt assumes a fixed principal. But messy code doesn’t sit still. It compounds through usage.
Every time you...
- Add a feature
- Fix a bug
- Integrate a new system
...you interact with that code. And each interaction becomes slightly more expensive than the last. Not because the code changed, but because the context around it did.
I argue: that’s no debt but a multiplying tax!
Why "technical tax"
A better model could be: technical tax is a cost applied every time you touch a part of the system.
The worse the code, the higher the tax rate.
- Clean module: low tax, fast iteration
- Tangled legacy service: high tax, slow and risky changes
And crucially: You don't "pay it off" once. You pay it repeatedly!
If a piece of code is touched 100 times per month, even a small inefficiency becomes expensive. If it's critical-path code, the tax multiplies across the entire team.
What this changes
If you think in terms of debt, you optimize for payoff events:
- "We’ll refactor this later"
- "Let’s allocate a sprint to clean this up"
If you think in terms of tax, you may optimize for a multiplier reduction:
- Lower the cost of common paths
- Improve code that is frequently touched
- Replace atomic entities with a parallel rewrite
- Ignore rarely used areas, even if they're ugly
This leads to different decisions:
- Refactor hotspots, not entire systems
- Measure change frequency, not just code quality
- Accept "bad" code if it's cold
- Prioritize developer experience on critical paths
The (simple) example
You have a billing module everyone is afraid to touch (who hasn't ^^).
- Adding a feature takes 3 days instead of 1
- Every change requires manual verification
- Bugs are subtle and expensive
That's not a fixed debt you can "pay off." It’s a 3x tax on every future change.
Now compare that to a messy admin tool used once a month. It might be worse code, but the tax is negligible.
What to do
Treat technical debt like a tax system:
- Identify high-traffic areas (git history, ownership, deploy frequency)
- Reduce friction where it matters (tests, simplification, better boundaries)
- Avoid over-investing in low-impact code
- Continuously shave off tax instead of planning big payoffs
Small, targeted improvements beat large refactoring projects that never land.
Calling it "debt" suggests a future problem. Calling it a "tax" makes it a present, ongoing cost. And once you see it that way, the goal is obvious: Evade the tax and become rich in code.
Top comments (0)