Technical debt isn't a synonym for bad code — it's a genuine, sometimes correct engineering trade-off: shipping a faster, less ideal solution now, in exchange for owing cleanup work later. The skill is naming the trade-off explicitly and actually tracking the debt, not avoiding it entirely.
3 min read
Technical debt is a deliberate metaphor: like financial debt, taking on a quick, imperfect solution now can be the correct choice — it lets you ship sooner, exactly like a loan lets you buy something sooner than saving up would. The debt itself isn't the mistake. The mistake is either taking on debt you never intended to (accidental debt) or taking on debt and never paying it back (interest compounding until it cripples future work).
Deliberate debt is a conscious trade-off, ideally made out loud: "we're hardcoding this configuration instead of building a settings UI, to hit the launch date — we know this needs to change once we have more than one customer." Accidental debt is different — code that became debt without anyone deciding it should be: a design that made sense for the original scope but never got revisited as requirements grew, or a shortcut taken under pressure that nobody flagged for follow-up. Deliberate debt, named explicitly, can be tracked and prioritized. Accidental, unnamed debt just quietly accumulates until someone is surprised by how hard a "simple" change turns out to be.
Taking on debt is often the right call specifically when:
It's usually the wrong call when:
The single highest-leverage habit here: when you knowingly take on debt, say so somewhere it'll actually be seen again — a tracked issue, a clearly labeled comment at the exact spot, a note in a pull request description. Debt that exists only in the memory of whoever wrote it effectively doesn't exist for anyone else, and often not even for that same person eight months later.
# TECH DEBT: hardcoded to the first pricing tier only — fine while we
# have one customer, needs a real pricing table before the next signup.
# Tracked in ISSUE-482.
def calculate_price(usage):
return usage * 0.10Teams that never explicitly track debt often intend to "clean it up later" in some vague future — but later never has a specific trigger, so it competes against every new feature request with a real deadline, and reliably loses. Debt that's tracked as a specific, visible item (a ticket, a line in a backlog) at least has a chance of being prioritized deliberately; debt that only exists as a vague feeling that "this part of the codebase isn't great" almost never gets scheduled at all.
Recognizing debt is only useful if there's a safe way to actually pay it down without breaking what already works — which is exactly the subject of the next lesson: refactoring as a disciplined, low-risk process, not a rewrite-and-hope.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. What's the difference between deliberate and accidental technical debt?
2. When is taking on technical debt generally the right call?
3. Why does technical debt that's never tracked anywhere tend to become permanent?
Software Engineering Fundamentals