Limitations¶
This page lists what codaviz does not do and where its numbers stop being trustworthy.
Metric coverage is uneven¶
Maintainability index and cognitive complexity are Python only. Every other language reports cyclomatic complexity and SLOC.
MI needs Halstead volume, which has no principled definition across arbitrary grammars; codaviz leaves a blank where it cannot defend the number. A shared SonarSource cognitive walker for the tree-sitter analyzers is planned; until it ships, those columns are empty and the report opens on a cyclomatic lens.
Cross-language numbers are not comparable¶
Cyclomatic complexity for the tree-sitter languages is McCabe-family, computed from the syntax tree. It is internally consistent per language, but it is not a reimplementation of gocyclo, PMD, or ESLint's complexity rule, so numbers are not comparable across analyzers.
Python is the exception: it comes from mccabe, so it matches Ruff's C901 exactly.
Nested definitions are folded in¶
Closures and methods of function-local classes are counted into their enclosing function's score, matching how Ruff and mccabe report the outer function.
A function that looks alarming may be alarming because of a closure inside it. The source snippet in the drill-down shows you which.
Circular imports: Python only, and conservative¶
Detection is a static AST import graph. No code is imported or executed.
It misses, by design:
- Dynamic or conditional imports:
importlib.import_module,__import__, anything computed at runtime. - Imports inside functions or classes: they do not cause an import-time cycle. Moving an import into a function is the standard fix.
if TYPE_CHECKING:blocks: same reason.- Every non-Python language: no cycle detection at all.
Import resolution favours false negatives over false positives: an import that cannot be matched to a known internal module is dropped. codaviz does not guess. src/ layouts, implicit namespace packages, and relative imports resolve correctly because module names are computed relative to the detected source root.
A clean circular-imports panel means "none found by this method". It does not mean "none exist".
Hierarchy is path-based everywhere¶
Every analyzer turns directories into packages and files into modules. For Go, Rust, and Java, whose unit of namespacing is declared in source, the mapping is an approximation. In practice each .go file appears as its own module even though Go treats the whole directory as one package. Package-level aggregates still roll up correctly, since the directory is the package node. Relabelling packages by their declared name is a deferred refinement, and the plugin hook already supports it.
SLOC is approximate off Python¶
For Python, SLOC comes from radon's raw analyzer, which excludes blanks, comments, and docstrings.
For the tree-sitter languages it is "non-blank lines", so comment lines are counted. A heavily commented file reads larger than it is, and treemap tiles are sized by SLOC, so comment-heavy modules get oversized tiles. Refining this means walking comment-node spans per grammar; the code marks it as a known shortcut.
The color scale is relative¶
Treemap and bar colors place each node between the minimum and maximum in this project. The worst module in a clean codebase still renders red.
The ramp answers "where should I look in this repo". Use the numeric columns for absolute judgements.
The ramp itself is green → amber → red. Luminance rises toward the bad end and a textual better → worse legend sits under the treemap, so the scale is readable without relying on color discrimination. A fully colorblind-safe palette is planned.
Report-wide config comes from one root¶
When analyzing several roots, exclude and include-tests are read per root, but max-complexity, max-cognitive, and treemap-depth come from the first root only. See Configuration.
Generated and vendored code is not detected¶
codaviz excludes build/, dist/, and node_modules/, but it has no notion of "this file was generated". Emscripten output, compiled bundles, and vendored blobs are analyzed as ordinary source. Because they are enormous and machine-shaped they dominate every ranking: on one real codebase the top five by cyclomatic complexity were all *_ie.js emscripten output.
Exclude them explicitly:
Report size scales with embedded source¶
Source snippets are embedded per function. On a large codebase that gets out of hand fast: a 97,000-function project produced a 1.17 GB report.
That is more than slow: the page does not load at all. The payload is one JSON string inside a <script> element. Browsers cap how long a single JavaScript string may be: Firefox at ~1 GB (2³⁰−2 characters), Chrome/V8 at ~512 MB. That report's JSON island was 1,169,821,956 characters, past both caps. The page cannot read its own data element and renders blank with nothing useful in the console.
codaviz warns on stderr when a written report passes 256 MB. The remedy:
The same report drops to 22 MB and opens normally. Use the line numbers to jump to the code in your editor. Excluding generated files (above) cuts it further.
Not a gate¶
codaviz exits 0 whenever the analysis completes, whatever the numbers say. It will not fail your build. To ratchet a threshold in CI, parse the JSON or CSV export and compare against your own budget.
Out of scope¶
Security scanning, runtime profiling, and test coverage are non-goals; Bandit, cProfile/scalene, and pytest-cov do those jobs well. codaviz measures structural complexity.
Planned¶
In rough order:
- A shared cognitive-complexity walker for the tree-sitter languages.
- Churn-weighted hotspots: complexity × git change frequency.
- Coupling metrics: afferent, efferent, instability.