Code Quality Score: The 6 Dimensions Explained
← Back to Blog

Code Quality Score: The 6 Dimensions Explained

A number between 0 and 100 doesn't tell you much. Two files can both score 60 for completely different reasons, one because it's a tangled mess that's hard to change safely, the other because it has zero tests and nobody's documented what it does. Treating a single composite score as the takeaway is how teams end up fixing the wrong thing first. The number that matter is underneath the number: six separate dimensions, each measuring something specific, each pointing at a different kind of risk.

A Worked Example

Take a checkout module that's been touched by four different engineers over two years. Run it through the six dimensions and the report might read something like this: complexity 38 out of 100, because the main handler function has grown to 400 lines with deep nested conditionals. Security 72, one flagged pattern for an unsanitized input on a legacy endpoint. Dependency health 90, dependencies are current. Test coverage 45, the core handler is called from six other modules but has no direct unit tests. Documentation 20, no comments explaining why the discount logic branches the way it does. On its own, none of those five numbers is an emergency. Averaged into one composite score, they might land around 53, a number that reads as mediocre but forgettable.

The tech debt dimension is what changes that read. That 400-line handler is also the untested one, and it's also the undocumented one. The same file is carrying all three risk factors at once, which means it's not a mediocre file, it's the specific place in the codebase where a change is most likely to introduce a defect nobody catches before it ships. That's the difference between a score you skim and a score you act on.

What's Actually Inside the Score

WalnutAI's code quality scoring runs as part of gap analysis, scoring every user story's code from 0 to 100 across complexity, pattern adherence, and anti-patterns, then surfacing architectural gaps, security vulnerabilities, missing unit tests, and undocumented code on top of that. Underneath those categories are six dimensions worth understanding individually, because each one tells you something different about where the actual risk sits.

  1. Complexity

This measures which files are hardest to maintain, using complexity metrics like function length, nesting depth, and branching, the same category of analysis tools like scc are built around. A low complexity score means a file is doing too many things at once, which makes every future change to it riskier than it looks. The fix isn't a rewrite. It's usually extracting the tangled logic into smaller, single-purpose functions before adding anything else to the file.

  1. Security

This dimension surfaces pattern-matched vulnerabilities, the kind of static analysis tools like semgrep specialize in known-dangerous code patterns such as unsafe deserialization, SQL injection risk, or hardcoded secrets, flagged by matching code structure against a library of known-bad patterns. A low score here doesn't mean every flagged line is actively exploited, it means the pattern exists. Prioritize by exposure and exploitability, not by raw count, and fix the pattern at its source rather than patching the one line that got flagged.

  1. Dependency Health

This measures CVE exposure in third-party packages, the software composition analysis category tools like trivy handle: scanning package manifests against known vulnerability databases to flag dependencies with disclosed security issues. A low score means the codebase is relying on a package with a known, publicly documented vulnerability, not a hypothetical one. Where a patched version exists, upgrading is the fix. Where it doesn't, the immediate move is isolating or mitigating exposure to that dependency until one does.

  1. Test Coverage

This isn't a raw coverage percentage. It specifically flags high-centrality components without tests, meaning code that a lot of other code depends on, left completely unprotected. A module used in twelve places with zero tests is a much bigger risk than an isolated module with the same lack of coverage, even though a blanket coverage percentage would treat them identically. The fix is prioritizing tests for whatever has the most things depending on it, not chasing the overall coverage number upward evenly across the codebase.

  1. Documentation

This flags undocumented modules, parts of the codebase with no explanation of intent or usage. A low score here is a slower-burning risk than a security flag, but a real one: undocumented code means every future change, human or AI-generated, is working from inference instead of stated intent. The fix is prioritizing documentation for the modules that also score poorly on complexity, since that combination is exactly where misunderstanding a file's purpose does the most damage.

  1. Tech Debt

This is a composite, not a separate measurement: the overlap of high complexity, low coverage, and low documentation on the same file. Any one of those three alone is manageable. All three together on the same piece of code is where defects actually tend to originate, because it's complex enough to misunderstand, untested enough that a mistake won't get caught, and undocumented enough that nobody catches the misunderstanding before it ships. This overlap, more than any individual dimension, is the honest priority list.

Two Misreadings Worth Correcting

The first is treating a low complexity score as a verdict on the engineer who wrote the file. Complexity accumulates over years and multiple contributors, usually because a function kept getting one more responsibility added to it rather than being restructured. It's a description of the file's current shape, not a judgment of anyone's skill, and treating it as the latter is a fast way to make a team defensive about a metric that's supposed to help them.

The second is assuming a high security score means the code is secure. Pattern-matched scanning is precise about what it's designed to catch, known-dangerous patterns like unsanitized input or hardcoded credentials, and silent about everything outside that scope: business logic flaws, authorization bugs that don't match a known pattern, or anything specific to how a particular application handles trust. A clean security score is evidence of the absence of known bad patterns, not proof of the absence of vulnerabilities. Teams that treat it as the latter tend to skip the manual review or penetration test that would have caught what pattern matching structurally can't.

Why the Composite Score Alone Isn't the Useful Number

A single 0 to 100 score is good for tracking trend over time or comparing one project against another at a glance. It's not good for deciding what to fix Monday morning. That decision needs the dimension breakdown, and specifically the tech debt overlap, because a file with a mediocre composite score for six different small reasons is a very different problem than a file with the same composite score because it's complex, untested, and undocumented all at once.

What This Means for a Release Decision

For engineering leads and CTOs evaluating release readiness, the useful artifact isn't the score, it's the prioritized report underneath it: annotated code snippets tied to specific dimensions, ranked by what's actually urgent rather than by file name or ticket age. Paired with the Requirement Traceability Matrix, which shows whether a story is tested at all, the quality score adds the second half of the picture: whether the code behind that story is actually safe to ship. That combination is what turns a quality score from a vanity metric into something a team can act on before a release, not just measure after one.

See the six dimensions on your own codebase: get your codebase's quality score. Get your codebase's quality score

W
WalnutAI Team

Frequently Asked Questions