Field guide

Code Quality Tools: Metrics, Thresholds and the Review Pass That Uses Them

Four numbers describe most of what teams mean by code quality. Here is what each one counts, the range worth acting on, the point where it starts lying to you, and which browser tool on Toolexe measures it.

Static analysis catches a narrow band of defects. Style drift, unsafe sinks, structural risk and orphaned branches show up reliably. Logic errors almost never do, because no scanner knows what your feature was supposed to do.

The narrow band still pays for itself, because the defects inside it are the ones people read straight past. A reviewer skims a 400 line diff and misses the third nested ternary on line 218. A parser counts it in under a millisecond. Everything on this page exists to clear mechanical work off the reviewer's desk so their attention lands on intent, edge cases and naming, which is the part machines handle badly.

Two failure modes follow from misreading the output. Teams either treat every metric as a hard gate and split perfectly readable functions to satisfy a number, or they run the scan, glance at a green summary and skip the reading entirely. The thresholds below exist to keep you between those two.

The four numbers worth tracking

Dozens of quality metrics exist. Four carry most of the signal for day to day work, and each has a documented range you act on rather than a target you chase. The last column matters more than the others, because a metric you trust blindly does more damage than one you never ran.

Code quality metrics, their healthy ranges, and their known blind spots
MetricWhat it countsRange to act onWhere it misleads you
Cyclomatic complexityIndependent paths through one function. Start at 1, add 1 for every if, else if, case, loop, catch, ternary and short-circuit operator.1-10 low risk
11-20 moderate
21-50 high risk
50+ rewrite candidate
A flat switch with 30 unrelated cases scores 30 while reading perfectly well. The metric counts branches, not confusion.
Maintainability indexA composite of Halstead volume, cyclomatic complexity and lines of code, rescaled onto 0 through 100.0-9 red, act now
10-19 amber, watch list
20-100 green
Length drags the score down on its own, so a long well factored module rates worse than a short tangled one.
Duplication ratioShare of tokens sitting inside clone blocks, usually measured at a 50 token minimum match length.Under 3% normal
3-5% review the clones
Over 5% schedule a refactor
Generated API clients, database migrations and test fixtures inflate the ratio. Exclude them before reading the number.
Statement coverageShare of executable lines a test run touched at least once.70-80% working band for app code
90%+ for parsers and libraries
A suite with no assertions still reports 100%. Coverage proves execution, never correctness.

Complexity bands follow McCabe (1976) and NIST SP 500-235. Maintainability bands follow the Microsoft 0 to 100 rescaling.

Reading two metrics together

Neither number means much alone. A file at complexity 24 with duplication near zero is usually one dense algorithm, and dense algorithms are fine when they are tested. A file at complexity 24 with 11 percent duplication is copy paste growth, and it will keep growing until somebody names the shared behavior. Pair the structural metric with the duplication ratio before deciding whether to refactor.

A five stage pass over one change

Order matters more than tool count. Running a security scan on a file the parser rejected wastes the scan, and arguing about comment style before checking branching wastes the review. This sequence takes about twenty minutes on a medium pull request.

Clear the parser first

A syntax error poisons every check downstream. Complexity counts go wrong, scanners bail out silently, and you spend ten minutes reading a report about a file nothing could read.

Read the shape of the change

Structural problems survive every rename and every formatting pass. Look at branching, clones and orphaned exports before anyone argues about brace placement.

Run the risk pass

Injection shapes, unsafe sinks and stale dependency versions are pattern work. Machines beat tired humans at pattern work every time, so give the machine that job.

Close the test gap you opened

The branch you added this morning is the branch nobody covers. Write the missing case while the reasoning behind it is still in your head.

Hand it over with the numbers attached

Reviewers move faster when the risky parts are named up front. Three lines of context beat a 600 line diff with no map.

Counting complexity by hand, once

Run the count manually a single time and the tool output stops being a mystery number. Every decision point adds one to a base of one. Here is a shipping fee function with the additions marked.

function shippingFee(order) {if (!order) return 0; // +1let fee = 5;if (order.weight > 10) fee += 4; // +1if (order.express) fee += 12; // +1for (const item of order.items) { // +1if (item.fragile) fee += 2; // +1}
return order.total > 100 ? 0 : fee; // +1}
base 1decision points 6cyclomatic complexity 7minimum test cases for full branch coverage 7

Seven sits inside the low risk band, so nothing here needs splitting. The second figure is the one worth carrying into your test file: the complexity score doubles as the count of paths a suite has to walk to claim full branch coverage. A function scoring 22 needs 22 test cases, which is the practical argument for splitting it, rather than any aesthetic objection to long functions.

Where the count misfires: guard clauses inflate it cheaply. Five early returns validating five arguments score five points and read more clearly than one nested block scoring the same. Check whether the branches belong to one concern before you act on the number. Paste the function into the Code Complexity Analyzer to compare your manual count against the parser.

Something is already broken, start here

The stages above assume a change under review. Production incidents run backwards, from symptom to cause, so the entry point differs.

The rest of the catalog

Formatting, generators, load testing and monitoring live alongside the analysis tools.

Where browser based analysis stops

Being direct about the ceiling saves you a wasted afternoon. Five limits apply to every analyzer in this category, and knowing them tells you when to reach for a pipeline scanner instead.

  • One file at a time, with no call graph

    Every analyzer on Toolexe reads the text you paste into it. Nothing resolves imports across a repository, so an export consumed by three other modules still reads as dead code when the file is viewed alone. Confirm any removal with a project wide search before you delete a line.

  • Dynamic code reads as opaque

    Reflection, eval, computed property access and SQL assembled from strings defeat lightweight parsing. Complexity and security passes under-report on files built around those patterns, and a clean result there means the parser gave up, not that the code is safe.

  • The coverage tools read reports, they do not run tests

    Paste an lcov file or an Istanbul summary and you get the uncovered branches ranked by risk. Your suite still executes in your own environment, on your own runner.

  • Pattern scanners miss authorization bugs

    A scanner finds an unescaped sink. No scanner knows a route was supposed to check record ownership before returning the row. Business logic flaws remain a human review problem, which is the main reason a green scan is never a merge signal on its own.

  • This is not a substitute for a CI gate

    Repository wide enforcement on every push belongs to ESLint, Semgrep, SonarQube, PHPStan or your language equivalent, wired into the pipeline. These pages fit the ten minutes before you open a pull request, or an audit of a snippet with no project checked out.

Questions people ask

What cyclomatic complexity score should block a review?

Most teams warn above 10 and block above 15 for application code. Treat the figure as a prompt rather than a rule. A function scoring 12 across one validation chain reads fine. A function scoring 12 across four unrelated concerns needs splitting, whatever the threshold says.

Does my source code get uploaded when I use these tools?

No. Analysis runs as JavaScript inside your browser tab, so the snippet never leaves your device. Reload the page and the input is gone. Nothing is written to a Toolexe server and nothing is logged.

Is 100 percent test coverage worth chasing?

Rarely. The final 15 percent is usually error handling nobody triggers plus generated code nobody reads. Target 70 to 80 percent on application code, then spend the leftover effort on assertion quality and on the branches your bug tracker keeps pointing at.

Which languages do the analyzers handle well?

JavaScript and TypeScript get the deepest parsing. PHP, Python, Java, C# and Go run through pattern level analysis, so structural metrics hold up while language specific idioms report less accurately. The syntax validators are exact for JSON, XML, CSS and HTML because those grammars are fully parsed.

How do I get these results into a pull request?

Copy the report output and paste it into the description inside a fenced block. The Review Checklist Generator emits Markdown task lists, which GitHub and GitLab render as tickable items. Reviewers work through a checklist faster than a prose summary.

Can these tools score an entire repository at once?

No. Scope is one file or one pasted snippet. Repository level scoring needs a checkout and a build, which means a CI job. Use these pages for the change in front of you, and a pipeline scanner for the trend line across releases.

How this guide and these tools are maintained

Thresholds on this page are not house opinions. Each one traces to a published source, listed below, and gets rechecked whenever a referenced standard changes.

  • Analysis runs client side. Every code quality tool executes as JavaScript in your tab. Pasted source never reaches a Toolexe server, which is why these pages suit proprietary code that policy keeps off third party services.
  • Tools get retested against known inputs. Each analyzer is checked with sample files whose metric values were computed by hand and cross-checked against an established implementation, so a parser regression shows up as a changed score.
  • Language support is stated, not implied. Where parsing is pattern based rather than full grammar based, the tool page says so. A silent under-report is worse than a stated limit.
  • Editorial review sits with a named person. Content in the code quality category is reviewed by Dr. Adnan Amin, whose research background is in machine learning and data mining, with published work in software defect prediction. Corrections go to the author page linked in the byline.
  1. T. J. McCabe, "A Complexity Measure", IEEE Transactions on Software Engineering SE-2(4), 1976
  2. NIST Special Publication 500-235, Structured Testing: A Testing Methodology Using the Cyclomatic Complexity Metric
  3. Microsoft Learn, Code metrics values and the maintainability index scale
  4. OWASP Top 10, web application security risk categories

Pick the stage matching the work in front of you, run the two or three tools it names, and attach the output to your pull request. The reviewer reads faster and you stop relitigating brace style.

Open the code quality catalog