AI Adoption GuideSoftwareBuild
Tech Debt Classifier
ML scores modules by complexity, coupling, and change frequency to produce a prioritized refactoring queue, using tools like SonarQube AI.
Software processDiscoverDesignBuildTestReleaseAdoptSupportRetire
By Don, DoneThat’s AI coach · updated
What a tech debt classifier does
A tech debt classifier turns scattered quality signals into an ordered backlog of modules that most deserve refactoring attention. Instead of debating which package “feels” worst, the system scores units of code (packages, services, or directories) on measurable drivers of maintenance cost: structural complexity, coupling to the rest of the system, and how often the module actually changes.
The output is a ranked queue, not a mandate. Rankings help a tech lead allocate limited refactoring capacity toward areas where friction compounds: high complexity, tight coupling, and frequent edits in the same place. Tools in this family often sit beside static analysis platforms such as SonarQube AI, which already emit complexity, duplication, and rule findings that a classifier can consume as features.
This page is for leads who already know debt exists and need a defensible order of work. Related practices that feed or consume the same signals include Agentic Feature Implementation, Automated PR Review, and Codebase-Aware Code Completion.
Inputs the model needs
Useful rankings depend on three input families. Complexity metrics (cyclomatic complexity, cognitive complexity, nesting depth, size) describe how hard a module is to reason about. Coupling metrics (fan-in, fan-out, circular dependencies, boundary violations) describe how expensive a change is to isolate. Change frequency comes from version control: commit density, unique authors, and churn over a recent window on the same paths.
Static analysis alone is not enough. A complex but untouched module may be painful in theory and cheap in practice. A frequently edited module with moderate complexity often costs more week to week. The classifier combines both so the queue reflects operational pain, not only structural smell.
When complexity signals or change-frequency history are missing for a module, the classifier should emit empty output for that module rather than invent a score. Partial data produces confident-looking ranks that are not comparable across the codebase. Prefer a short queue of well-supported candidates over a long list padded with guesses.
Telemetry hygiene matters before model quality does. Align path boundaries with ownership maps, exclude generated and vendored trees, and use a stable lookback window so rankings do not swing wildly between runs. Document which metrics feed which features so reviewers can challenge a high rank with evidence, not opinion.
How prioritization typically works
Most practical systems compute a composite score per module, then sort descending. Complexity and coupling raise the cost of each change. Change frequency raises how often that cost is paid. Some pipelines add secondary signals such as defect density from issue trackers, test coverage gaps, or hotspot flags from the VCS, but those remain optional amplifiers once the core three inputs exist.
The ranked list should be interpretable. Each row needs the module identifier, the overall score, and the contributing factors (for example, high fan-out plus elevated churn). Without that breakdown, teams treat the model as a black box and ignore it when politics conflict with the ranking.
Refresh cadence should match how the codebase moves. Weekly or per-release recomputation is common for active monorepos. Recompute after large migrations so temporary churn does not permanently dominate the queue. Keep historical snapshots so you can see whether last sprint’s top modules actually improved after focused work.
SonarQube-style platforms already centralize many static features. The classifier’s job is not to replace those findings; it is to turn them, plus VCS churn, into an ordered queue a lead can schedule against. Rule counts and quality gates still matter for PR policy; the classifier answers a different question: where should scarce refactoring time go next?
Where human judgment stays in control
The model ranks modules. The team still schedules work. Product deadlines, regulatory freezes, hiring plans, and upcoming platform bets routinely override a pure score order, and that is expected. Treat the queue as decision support: a shared, evidence-backed starting point for planning conversations.
Human review should confirm that a high-ranked module is a coherent ownership unit, that refactoring it will not collide with an imminent rewrite, and that the proposed slice is small enough to finish. A lead might split a top-ranked package into smaller extraction steps, defer a module pending a library upgrade, or raise a lower-ranked area because an on-call rotation is burning there despite quieter metrics.
Keep a thin process around the queue. In planning, pull the top N supported candidates, discard empty or incomplete rows, and assign owners only after a short design note for the intended refactor. Tie acceptance to measurable movement on the same inputs (complexity down, coupling reduced, or churn spreading away from the hotspot), not to “the model said so.”
Do not auto-open tickets from every score change. Automated ticket spam erodes trust. Prefer a human-gated export into the backlog once per planning cycle, with the score and feature breakdown attached as context for whoever picks up the work.
Failure modes and empty output
The most important failure mode is scoring without required inputs. If complexity metrics are unavailable (analysis never ran, paths unmatched) or change-frequency cannot be derived (shallow clone, missing history, wrong path mapping), return empty output for those modules. Do not substitute zeros, defaults, or peer averages that quietly invent priority.
Other failure modes are subtler. Generated code can dominate complexity. Mirror and bot commits can inflate churn. Coarse package boundaries can hide a single hot file inside an otherwise quiet tree. Mitigate with exclusion lists, author filters, and optional file-level rollups when directory scores look suspiciously flat.
Watch for gaming and for metric drift. If teams chase score drops by renaming packages or splitting files without reducing real coupling, the queue loses meaning. Revisit feature definitions when the static analyzer or VCS tooling changes so historical comparisons stay honest.
When the entire run produces empty or near-empty output, treat that as a pipeline problem, not as a clean codebase. Fix analysis coverage and history depth first. Only then trust the ranked queue enough to influence roadmap tradeoffs.
How it fits the build stage
In the software build stage, quality outcomes come from how code is structured and how change lands over time. A tech debt classifier sits after analysis and before capacity planning: it consumes build-time and repo signals, then informs which structural improvements compete with feature work.
Upstream, automated review and codebase-aware completion reduce new debt at the point of change. Downstream, agentic feature implementation benefits when high-coupling hotspots are known, because agents and humans alike struggle more in tangled modules. The classifier does not implement the refactor; it makes the next structural investment easier to justify and easier to sequence.
Success looks modest and operational. Leads open planning with a short, comparable list. Incomplete modules stay blank instead of speculative. Refactors land against named hotspots, and subsequent runs show those modules dropping or leaving the top of the queue. That loop, score, human schedule, measure again, is the whole practice.
Is this worth automating for you?
Whether this pays back depends on how much time it takes your team today. Most teams estimate that from memory, and the estimate is usually wrong in one direction or the other.
DoneThat reconstructs where the time actually went, with no timers to forget, so you can measure the baseline before committing to a project and check the gain afterward.
Measure the baseline first