Measuring Technical Debt: KPIs, Tools & Dashboards
Last updated . Sources are named and dated inline - how we source claims.
Until someone puts numbers behind it, technical debt is one team's opinion against another's. Learn how to quantify, track, and communicate it with data-driven metrics that hold up in front of both engineers and executives.
What This Page Covers
This page covers HOW to measure technical debt: which signals to instrument, where the raw data comes from, how often to collect it, how to read it honestly, and how to keep the numbers from being gamed. It is the canonical definition of the DORA metrics on this site - the other two pages below reference this one rather than redefining them.
For WHICH framework to adopt
SQALE, ISO 25010, the Technical Debt Ratio, the Maintainability Index, SPACE, and what each one is good for - and where each one misleads. See Measurement Frameworks.
For WHAT TO BUILD and show people
Dashboard layout, tile anatomy, refresh cadence, who reads which view, and what to do when a number moves. See Metrics Dashboard Guide.
"We have too much tech debt" is a feeling every developer knows. But feelings don't get budget. To actually reduce technical debt, you need numbers - and specifically, numbers you collected the same way every week, from a source nobody can quietly edit. This page is about that collection problem: what to instrument, where the data lives, and how to read it without fooling yourself.
Whether you're a developer building a case for refactoring time, a tech lead justifying tooling purchases, or a CTO presenting to the board, the discipline is the same. Pick a small set of signals. Collect them automatically. Never change the definition mid-year. Then argue about the trend, not about whose anecdote is more vivid.
Why Measurement Matters: The Numbers Don't Lie
hours of a 41.1-hour work week the average developer spends on technical debt
Source: Stripe, The Developer Coefficient (2018)
of accumulated technical debt across US software, on the most recent estimate
Source: CISQ, The Cost of Poor Software Quality in the US: A 2022 Report
rise in block duplication from 2023 to 2026, measured across 623 million code changes
Source: GitClear, The Maintainability Gap: AI Code Quality in 2026
The takeaway: the cost is large, it is measurable, and it is currently getting worse. None of that helps you until you can point at your own numbers instead of someone else's.
What to Instrument, and Where the Data Comes From
Every signal below names its source system. If you cannot say where a number comes from and who could change it, do not put it on a chart.
Code Quality Signals
Collected automatically from static analysis in CI. Source of truth: your analyser, on every merge to the main branch.
Code Coverage
From: test runnerPercentage of code executed during automated tests. Low coverage means more untested code that can break silently.
How to collect: emit an lcov or cobertura report from the test job and publish it as a CI artifact. Collect on every main-branch build so the series has no gaps.
100% coverage isn't the goal - test meaningful paths, not every getter.
Cyclomatic Complexity
From: static analysisNumber of independent paths through a function. Higher complexity means code that is harder to test, understand, and safely change.
How to collect: take the per-function distribution, not the codebase average. An average hides the twelve functions doing the damage. Track the count of functions above your own agreed limit, and watch whether that count grows.
Analysers disagree on how they count. Pick one tool and never compare its output to another tool's.
Code Duplication
From: static analysisPercentage of duplicated code blocks. Duplicates mean bugs get fixed in one place and remain everywhere else.
Tools detect blocks of identical tokens across files, so the number is sensitive to the block size you configure. Write that setting down next to the metric.
How to collect: report duplication per module as well as overall. A stable site-wide 3% can hide one service at 20%.
Dependency Age and Exposure
From: package manifestHow far behind your dependencies are, and how many carry a known advisory. This is the cheapest debt signal to collect and one of the most predictive of an unplanned weekend.
How to collect: a scheduled job running your ecosystem's audit command, plus a count of packages more than one major version behind and a count of packages whose upstream is unmaintained.
Track end-of-life runtimes separately. They are a date on a calendar, not a gradual trend.
Looking for the Technical Debt Ratio? That is a named framework rather than a raw signal - it converts analyser findings into estimated remediation time and divides by development cost. It is defined, with its assumptions and its failure modes, on Measurement Frameworks.
Delivery Signals: The DORA Metrics
Defined once here, for the whole site. Source of truth: your CI/CD system and your incident tracker.
Why DORA: DORA stands for DevOps Research and Assessment, the long-running research program now published by Google Cloud. Its delivery metrics are the closest thing the industry has to a shared vocabulary, and technical debt degrades all of them at once.
There are five of them now, not four. The famous "four key metrics" date from the original 2014 to 2023 research. DORA's current guidance names five, in two groups: throughput is measured by change lead time, deployment frequency, and failed deployment recovery time; instability is measured by change fail rate and deployment rework rate. Mean time to recovery has been renamed failed deployment recovery time. If you find a page - including older pages of ours - referring to "the four DORA metrics" without dating the claim, that page is behind.
The grouping matters more than the count. A team that improves throughput by accepting more instability has not improved, and reading either group on its own is how that trade gets hidden.
Source: DORA, DORA's software delivery performance metrics
Deployment Frequency
ThroughputHow often your team successfully releases to production. Debt suppresses it, because every release needs more manual verification than the last one.
How to collect: count successful production deployment jobs per week from your CI/CD system. Decide up front whether a rollback counts as a deployment, write the decision down, and never change it.
Gaming risk: splitting one release into five pipeline runs. Count releases that reach users, not pipeline executions.
Lead Time for Changes
ThroughputElapsed time from a commit landing on the main branch to that commit running in production. It measures the friction in the pipeline, and debt adds friction at every stage.
How to collect: join commit timestamps to deployment timestamps. Report the median and the 85th percentile together - the mean is dominated by the one change that sat in a release train for a month.
Gaming risk: starting the clock at "ready to deploy" instead of at commit. That measures your deploy script, not your delivery.
Change Fail Rate
InstabilityThe share of deployments that degrade service and require a rollback, a hotfix, or a patch. Debt raises it because tightly coupled, thinly tested code fails in ways nobody predicted.
How to collect: divide the number of deployments linked to an incident or a revert by the total number of deployments in the same window. The hard part is the link, so require every incident record to name the deploy that caused it.
Gaming risk: quietly reclassifying failures as "planned follow-up work". Define failure once, in writing, before you start counting.
Failed Deployment Recovery Time
ThroughputHow long it takes to restore service after a failed change. This is the metric formerly called mean time to recovery, or MTTR - if your tooling still uses that label, it is the same measurement under an older name. Debt lengthens it, because undocumented and tightly coupled systems are slow to diagnose.
How to collect: from your incident tracker, measure detection timestamp to service-restored timestamp. Report the median, not the mean, and keep the sample to deploy-caused incidents so it stays comparable to change failure rate.
Gaming risk: starting the clock when someone acknowledges the page rather than when the customer was first affected.
Deployment Rework Rate
InstabilityThe fifth metric, and the one most teams have never instrumented. It counts deployments made to fix a problem that a previous deployment introduced, without that problem having caused a user-visible failure. Change fail rate catches the outages; rework rate catches the quiet follow-up commits that never showed up on a status page but consumed a real share of the week. Together they describe instability far better than either does alone, and rework rate is often the first of the two to move when debt starts to bite.
How to collect: requires deployments to be tagged with intent. The cheapest workable version is a required field on the deploy - new work, or fixing prior work - filled in by whoever triggers it. Anything more sophisticated depends on commit conventions you probably do not have yet.
Gaming risk: the intent field is self-reported, so it is the softest signal of the five. Treat a suspiciously low rework rate as a data-quality question rather than an achievement.
There is no Elite / High / Medium / Low table here, and that is deliberate
For years this page and its neighbours carried a four-tier benchmark table, including a numeric band for an "elite" change failure rate. We removed it. DORA's current framework publishes no numeric performance-cluster thresholds, and the widely circulated numbers contradict each other: the two figures most often quoted for an elite change failure rate differ by a factor of three, and neither traces to a current edition. The 2025 DORA report moved away from the four clusters entirely, describing seven team profiles as narrative patterns rather than as numeric grades.
What to do instead: establish your own baseline over one full quarter, then trend against it. "Our median lead time fell from three days to eleven hours over two quarters" is a real finding you can defend. "We are now a high performer" is a claim you cannot source, and the first executive who checks will find the same contradiction we did.
Comparison across organizations is the weakest use of these metrics anyway. Deployment frequency in a regulated payments platform and in a marketing site are not the same quantity, however identical the axis labels look.
Source: DORA, The 2025 DORA Report: State of AI-Assisted Software Development
How to collect DORA metrics with no platform team (click to expand)
You do not need a data pipeline to start. In order of effort:
- Your CI/CD system already knows two of them. GitHub Actions, GitLab CI, Azure DevOps, and Jenkins all expose deployment job history through their APIs. That gives you deployment frequency directly, and change lead time once you join it to commit timestamps.
- Your incident tracker knows two more - provided every incident record names the deployment that caused it. If that field does not exist, add it before you add anything else. It is the single highest-value schema change on this page.
- Deployment rework rate needs a decision, not a tool. Add a required intent field to the deploy - new work, or fixing prior work - and you have it. Leave this one until the other four are running; it is the newest of the five and the least well supported by off-the-shelf tooling.
- A shared spreadsheet is a legitimate first month. Deploy date, incident start, service restored, rollback yes or no. It is manual and it is honest, and it will tell you whether the metrics are worth automating before you spend a sprint automating them.
- Commercial delivery-analytics tools such as Sleuth, LinearB, and Faros AI compute all four from your existing pipelines. Buy one when the manual version has proved the metrics get used, not before.
Backlog Signals
Source of truth: your issue tracker. Cost of collection: one label and a saved filter.
These are the cheapest signals on this page, because you are already logging the work. Apply a single tech-debt label in Jira, Linear, Azure DevOps, or whatever you use, then build saved filters over it. The discipline that matters is applying the label at creation time, every time - a label applied retroactively during a metrics review is a number you invented.
Debt Items Created per Sprint
New labelled items logged in the period. A rising count is ambiguous on its own: it can mean the codebase is decaying, or it can mean the team finally started writing things down. Read it next to the closure count.
Filter: created in period, label present.
Debt Items Closed per Sprint
Labelled items resolved in the period. Count items, and separately count estimated effort, because ten trivial closures and one architectural fix are not the same sprint.
Filter: resolved in period, label present.
Age of Open Debt Items
Median days since creation across the open labelled backlog. Old items are harder to fix than new ones: the context has faded and the code has moved on around them.
Use the median. One four-year-old ticket drags an average anywhere you like.
Critical Debt Count
Open labelled items at your highest priority: active security exposure, a dependency past end of life, or something that is blocking delivery outright. This one is a count, not a trend, and it should be reviewed at every escalation.
Filter: open, label present, priority highest.
Unplanned Work Share
Share of completed sprint capacity that was not in the sprint plan: production fires, emergency patches, and interrupts. It is the most direct measurement of what existing debt costs you every two weeks.
Requires that work added mid-sprint is flagged as added. Enforce that in the tool, not in a habit.
Planned Debt Allocation
Share of capacity deliberately budgeted to debt work at sprint planning. Track planned and actual separately: the gap between them tells you whether the allocation is real or ceremonial.
Compare against unplanned work share in the same period.
Collecting these counts is not the same as interpreting them. Turning created-versus-closed into a debt velocity, converting unplanned work into an interest rate, or analysing defects by the vintage of the code that produced them are modelling decisions. Those models live on Measurement Frameworks.
Developer Experience Signals
Source of truth: a survey you run yourself, plus timestamps from HR and CI. The only signals here that lead rather than lag.
Onboarding Time
Days from a new engineer's start date to their first merged change that touches core functionality. Complex codebases stretch this out, and the number is impossible to argue with because it is two dates.
How to collect: start date from HR, first qualifying merge from version control. Agree in advance what "core" means so the definition does not drift with each hire.
Local Build and Test Time
Time to build and run the test suite on a developer machine. Long builds destroy flow, and a build time that climbs steadily is one of the earliest structural warnings you get.
How to collect: the CI job duration is a usable proxy if the hardware is stable. Record the runner specification alongside the number, or a fleet upgrade will look like an engineering win.
Developer Confidence Survey
A short recurring survey asking engineers how it feels to change specific parts of the system. Keep it to five to eight questions, keep it anonymous, and keep the wording identical every quarter or the series is worthless.
Ask: "On a scale of 1 to 5, how confident are you making changes to [module X]?" Ask it per area, not once for the whole codebase.
Voluntary Engineer Turnover
Engineers who leave by choice, as a share of headcount. Debt drives frustration, frustration drives attrition, and attrition removes exactly the people who knew why the code is shaped that way.
How to collect: from HR, voluntary departures only. Tag exit-interview themes so you can tell a compensation problem from a codebase problem.
Business Impact Signals
Source of truth: incident records, support tickets, and delivery history. These are the ones that survive translation into a budget conversation.
Incident Frequency by Severity
Production incidents per month, counted separately at each severity. Every incident costs engineer time and some cost customer trust; a single blended count hides which kind you are getting more of.
From your incident tracker. Never merge severities into one line.
Delivery Throughput Trend
Completed work per sprint, tracked over at least a year. Declining throughput with a stable team size and stable scope is the clearest evidence that debt is taking a cut.
Only comparable within one team. Story points are not a currency between teams.
Escaped Defect Share
Of all defects found in a period, the share first reported by a customer rather than caught internally. A rising share points straight at your test and review gates.
Requires a "found by" field on every defect. Add it once.
Maintenance Versus New Capability
The split of engineering effort between keeping the current system alive and building something that did not exist before. This is the number a finance audience understands without translation.
Derive it from work categories in the tracker, not from asking people to guess at quarter end.
Measurement Tools Deep Dive
You don't need to measure everything manually. These tools automate collection and integrate with your existing workflow.
SonarQube - The Industry Standard
Broad language coverage, self-hosted or cloudKey Features
- 30+ languages: Java, JavaScript, TypeScript, Python, C#, PHP, Go, Ruby, and more
- Quality Gates: Automatically fail builds that don't meet standards
- Technical Debt calculation: Estimates hours to fix each issue
- AI CodeFix: One-click suggested fixes for issues found by static analysis
- PR decoration: Inline comments on pull requests
When to Choose It
- You have a mixed stack: One dashboard beats six language-specific linters nobody reads
- You want enforcement, not reports: Quality Gates fail the build, so the number cannot be ignored
- You need a baseline today: Community Build is free, so there is no procurement cycle between you and your first measurement
- Skip it if: You only need dependency updates - Dependabot or Renovate is a smaller tool for that job
Paid editions are licensed per instance by lines of code, and the terms move. See the canonical pricing table on the Tools page.
Quick Setup Guide (Click to expand)
1. Add to your CI pipeline (GitHub Actions example):
# .github/workflows/sonar.yml
# Note: the old SonarSource/sonarcloud-github-action is superseded.
# The current official action is sonarqube-scan-action - pin a release tag, not @master.
name: SonarQube Analysis
on: [push, pull_request]
jobs:
sonarqube:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: SonarQube Scan
uses: SonarSource/[email protected]
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}2. Configure sonar-project.properties:
sonar.projectKey=your-org_your-project
sonar.organization=your-org
sonar.sources=src
sonar.tests=tests
sonar.javascript.lcov.reportPaths=coverage/lcov.infoQlty - Prevention-Focused
Formerly Code Climate QualityKey Features
- One config, many linters: 70+ linters, formatters, and analysers behind a single setup
- PR-level analysis: Shows the quality impact of each change before merge
- Coverage gates: Threshold-based standards per project or org-wide
- Duplication and complexity: The two metrics that predict future rework best
When to Choose It
- Your debt arrives one PR at a time: Gates at merge stop the bleeding before dashboards matter
- You are drowning in linter config: One setup replaces a per-language pile of them
- Skip it if: You need DORA metrics - Code Climate Velocity, which used to supply them, is gone. Read them from your CI and issue tracker instead
- Name history: Old blog posts and configs call this Code Climate Quality. It is Qlty now
Free tier plus per-contributor paid tiers, free for open source. See the canonical pricing table on the Tools page.
NDepend - .NET Specialist
.NET deep analysisKey Features
- 150+ code metrics: The deepest .NET analysis available
- Dependency visualization: Interactive graphs of code relationships
- Roslyn inspections: Real-time analysis in Visual Studio
- Trend analysis: Track metrics over years of history
When to Choose It
- You are all-in on .NET: Nothing else goes this deep on assemblies, coupling, and layering
- You are arguing about architecture: Dependency graphs and CQLinq queries turn opinions into evidence
- Skip it if: Your stack is polyglot - NDepend analyses .NET only, so it cannot be your single dashboard
- Budget note: Developer seats and build-server/CI seats are licensed separately - price both
NDepend sells annual subscriptions with perpetual fallback rights. See the canonical pricing table on the Tools page.
Tool Comparison Matrix
| Feature | SonarQube | Qlty | NDepend |
|---|---|---|---|
| Languages | 30+ | 15+ | .NET only |
| Self-hosted option | |||
| Cloud option | |||
| Free tier | Community Build | Free plan + OSS | Trial only |
| DORA metrics | |||
| PR integration | |||
| AI fix suggestions | |||
| How it is licensed | Per instance, by lines of code | Per contributor, per month | Per seat, annual subscription |
Recommendation: Start with SonarQube Community Build (free). It covers most needs and establishes measurement habits. Add Qlty if most of your debt arrives through pull requests, or NDepend if you're deep in .NET legacy code. Note the DORA row: none of these three produce delivery metrics, which is why the collection guidance above sends you to your CI system and incident tracker instead.
Prices deliberately live on one page. This page tells you which tool to pick and why. For what each one actually costs, see the canonical pricing table on the Tools page, which carries a verification date. Vendors change pricing without notice - confirm with the vendor before you budget.
Developer Sentiment: The Only Leading Indicator You Have
Every other signal on this page is a lagging indicator. Here is how to collect the one that is not.
The most useful early signal for tech debt is developer sentiment. Code metrics are lagging indicators - by the time they look bad, the damage is already done. Asking developers where they get stuck is a leading indicator, which is why the strongest measurement programs pair surveys with hard engineering data.
Run the Survey Properly
Quarterly, anonymous, and identically worded every time:
- How confident are you making changes in [area]?
- What slowed you down most this quarter?
- Which system do you avoid touching, and why?
Change one word and you have started a new series. Version the questionnaire like code.
Corroborate With Logs
Sentiment on its own is deniable. Pair every survey with engineering telemetry:
- Build duration and flaky-test frequency
- Code review turnaround time
- Change frequency per module
When the survey and the telemetry point at the same module, the argument is over.
Ask About All Three Forms
Surveys catch debt that scanners structurally cannot:
- Code degradation: quality eroding over time
- Expertise gaps: knowledge that left with someone
- Migration debt: platforms overdue for replacement
No static analyser has ever detected the second one.
The Confidence Heatmap
Aggregate one question - "On a scale of 1 to 5, how confident are you making changes in [specific area]?" - across every team and plot it per module. You get a heatmap of where the organization is afraid of its own code. The interesting cells are the contradictions: an area with clean analyser grades but low confidence is documentation debt, an expertise silo, or architectural complexity that no scanner can see. Those cells are where your next quarter of remediation should go.
Collection Cadence, and How Numbers Get Gamed
Sample each signal at the rate it actually changes, then defend it from the incentives you are about to create.
How Often to Collect Each Signal
| Signal group | Collect | Why that rate |
|---|---|---|
| Code quality signals | Every merge to main | Cheap, automatic, and you want the exact commit that moved it. |
| Delivery (DORA) signals | Continuously, reported weekly | Events arrive continuously but are too noisy to read daily. |
| Backlog signals | Once per sprint, at close | The sprint boundary is the only point where created and closed are both settled. |
| Developer experience signals | Quarterly | Sentiment moves slowly, and surveying more often collapses your response rate. |
| Business impact signals | Monthly | Incident and delivery counts need a month of volume to mean anything. |
Two rules make the series usable a year from now. Never change a definition mid-year - if you must, start a new series next to the old one and keep both for two quarters. And never backfill. A gap in the data is information; a reconstructed gap is fiction that nobody will remember was reconstructed.
Pitfalls: How These Numbers Get Gamed
The moment a metric affects a performance review, a budget, or a promotion, it stops measuring the thing and starts measuring the incentive. That is not cynicism about your team, it is the predictable behaviour of any measured system. Design against it up front.
Coverage padding
Tests that execute code and assert nothing move coverage without moving quality. Defence: pair coverage with mutation testing, or with escaped defect share, and never report coverage alone.
Ticket splitting
One debt item becomes six, and closure velocity triples overnight. Defence: report closed effort alongside closed count, and watch the median item size.
Silent reclassification
A production failure is logged as "planned follow-up", and change failure rate improves without anything improving. Defence: write the failure definition down before you count, and review reclassifications, not just totals.
Not filing the bug
If a low count is rewarded, people stop writing things down, and your backlog stops describing reality. Defence: never treat a falling creation rate as a win on its own. Read it against escaped defect share.
Window shopping
A quarter chosen after the fact can show any trend you want. Defence: fix the reporting window in advance and keep every past report, including the bad ones.
Aggregation cover
A healthy organization-wide average can contain one service that is on fire. Defence: every signal that can be broken down by module or team should be, and the breakdown should be reviewed more often than the total.
The structural defence: keep these numbers out of individual performance evaluation entirely. Measure teams and systems. The moment an engineer's rating depends on a number they can edit, you no longer have a measurement program - you have a reporting ritual.
Frequently Asked Questions
Your own baseline, measured over a full quarter, and nothing else. We deliberately publish no Elite/High/Medium/Low threshold table. DORA's current framework publishes no numeric performance-cluster thresholds, the figures circulating for an "elite" change failure rate contradict each other by a factor of three, and the 2025 DORA report moved to describing seven team profiles as narrative patterns rather than numeric grades. A cross-company comparison is the weakest use of these metrics anyway, because deployment frequency in a regulated payments platform and in a marketing site are not the same quantity. Trend against yourself: "median lead time fell from three days to eleven hours over two quarters" is defensible, "we are now a high performer" is not.
Track completed work per sprint for a single team over at least a year, and read it alongside lead time. A declining trend with a stable team size and stable scope is your evidence. The connection to debt gets much stronger when you segment: compare throughput and lead time for work in a module your confidence survey rates badly against work in a module it rates well. Same team, same sprint length, different code - so the difference is the code. That comparison is far more persuasive than any absolute velocity number, and it survives the obvious objection that story points are not a real unit. Never compare velocity between teams; it is not a currency.
Set the target from your own current number, not from a figure you read somewhere. Coverage percentage in isolation is one of the most misleading signals available: a suite can execute 95% of the code and assert almost nothing. Better companions are mutation testing score (how many deliberately injected faults do the tests catch?), escaped defect share (what proportion of bugs did customers find first?), and coverage of the specific flows that make money. The practical rule is directional: coverage on new and changed code should not fall, and the modules you change most often should be the ones you cover best. Legacy code sitting at zero should be covered as you touch it, not in a one-off coverage sprint that produces assertions nobody believes.
Cognitive complexity estimates how hard code is for a human to follow. Unlike cyclomatic complexity, which counts independent paths, it weights nested structures, breaks in linear flow, and recursion - the things that actually make a function exhausting to read. It is worth measuring because it correlates with the experience your team reports in the confidence survey, which makes it one of the few static measures that a developer will recognise as true. SonarQube and several other analysers calculate it automatically. Treat the default flagging threshold as a tool convention rather than a law: what matters is the count of functions above whatever limit you agreed, and whether that count is rising.
Ready to Make Your Tech Debt Visible?
Measurement is just the first step. Once you have data, you need to put it somewhere people will look at it, and then get buy-in for the work it justifies.