The three kinds of technical debt
Deliberate, accidental, and structural. They need completely different responses and everyone calls them the same thing.
"Technical debt" has become a phrase that means "code I do not like," which has made it useless in the conversations where it matters — the ones where you are asking for time to fix something.
Three distinct things wear the label. They have different causes, different costs, and different correct responses.
1. deliberate debt#
You knew the better solution. You chose the faster one on purpose, for a reason.
"We are hard-coding this to hit the deadline. We will parameterize it in Q3."
This is the original meaning of the metaphor and it is a legitimate engineering tool. Borrowing against future effort to ship now is frequently correct, especially when you are not yet sure the thing will survive.
The failure is not taking on the debt. It is not recording it.
What makes deliberate debt manageable:
- A comment at the site, explaining the trade-off and the condition that would trigger the fix.
- A ticket, linked from the comment.
- A named condition: "when we have more than five customers on this path" is much better than "later."
# DEBT: hardcoded to the US tax table to ship for the March launch.
# Parameterize when we take our first non-US customer. See ENG-4417.
TAX_RATE = 0.0725That comment costs thirty seconds and it is the difference between debt and mystery.
2. accidental debt#
You did not know better at the time. The requirements changed. The library you chose turned out to be wrong. The abstraction fit the problem you had and not the one you have now.
This is the largest category and it is not anyone's fault. It is the natural consequence of building things under uncertainty.
The response is refactoring as part of ordinary work, not as a project.
The practice that works: when you touch a file, leave it slightly better. Not a rewrite — rename the confusing variable, extract the function that is doing two things, delete the dead branch. Small, continuous, in the same commit as the feature.
Refactoring projects — a quarter dedicated to cleanup — mostly fail. They are unfunded after the first month, they conflict with in-flight work, and they produce large risky changes with no user-visible benefit to justify them.
Continuous small improvement compounds and is nearly invisible in the process. That is the whole trick.
3. structural debt#
The architecture is wrong for what the system now does.
The monolith needs to be split, or the microservices need to be merged. The data model does not represent the domain. The synchronous design cannot support the scale. The framework choice from 2018 is blocking everything.
This is the expensive category and it is qualitatively different from the other two, because you cannot fix it incrementally without a plan. Small improvements to a wrong structure make the wrong structure more entrenched.
The response is a real project, with a real justification, sized honestly.
That requires:
- A specific cost statement. Not "the architecture is bad." "Every new feature in this area takes three times as long as an equivalent feature elsewhere, and here are the last four examples with dates."
- A specific benefit. What becomes possible or fast afterward.
- A path with intermediate value. A twelve-month rewrite with no deliverable until month twelve will be cancelled in month seven. Structure it so each phase ships something.
- A strangler pattern, not a rewrite. Build the new alongside the old, migrate incrementally, delete the old. Big-bang rewrites have a well-documented failure rate and the reason is always the same: the old system's behavior includes a decade of undocumented edge cases nobody enumerated.
how to talk about it#
The conversation that fails: "we need to spend time on technical debt."
The conversation that works: "the last four features in the billing area each took about three weeks. Equivalent features elsewhere take one. The difference is the data model, specifically that subscriptions and invoices share a table. Fixing it is about six weeks and would bring billing features back to normal velocity. We have eleven billing features on the roadmap."
The second version has: a measurement, a cause, a cost, and a payback period. It is a business case, and business cases get funded.
The first version is a complaint, and complaints do not.
the thing nobody says#
Some debt should never be paid.
Code in a system that will be retired, or that nobody has changed in three years, or that works and has no pending requirements — leave it. Ugly code that is stable and unmodified costs you nothing. It is not debt if you never pay interest on it.
The question is never "is this code good." It is "is this code costing us anything." A lot of what gets called technical debt is code that offends someone's taste in a module nobody touches, and refactoring it is a hobby, not engineering.
— Dom, April 24, 2026