NIPUNO. ← Log

Not all tech debt is the same debt

Most "tech debt backlogs" are a single flat list. A hundred ESLint warnings, a missing test, a useEffect nobody trusts, a component nobody wants to touch — all filed the same way, all fought over in the same triage meeting, all eventually deprioritized because nothing on the list is scarier than shipping the next feature.

That's the actual failure mode. Not "too much debt" — debt treated as one undifferentiated pile, so nothing on it ever looks urgent enough to fix.

Two things matter more than "how much debt"

Does it change behavior? An unused import, a variable that should be camelCase, a component that could be destructured more cleanly — none of that changes what the code does. It's safe to fix in bulk, safe to automate, safe to ship without a second look.

Now take something that looks just as small: a useEffect with a dependency array that's missing a value, or quietly including one it shouldn't. Fixing it can change when the effect runs — which can change what data loads, what re-renders, what a user sees. Same diff size as the unused import. Completely different risk.

Treating both the same way is how "cleanup" work quietly introduces regressions, and it's also why teams get gun-shy about touching lint findings at all — once you've been burned once by a "trivial" fix that wasn't, everything on the list starts to feel dangerous, including the parts that genuinely aren't.

The fix: split the backlog on this axis before anything else. Lint-only, cosmetic, no-behavior-change work ships fast, in bulk, without a regression pass. Anything that touches real behavior — data fetching, caching, effects, mutations — gets flagged for actual test coverage and a proper review before it merges. Two different backlogs, two different levels of scrutiny, one honest list of what's actually risky.

Ship it in pieces, not a cleanup sprint

The other reason debt backlogs die: someone schedules "a sprint for tech debt," it turns into a sprawling PR nobody wants to review, it slips, and the next roadmap discussion quietly drops it.

Small, PR-sized batches — one module, one category, one afternoon of review — actually ship. They're boring, individually unimpressive, and they compound. A tracked backlog of shippable slices beats a heroic cleanup effort that never lands, every time.

Coverage should follow impact, not a percentage

"Get coverage to 80%" is a number, not a strategy — it rewards testing whatever's easiest to test, which is rarely what actually matters. The better question is: which code paths, if they silently broke, would cost real money or break something a customer depends on? Cover those first. A codebase with 40% coverage concentrated on its riskiest paths is in better shape than one with 80% coverage padded out by trivial components.

Why this is the part that matters

None of this is about having an opinion on AI tooling. It's a discipline problem that existed before agentic coding tools and still exists now — arguably more, since AI-assisted changes make it easier to generate a large diff fast, which makes "was this safe to ship in bulk?" a sharper question, not a softer one. The tooling accelerates the mechanical work. The judgment about what's safe to batch, what needs a human second look, and what actually deserves test coverage is the part that doesn't automate away — and it's the part most "we'll clean up the tech debt" efforts skip entirely.

If your team has a debt backlog nobody trusts enough to actually work through, that's usually not a volume problem. It's a triage problem.

Have a backlog like this? Get in touch →