Technical Debt Isn't a To-Do List — It's a Budget You Have to Manage
Every engineering org I've been part of has a graveyard of tech debt tickets. A backlog column, sometimes literally labeled "Tech Debt," with forty to sixty items in it, most written eighteen months ago, half of them no longer relevant because the code they describe doesn't exist anymore. Everyone agrees the debt is bad. Nobody can tell you which three items in that pile would actually move the needle if fixed this quarter. That gap — between vague guilt and actionable prioritization — is where most tech debt strategies die, and it's the thing I want to actually fix here, not just restate.
The framing that changed how I think about this came from, oddly, personal finance. Debt isn't inherently bad. Debt is a tool you use deliberately to move faster now in exchange for a cost later, and the mistake isn't taking on debt, it's taking on debt you didn't track, don't understand the interest rate on, and have no plan to pay off. Treat tech debt the same way: every shortcut is a loan, and your job as an engineering org is to know the balance and the interest rate, not just to feel bad about the principal.
A framework that actually produces a ranked list
The reason "write it down and revisit later" fails is that writing something down doesn't give you a way to compare it against everything else competing for engineering time. You need a scoring model, even a rough one, applied consistently. Here's the one I've used and refined across a few teams.
For every tech debt item, score three things on a 1-5 scale. Interest rate: how much does this cost you per unit time it stays unfixed? A slow test suite that costs every engineer ten minutes a day is high interest. An awkward internal API that nobody touches except twice a year is low interest, even if it's ugly. Blast radius: how much of the codebase or how many teams does this touch? A messy internal script used by one person is low blast radius. A shared auth middleware with no tests is enormous blast radius, because everything downstream inherits its risk. Fix cost: how much effort to actually pay it down? Be honest here, and time-box the estimate the same way you would a feature.
Then compute a simple priority score: interest rate times blast radius, divided by fix cost. High interest, high blast radius, low fix cost items float to the top — these are your "why haven't we done this yet" items. Low interest, low blast radius, high fix cost items sink to the bottom, and honestly, some of those you should just formally decide never to fix and remove from the backlog entirely. A ticket that's sat for two years with a low score isn't debt you're managing, it's backlog debt itself.
I keep this in a simple spreadsheet or a custom field set in Jira/Linear — nothing fancy. A row for "no integration tests on billing" with interest 5, blast radius 5, fix cost 3 scores 8.3. A row for "legacy jQuery admin panel" with interest 2, blast radius 2, fix cost 5 scores just 0.8. A row for "duplicate date-parsing logic" with interest 3, blast radius 4, fix cost 1 scores 12.0, the highest of the bunch.
That duplicate date-parsing logic item, ranked highest here, is the classic case people underrate — it's cheap to fix, but it's silently costing correctness bugs across four different codepaths. Meanwhile the jQuery admin panel, which feels embarrassing and generates the most hallway complaints, is actually the lowest priority by the numbers, because almost nobody touches it and rewriting it is expensive. This is exactly the kind of mismatch between gut feeling and actual cost that a scoring model is for.
Instrumenting debt instead of just listing it
The best tech debt programs I've seen don't rely purely on a backlog list — they instrument the codebase to surface debt automatically. A few things I've found genuinely useful.
Track build and test suite duration over time as a real metric, not a vibe. If your CI pipeline went from 4 minutes to 22 minutes over eight months and nobody noticed because it happened one PR at a time, that's classic boiling-frog debt. Graph it.
echo "ci_duration_seconds=$SECONDS" | curl -X POST -d @- https://metrics.internal/ci
Track flaky test rates. A test that fails 5% of the time isn't a broken test, it's debt with a specific, measurable interest rate: every flaky failure costs someone a re-run and a moment of doubt about whether their change broke something.
Use static analysis for the boring stuff — cyclomatic complexity, duplicate code detection, dependency freshness — but don't mistake a dashboard full of red for a prioritization tool. It tells you where debt exists, not which debt is worth paying down first. That's still the scoring model's job.
Talking to people who don't write code
This is the part most engineers actively avoid, and it's the part that determines whether debt paydown ever gets calendar time. The mistake is pitching tech debt in technical terms to people who allocate budget in business terms. Nobody outside engineering is moved by "our test coverage is low" or "we're on an old version of the framework." They are moved by consequences they already care about.
I've had success translating debt items into one of three business-legible categories: velocity ("features in this part of the codebase now take 3x longer to ship because of X"), risk ("this system has no tests and a security incident here would take down checkout"), and cost ("this inefficient query is why our database bill doubled"). Concretely, instead of a ticket titled "refactor the payments module," I've pitched the same work as "payments features have taken an average of 3 sprints instead of 1 for the last two quarters, and the last two production incidents both originated in this module — fixing the underlying structure gets us back to normal velocity and reduces incident risk." That's a sentence a VP of Product can put in their own status update. That's the difference between a ticket that sits for two years and one that gets prioritized this quarter.
Actually getting it into the roadmap
The single most effective mechanism I've seen for making debt paydown durable, rather than a one-time cleanup sprint everyone forgets about, is reserving a fixed percentage of every sprint's capacity for it — not as a stretch goal, but as a line item with the same seriousness as feature work. Numbers I've seen work in practice range from 15% to 25% depending on how old and how neglected the codebase is. The key is that it's negotiated once, at the roadmap level, not re-litigated sprint by sprint, because if it's up for debate every planning meeting, it loses every time to a customer-facing feature with a Q3 deadline.
I've also seen teams pair every debt-heavy area with a "debt owner" — not someone who does all the work personally, but someone accountable for knowing the current state and championing it in planning, the same way a service has an on-call owner. Debt without an owner has no advocate in the room when priorities get cut, and it will always lose to a feature that has a named champion and a deadline attached.
The teams that manage debt well don't have less debt than everyone else — every codebase accumulates it, and taking some on deliberately to hit a deadline is often the right call. What they have is visibility into the balance, a consistent way to compare items against each other, and a standing mechanism that guarantees some fraction of capacity actually goes toward paying it down instead of it living forever as guilt in a backlog column nobody opens.
Related Posts
Sponsor Our Newsletter
Reach thousands of developers who are actively evaluating AI tools, MCP servers, and dev infrastructure. Our weekly newsletter goes to engaged technical decision-makers.
All sponsored content is clearly labeled per our editorial policy.