Why Code Metrics Mislead You About Quality
Line count and complexity scores feel objective but mislead you about code quality. Here is why raw code metrics lie and what to measure in a repo instead.
Raw code metrics mislead you because they measure the code at rest, and code at rest is not where risk lives. Risk lives in change. A metric that counts lines or nesting depth or cyclomatic complexity is measuring what the code looks like, not what it costs to work with, and those two things diverge constantly. I have watched teams chase clean metrics into worse software, and I have shipped ugly-metric code that ran flawlessly for years. The number lied both times.
Here is why the usual metrics fool you, and what actually predicts quality.
Line count measures nothing on its own
The most seductive metric is size, because it is trivial to compute and feels like it means something. It does not. A 2,000 line file can be perfectly clear and safe. A 40 line file can be an unmaintainable knot. Length only becomes meaningful when you combine it with how often the file changes and how much depends on it.
Optimize for short files and you get a codebase shattered into a hundred tiny fragments where following a single request means jumping through twenty of them. You made the metric happy and the code harder to reason about. That is the trap of measuring the wrong thing: you get exactly what you asked for and none of what you wanted.
Cyclomatic complexity ignores whether the code changes
Complexity scores are the respectable version of the same mistake. A high complexity number on a parser that has not changed in four years is harmless. The same number on a billing path edited every week is a five-alarm fire. The metric treats them identically because it cannot see time.
Context is the whole story, and static complexity throws context away by design. This is why I never rank cleanup work by complexity alone. I rank it by complexity times churn, so the code I actually touch drives the priority. I lay out that formula in how to measure technical debt in a repo.
Metrics get gamed the moment they become targets
Here is the deeper problem. Any metric you turn into a goal stops measuring what you care about, because people optimize the number instead of the thing. Set a coverage target and you get assertion-free tests that run everything and check nothing. Set a complexity ceiling and you get functions split arbitrarily to duck under the line, scattering logic that belonged together.
The metric improves, the code gets worse, and the dashboard says everything is fine. This is not a people problem, it is a measurement problem. Point a team at a proxy and they will hit the proxy. If the proxy is not the same as quality, you have paid for the illusion of quality. I dig into a specific version of this in code quality score vs test coverage.
What actually predicts quality
If the easy metrics mislead, what do I trust. Change cost, measured directly. How long a typical change actually takes relative to its size. That number captures everything the proxies miss because it is the outcome the proxies were pretending to predict.
Underneath change cost sit a few real drivers worth measuring. Coupling, meaning how much breaks when you touch one thing, measured by which files change together in history. Churn concentration, meaning which handful of files absorb most of the edits and bugs. And the honesty of the test signal, meaning whether the tests catch a real regression when you introduce one. These are harder to compute than a line count, which is exactly why the lazy tools skip them. ReformCode exists because I got tired of tools that measured what was easy instead of what was true.
Use metrics as questions, not answers
None of this means metrics are useless. It means they are questions, not verdicts. A high complexity score is a prompt to go look, not a conclusion that the code is bad. A churn spike is a reason to open the file, not proof of rot. The number tells you where to point your judgment. Your judgment does the actual grading.
The operators who get burned are the ones who let the dashboard replace the reading. The ones who get value treat every metric as a lead to investigate and finish by reading the code the metric flagged. This is why I keep one governed foundation under every company: consistent measurement so a number means the same thing everywhere, and a shared understanding that the number is where the work starts, not where it ends.
Measure change cost, weight everything by how often the code moves, and never let a proxy become the target. Do that and metrics earn their place. Trust them raw and they will confidently tell you a swamp is a garden.