Skip to content
Fallow home
All docs pages

Runtime coverage

See which functions your production traffic runs, which never run, and which hot paths a change touches. Runtime coverage merges execution data into fallow health, so you can delete cold code with evidence.

Runtime coverage tells you which functions ran when your deployed app handled real traffic. Use it to delete cold code with confidence and to spot changes to hot code in review.

Static analysis finds exports with no references and files that no test reaches. Runtime coverage adds the production view on top of fallow health.

fallow license activate --trial --email you@company.com
fallow coverage setup
fallow health --runtime-coverage ./coverage

What it tells you

When you enable runtime coverage, fallow merges runtime evidence into the health report. Each function gets a per-finding verdict. The verdict combines the static reachability signal with the invocations that fallow observed in production:

VerdictMeaning
safe_to_deleteStatically unused AND V8 tracked AND zero invocations. This is the strongest delete signal from fallow.
review_requiredStatically used but never invoked in production. A person must check it. The function can be seasonal, used only on an error path, or really dead.
low_trafficInvoked in production, but below the --low-traffic-threshold ratio of the total trace count (spec default 0.1%). Effectively dead for the observed period.
coverage_unavailableV8 could not track the function (lazy-parsed, worker-thread isolate, dynamic eval, or the source map did not resolve). Information only, not a dead-code signal.
activeInvoked in production above the low-traffic threshold. Not dead.
unknownA forward-compatibility value that newer sidecars can return. Rare.

The report also has these fields:

OutputMeaning
verdictOverall report status: clean, cold-code-detected, hot-path-touched, license-expired-grace, or unknown. In PR review, when --diff-file (or --changed-since) is set, hot-path-touched takes priority over cold-code-detected.
signalsEvery signal that the post-processing pass detected, as an array. verdict shows only the most actionable signal for the current context, and signals shows all of them. Sorted by severity, highest first. Omitted when empty. A typical PR run emits ["cold-code-detected", "hot-path-touched"] when both apply.
summaryAggregate counts and capture metadata. See Summary fields.
findingsCold or unresolved functions. See Finding fields.
hot_pathsThe busiest runtime functions, with a stable id, an optional stable_id join key, line, end_line, invocations, percentile rank, and an optimization_target block. See Optimization target fields.
blast_radiusFor each runtime-observed function, how far a change can spread: caller_count, caller_count_weighted_by_traffic, deploys_touched, and a risk_band.
importanceFor each runtime-observed function, how much it matters in production: invocations, cyclomatic, owner_count, an importance_score, and a reason.
actionableWhether the report has enough evidence to act on, with actionability_reason and actionability_verdict. A capture with no tracked functions reports an insufficient_evidence verdict, so fallow does not read it as cold. This verdict can only hold back action: it never gates a positive verdict or a confidence score.
provenanceContext about the data behind the report (data_source, is_production, freshness_days, untracked_ratio, unresolved_ratio, stale, stale_after_days). Context only. It never gates a verdict.
watermarkSet when a license or trial is in its grace period, for example license-expired-grace when the license expired 8 to 30 days ago. The analysis still runs. See Trial and watermark behavior.
warningsNon-fatal merge diagnostics, for example partial source-map remapping.

Summary fields

  • Aggregate counts: functions_tracked, functions_hit, functions_unhit, functions_untracked, coverage_percent.
  • trace_count, period_days, and deployments_seen.
  • An optional capture_quality block (window_seconds, instances_observed, lazy_parse_warning, untracked_ratio_percent). Sidecars on protocol 0.3 or newer emit it. It flags short-window captures in which lazy-parsed scripts may not appear.

Finding fields

  • id: a per-finding suppression id (fallow:prod:<hash>). The hash covers the current line.
  • stable_id (optional): a cross-surface id (fallow:fn:<hash>). The hash covers file + name + start line. One function has one value across findings, hot paths, blast radius, and importance, and across the V8, Istanbul, and oxc producers.
  • source_hash (optional): a content digest that does not change when a line moves. A function that moved but was not edited keeps the same value, so its baseline stays suppressed after a pure line shift.
  • line, the per-finding verdict, confidence, supporting evidence, and machine-readable actions.
  • discriminators (optional): the inputs behind the verdict, so an agent can reproduce the verdict without deriving it again. The block has:
    • tracking_state: called, never_called, or untracked.
    • invocation_ratio.
    • The low_traffic_threshold and min_observation_volume in effect.
    • trace_count with meets_observation_volume.

Every finding has an evidence block. It gives the reason for the verdict in machine-readable form:

"evidence": {
  "static_status": "used",
  "test_coverage": "not_covered",
  "test_only_reference": false,
  "v8_tracking": "tracked",
  "untracked_reason": null,
  "observation_days": 30,
  "deployments_observed": 14
}

test_only_reference covers the one case where production mode and static reachability disagree. Production mode drops test, spec, story, fixture, and benchmark files from discovery. An export that only those files call therefore reads as statically unused.

  • true: the function is unreachable in the production graph, but an excluded file still references it. It is not dead code, because a removal breaks the test that references it. Fallow reports it as review_required, not safe_to_delete, with the action "Only tests reference this export; delete the test usage together with the function or keep it".
  • false: fallow compared the production graph with the full tree and found no such reference.
  • null: the report ran without a production filter, so there is no second reachability answer.

Optimization target fields

A hot path tells you how often a function runs. The optimization_target block also tells you how much work each call does, so you know where speed work gives the largest gain. importance ranks the risk of a change. optimization_target ranks the speed gain.

FieldMeaning
cost_scoreThe invocations multiplied by the work per call. An integer with no upper limit. Compare it only between hot paths with the same cost_basis.
cost_basisThe input for the work per call: inner_iterations or cognitive.
inner_iterations_per_callThe peak runs of one block inside the function for each call. 1.0 means no block ran more than once per call. A loop body that runs 3 times per call gives 3.0. Calls to other functions do not change the value: a function that calls a helper 3 times per call still gives 1.0 when it has no loop. Present only when the dump has V8 block counts for the function.
cognitiveThe static cognitive complexity of the function.
cyclomaticThe static cyclomatic complexity of the function.
line_countThe number of lines in the function body.

The block counts come from the V8 dump. Node writes them under NODE_V8_COVERAGE, so a dump from a test run or a benchmark run gives the measured value. When the dump has block counts for the function, cost_basis is inner_iterations. When it has none (an Istanbul file, or a cloud pull), cost_basis is cognitive. Cognitive complexity is a static proxy for the work per call, not a measurement. On the cognitive basis, the work per call is at least 1.

One report can hold both bases. A dump with block counts also gives cognitive for a function that shares its start line with another function, or that fallow cannot map to a source line. Scores on the two bases do not compare, so sort by cost_basis first.

A hot path has no optimization_target block when it has no stable_id, or when no function in the checkout matches the stable_id. The optimization_target_unmatched warning gives the count of these hot paths in the output.

The runtime coverage pass selects hot paths by invocations, and --top keeps the busiest. A function with fewer invocations but a large amount of work per call can be absent from the list. Raise --top, or lower --min-invocations-hot, to see more candidates.

To list the best speed targets from the JSON output:

fallow health --runtime-coverage ./coverage --format json --quiet \
  | jq '.runtime_coverage.hot_paths
        | map(select(.optimization_target))
        | sort_by(.optimization_target.cost_basis != "inner_iterations",
                  -.optimization_target.cost_score)
        | .[:10]
        | map({path, line, function, invocations, basis: .optimization_target.cost_basis,
               cost: .optimization_target.cost_score,
               per_call: .optimization_target.inner_iterations_per_call})'

The human output shows the cost at the end of each hot path line, for example (600 invocations, p100, cost 1,800 at 3.00 iterations/call).

Confidence thresholds

Two flags control how strict fallow is when it assigns verdicts:

FlagDefaultEffect
--min-observation-volume <N>5000Minimum total trace volume before the sidecar can emit high-confidence safe_to_delete or review_required verdicts. Below this value, confidence is capped at medium, so new or low-traffic services do not get overconfident verdicts.
--low-traffic-threshold <RATIO>0.001An invoked function below this fraction of trace_count is classified low_traffic instead of active. A decimal value (0.001 = 0.1%).

Static coverage vs runtime coverage

fallow health has three coverage options. Each answers a different question:

SurfaceFlagInputAnswersPaid
Static reachability gaps--coverage-gapsnoneWhich runtime files and exports have no transitive test path?no
Exact CRAP scoring--coverageIstanbul JSONHow covered is each function for CRAP calculation?no
Runtime coverage--runtime-coverageV8 directory, V8 JSON file, or Istanbul JSONWhich functions actually ran, which stayed cold, and which hot paths changed?A single local capture is free. More needs a license.

To find untested code, use --coverage-gaps. For more accurate CRAP scores, use --coverage. For evidence about the code that production runs, use --runtime-coverage.

Accepted input

fallow health --runtime-coverage <path> accepts:

InputExample
V8 dump directory./coverage created with NODE_V8_COVERAGE=./coverage
Single V8 JSON file./coverage/12345.json
Single Istanbul coverage map JSON file./coverage/coverage-final.json

If a V8 dump includes Node's source-map-cache, fallow remaps the supported source-map URLs before analysis. Supported URLs include file paths, relative paths, webpack://..., and vite://.... For an unsupported virtual scheme, fallow uses raw V8 handling and the run continues.

A remapped function count has a ceiling of 4,294,967,295 calls. The remap writes 32-bit counts, so a function that V8 counted more often than that reports the ceiling as its invocations. Only very hot helpers in long-running processes reach it.

Production coverage: getting source attribution

Production runtime coverage records positions in your built bundle. Examples are a Next.js .next/server/chunks/*.js, an Electron out/main/index.js, a NestJS dist/, or a minified browser bundle. Bundle positions cannot map back to your TypeScript or JSX source on their own.

If you do not take one of the two steps below, the cloud records every function as unresolved. fallow coverage analyze --cloud then shows no attributable findings, and a coverage_unresolved warning explains why.

Pick one:

  • Upload source maps (for V8 coverage). After the build, upload the .map files in a CI step. The cloud uses them to map bundle positions back to source:

    fallow coverage upload-source-maps --dir <build-dir>   # e.g. dist, .next, out

    Upload the maps for the same commit on which you collected the coverage. fallow coverage documents the flags, per-commit SHA matching, and CI snippets. When fallow coverage analyze --cloud finds built .map files on disk that you did not upload, it prints the exact command to run.

  • Instrument at build time with Istanbul (no maps needed). oxc-coverage-instrument rewrites your source and puts the original file and line positions in the coverage itself, so nothing needs remapping. The fallow project uses this path for its own code.

First-run workflow

  1. Optional: start a trial or activate a license with fallow license. A single local capture works without one.
  2. Run fallow coverage setup.
  3. Follow the generated recipe in docs/collect-coverage.md.
  4. When coverage exists, run fallow coverage setup or fallow health --runtime-coverage <path> again.

fallow coverage setup is resumable. It does these steps:

  1. Checks the license state.
  2. Installs or locates fallow-cov.
  3. Writes a collection recipe for your framework.
  4. Detects an existing coverage artifact, when one is present.
  5. Continues into fallow health --runtime-coverage.

Review changed code

In change review, runtime coverage shows which hot code a change touches:

fallow health --changed-since main --runtime-coverage ./coverage

When changed files touch hot runtime code, fallow can upgrade the overall verdict to hot-path-touched. PR review then has a stronger signal than static complexity alone.

For tighter line-level scoping, pass a pre-computed unified diff:

git diff --unified=0 main...HEAD > /tmp/pr.diff
fallow health --runtime-coverage ./coverage --diff-file /tmp/pr.diff

Fallow then flags a hot function only when the diff changes a line inside its [start_line, end_line] range, not anywhere in the file. The bundled GitHub Action and GitLab CI template already pre-compute the diff and pass it in. PR comments therefore show only the hot paths that the change touches.

Line-level hot-path matching needs protocol 0.5 or newer in the sidecar. Older sidecars report only a start line, so fallow uses file-level scoping.

Trial and watermark behavior

Runtime coverage is part of Fallow Cloud, the paid product. A single local capture is free and needs no license. Continuous or multi-capture monitoring (for example, a V8 directory with more than one JSON file) needs a license with the runtime_coverage feature. To try it, start a 30-day trial with fallow license activate --trial --email <addr>.

Fallow verifies the license offline against a compiled-in Ed25519 public key. Only fallow license activate --trial and fallow license refresh need network access.

When a license expires, fallow gives you a grace period:

Days after expiryBehavior
1 to 7The analysis runs as normal. fallow license status tells you to refresh.
8 to 30The analysis runs. The output gets the license-expired-grace watermark and verdict.
31 or moreFallow blocks runtime coverage and exits with code 3. Run fallow license refresh.

When a license is present, fallow checks it for every runtime coverage run, also for a single capture. An expired license past the grace period, an invalid token, or a license without the runtime_coverage feature stops the run with exit code 3. For details, see fallow license.

See also