Stop tracking lines of code. Nobody cares.
Lines of code, commit frequency, pull request throughput: these are activity metrics, not quality metrics. A team can produce 10,000 lines of code in a week and leave the codebase worse than they found it. A team can merge 50 PRs and introduce no new capability.
The metrics that actually tell you whether the code is any good are different. Seven of them.
1. Cyclomatic complexity
The number of independent paths through a function. Measured per function, aggregated across the codebase.
Why it matters: functions above 15 are hard to test comprehensively, hard to reason about, and hard to change without regressing. They are where bugs hide.
What to track: the p95 and p99 of complexity across your codebase, and the count of functions above a threshold. Both should trend flat or downward.
2. Test-to-source ratio
The number of test files divided by the number of source files. Not a perfect metric, but a strong leading indicator.
Why it matters: when source files are being added faster than test files, either tests are being skipped or tests are being consolidated into larger files that lose specificity. Either is a problem.
What to track: the ratio over time. Healthy codebases hold this roughly flat or trend upward as the team matures.
3. Security findings
Committed secrets, dangerous API usage patterns, known vulnerabilities in dependencies. Surfaced by static analysis and dependency scanners.
Why it matters: security findings are the cheapest bugs to catch early. Every one that escapes to production costs 10 to 100 times more to fix than one caught in CI.
What to track: total count by severity. The count should trend downward (findings fixed) or hold flat (same patterns rediscovered on new code).
4. Dependency count
Direct dependencies and transitive dependencies. Both.
Why it matters: every dependency is code you did not write that you ship anyway. The cost of a dependency is proportional to its footprint in your application, the attack surface it adds, the build time it consumes, and the licence risk it introduces.
What to track: total count, flagged against "why do we have this?". Most codebases can shed 20 to 30% of their deps if someone asks the question.
5. Duplication ratio
Percentage of code that appears in multiple places, measured by token-level similarity tools (jscpd, simian, duplo).
Why it matters: duplication is the clearest signal that abstractions are being skipped. Each duplicated block is a bug-fix-in-waiting: the day you find a bug, you find it in one place and miss the three other places with the same code.
What to track: percentage over time. A healthy codebase holds duplication under 5%.
6. Change coupling
Pairs of files that are frequently changed together, derived from git history. High coupling without a shared abstraction signals hidden dependencies.
Why it matters: if src/auth/login.ts and src/billing/subscription.ts get touched in the same PR 40% of the time, you have a hidden coupling. The boundary between auth and billing is not real. The team is paying a tax on every change.
What to track: top 10 most-coupled file pairs. Investigate each pairing. Either they are genuinely shared (extract the shared part) or they are coupled wrongly (split the concerns).
7. Documentation coverage
Percentage of public interfaces that have at least a one-line description.
Why it matters: not a vanity metric. Documentation drift is a leading indicator of team attention slipping. When docs stop getting updated, the code is usually also getting harder to change.
What to track: ratio of documented public interfaces to undocumented. Healthy projects stay above 70%.
Metrics to ignore (or demote)
These show up on dashboards but rarely earn their space:
- Lines of code. Activity, not quality.
- Commit frequency. Activity.
- PR throughput. Activity.
- Code review time. Operational, not codebase-quality.
- Test count. You can have 1,000 shallow tests. Assertion density matters more.
- Build time. Engineering-productivity, yes. Codebase quality, no.
Tracking these alongside the seven above creates noise. Tracking them instead of the seven above creates a false sense of progress.
What to do with the data
Every metric you track needs a reason and a reaction.
- Reason: what decision does this data inform?
- Reaction: what will the team do when the number changes?
If either answer is "I do not know", the metric does not belong on the dashboard.
For each of the seven above, the reaction is usually "investigate a specific file, function or dependency and fix the issue". That is why they make the cut. The reaction is concrete.
The starting point
If you are not tracking any of these today: pick two. Security findings and test-to-source ratio are the easiest to start with. Both have clear actions when they move.
Add more as you get comfortable. Tracking seven metrics well beats tracking twenty metrics badly every time.