The most striking finding from this analysis is not one problematic function but one problematic file: benchmark.js contributes all three top hotspots, each touched once in the last 30 days and sitting in the “watch” quadrant — active but structurally low-risk for now. Across 26,368 total functions analyzed, every single one lands in the “low” band, with no critical or high functions identified; the risk picture here is one of early signals rather than urgent fires. I would still start with message in benchmark.js, which carries the highest activity-weighted risk score of 3.63 — modest in absolute terms, but worth understanding before the benchmark script grows further.
The table below ranks functions by activity-weighted risk — a score that multiplies structural complexity by recent commit frequency. A function that is both hard to understand (high cyclomatic complexity) and actively changing is a higher priority than one that is complex but untouched. CC = cyclomatic complexity (independent execution paths); ND = max nesting depth; FO = fan-out (distinct callees).
Top Hotspots
| Function | File | Risk | CC | ND | FO |
|---|---|---|---|---|---|
message | benchmark.js | 3.6 | 3 | — | 1 |
spawnSync | benchmark.js | 3.2 | 1 | — | 3 |
cleanFolders | benchmark.js | 2.0 | 1 | — | — |
Large Repo Analysis
large-monorepo is a large repository. To stay within memory constraints, this analysis used hybrid touch mode: structural complexity — CC, ND, FO — is measured precisely for every function. Git activity is tracked at the function level (via git log -L) only for files with 5 or more commits in the last 30 days; other files use a file-level approximation. Rankings therefore surface functions that are both structurally complex and in the most actively-changing parts of the codebase. Dormant code with high structural complexity will rank lower than it would under a full per-function analysis — to surface it, run hotspots analyze . --per-function-touches on a machine with sufficient memory.
Quadrant Distribution
Before getting into individual functions, here is the full triage picture across the repository.
26,368 functions analyzed
The shape of this distribution is unusual: 26,365 functions are inactive and structurally simple, and the three that surface are there purely because benchmark.js received a commit recently. There are no “fire” functions combining high complexity with active churn, and no “debt” functions hiding structural complexity under dormancy. The repository’s risk profile is genuinely low at this snapshot — but that makes the concentration in one file easier to see and worth paying attention to.
Top Hotspots
message — benchmark.js
message is a simple logging helper that wraps a string in separator lines and writes it to stdout. The source confirms exactly that: a console.log of dashes, then the message, then more dashes. With a cyclomatic complexity of 3, zero nesting depth, and fan-out of just 1, there is nothing structurally alarming here — the CC of 3 reflects the three execution paths implied by its small conditional context in the surrounding script, not internal branching within the function itself.
What makes it the top-ranked function is purely the activity signal: it was touched once in the last 30 days, and benchmark.js has accumulated 13 total commits across its lifetime. That commit history means the file is not frozen — people return to it. The external signals are clean: zero bug-linked commits, zero reverts, zero bug-fix fraction across those 13 commits, which is reassuring. There are also no other contributors recorded in the last 90 days, which suggests the recent commit may have come from a single maintainer working in a short burst rather than ongoing team-wide churn.
My recommendation here is not to refactor message itself — it is doing exactly one thing and doing it simply. The actionable note is to keep an eye on benchmark.js as a whole: if the script is being revisited frequently, it is worth asking whether the benchmark logic should be more formally structured or moved into a proper test harness that gets coverage rather than a standalone script.
spawnSync — benchmark.js
spawnSync is the script’s shell-execution wrapper. Looking at the source, it resolves a binary path through node_modules/.bin with platform detection for Windows (appending .cmd), then delegates to cp.spawnSync with inherited stdio and a custom environment — specifically injecting NX_TASKS_RUNNER_DYNAMIC_OUTPUT and NX_DAEMON flags. Three distinct functions are called: path.join, os.platform, and cp.spawnSync, which is where the fan-out of 3 comes from.
Cyclomatic complexity of 1 means there is effectively a single execution path — the Windows ternary is the only branch, and it is a simple substitution. Nesting depth is zero. This is a well-contained utility function, and the external signals back that up: no bug-linked commits, no reverts across the file’s 13-commit history.
The one thing I would flag is the environment injection pattern. Hardcoding NX_TASKS_RUNNER_DYNAMIC_OUTPUT: 'false' and deriving NX_DAEMON from a module-level flag means any future change to benchmark configuration has to touch this function. If the benchmark script grows — more tools to compare, more configuration knobs — consider pulling the environment object into a separate configuration structure so spawnSync stays a thin wrapper and the configuration lives somewhere more visible.
cleanFolders — benchmark.js
cleanFolders is the most structurally minimal function in the list: cyclomatic complexity of 1, zero nesting, and a fan-out of 0 — meaning it calls nothing at all right now. The source makes this explicit: the actual cleanup command is entirely commented out, leaving the function as an empty stub that is called in each benchmark loop but does nothing.
This is worth a brief flag, not because of structural risk, but because of intent. The comment inside the function reads “uncomment this to remove all artifacts after every run”, which means benchmark results may currently be affected by build artifact state carrying over between runs. Whether that is intentional (to measure warm-cache behavior) or an oversight is something only the maintainer can answer — but it is the kind of silent behavioral flag that is easy to miss when scanning the script quickly.
With an activity-weighted risk of 2.03 and no historical defect signal on the file, I would not prioritize this for any refactoring. The concrete action is simply to add a comment clarifying whether the disabled cleanup is deliberate, so the next person reading the script does not have to guess.
Reproduce This Analysis
git clone https://github.com/vsavkin/large-monorepo
cd large-monorepo
git checkout 264da142a8696e3647216eb0525c15bd61ab9da8
hotspots analyze . --mode snapshot --explain-patterns --force --hybrid-touches 5
To run the same analysis on your own codebase, run hotspots analyze . --mode snapshot in any local git repo — no configuration required.
I use Hotspots to highlight structural and activity risk — not “bad code.” I treat these findings as a prioritization aid, not a bug predictor. Editorial policy →