eslint's rule engine carries the highest activity risk — 2 functions to address first

Two actively-changing rule implementations — no-unused-vars (CC 114) and indent-legacy (CC 88) — dominate eslint's structural risk profile at commit ee9ff31.

Stephen Collins ·
oss javascript refactoring code-health

Antipatterns Detected

exit_heavy5god_function5long_function5complex_branching4deeply_nested4cyclic_hub2hub_function2

Key Points

What is a god function and why does it matter in eslint?

A god function is one that has grown to handle too many distinct responsibilities in a single body — in eslint's case, `create` in `no-unused-vars.js` likely encodes every detection scenario for unused variables in one place, making it hard to understand, test, or safely change any single scenario without affecting the others. Because eslint rules are consumed by millions of projects, a regression in a god function like this one can silently break linting behavior across the entire ecosystem. Splitting it into focused sub-functions reduces the blast radius of any individual change.

How do I reduce cyclomatic complexity in JavaScript?

The most direct technique is extract-method refactoring: identify groups of related branches that share a clear sub-purpose, move them into a named function with its own tests, and replace the original branching block with a single call. This lowers the parent function's path count and makes each extracted path independently verifiable.

Is eslint actively maintained?

The activity data at commit ee9ff31 strongly suggests yes — `create` in `no-unused-vars.js` has a recent commit activity of 19.4 and `create` in `indent-legacy.js` has a recent commit activity of 20.04, both indicating high recent commit frequency on these files. A codebase with 672 critical-band functions and sustained churn on core rule implementations is one under active development, not abandonment.

How do I reproduce this analysis?

Run the hotspots CLI against the eslint repository pinned to commit ee9ff31 and add `"exclude": ["docs/src/assets/**"]` to your `.hotspotsrc.json` to filter out vendored documentation assets.

What does activity-weighted risk mean?

Complexity × recent commit frequency — functions that are hard to understand AND actively changing are the highest priority for refactoring.

At commit ee9ff31, eslint’s highest-priority structural risk lives inside its rule implementations: create in lib/rules/no-unused-vars.js carries an activity-weighted risk score of 20.35 with a recent commit activity of 19.4, meaning it is not just structurally extreme (CC 114) but is being changed frequently enough that each edit competes with nearly 114 independent execution paths for correctness. Across 11,396 total functions, 672 are rated critical — roughly 1 in 17 — with the rule-engine layer contributing the sharpest combination of complexity and churn. eslint is the JavaScript ecosystem’s dominant linting engine, so regressions in core rules have an unusually wide blast radius.

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 5 Hotspots

FunctionFileRiskCCNDFO
kdocs/src/assets/js/css-vars-ponyfill@2.js26.876968
udocs/src/assets/js/css-vars-ponyfill@2.js21.034337
createlib/rules/indent-legacy.js20.988952
<anonymous>docs/src/assets/js/css-vars-ponyfill@2.js20.41899161
createlib/rules/no-unused-vars.js20.3114777

Codemod / Tooling Files in Results

Three of the top five hotspots — functions k, u, and <anonymous> — belong to docs/src/assets/js/css-vars-ponyfill@2.js, a vendored third-party CSS custom-property polyfill bundled into the documentation site. The @2.js filename suffix is a classic bundled-asset naming pattern. Their high recent commit activity (up to 20.2) reflect churn in the docs build rather than changes to eslint’s linting engine itself, and their extreme CC values (up to 189) are characteristic of minified or transpiled vendor bundles. Exclude these from your hotspots analysis by adding "exclude": ["docs/src/assets/**"] to your .hotspotsrc.json.

Hotspot Analysis

create — lib/rules/no-unused-vars.js

The create function in no-unused-vars.js is almost certainly the entry point that registers AST visitors and implements the full detection logic for unused variables — one of eslint’s most-used and most-configured rules. A cyclomatic complexity of 114 means there are 114 independent paths through this function, each representing a distinct scenario (destructuring, exports, catch bindings, ignore patterns, etc.) and a required test case. A max nesting depth of 7 and fan-out of 77 mean the logic is both deeply conditional and broadly coupled to 77 other callees, so a single change can propagate in unexpected directions. With a recent commit activity of 19.4, this is not a static debt item — it is actively being modified, making each change a regression gamble across those 114 paths.

Recommendation: Before any refactoring, add characterization tests that cover the rule’s documented option combinations to establish a regression baseline. Then use extract-method refactoring to pull distinct detection strategies (e.g., handling of destructuring, exports, or ignore patterns inferred from the rule’s known feature set) into named sub-functions, reducing the core create function’s CC and making individual paths independently testable.

create — lib/rules/indent-legacy.js

The indent-legacy.js path signals that this is an older, preserved implementation of eslint’s indentation rule — the legacy suffix suggests it is maintained for backward compatibility rather than active development, yet its recent commit activity of 20.04 is the highest of any project-owned function in the dataset, meaning it is being changed more frequently than its ‘legacy’ label implies. With a CC of 88 and max nesting depth of 9, the function encodes an extremely large number of formatting edge cases across deeply nested conditional logic. A fan-out of 52 adds broad coupling on top of that complexity, and the exit-heavy pattern means multiple return paths must all remain correct as edits land.

Recommendation: Audit why a legacy-labeled rule is accumulating a recent commit activity of 20.04 — determine whether these commits are bug fixes, backports, or ongoing feature additions, since the answer changes the risk calculus. If the rule is truly in maintenance mode, freeze its interface, document the known edge cases as inline comments, and enforce a test-gate on all PRs touching this file to prevent silent regressions across its 88 complexity paths.

Patterns Found

Antipatterns detected across the top functions in this snapshot:

PatternOccurrences
exit_heavy5
god_function5
long_function5
complex_branching4
deeply_nested4
cyclic_hub2
hub_function2

These labels belong to two tiers — Tier 1 (structural): complex_branching, deeply_nested, exit_heavy, long_function, god_function. Tier 2 (relational/temporal): hub_function, cyclic_hub, middle_man, neighbor_risk, stale_complex, churn_magnet, shotgun_target, volatile_god.

Key Takeaways

  • create in lib/rules/no-unused-vars.js has CC 114 and recent commit activity 19.4 — add a characterization test suite covering all documented rule options before the next PR touches this file, or you are editing 114 paths without a safety net.
  • The legacy label on lib/rules/indent-legacy.js is misleading: its recent commit activity of 20.04 is the highest of any project-owned function, signaling active churn. Investigate whether these commits represent ongoing bug fixes, and consider a per-PR test gate to protect its 88 complexity paths.
  • Exclude docs/src/assets/js/css-vars-ponyfill@2.js from hotspots scoring via .hotspotsrc.json — its three entries inflate the critical-band count and obscure the true risk distribution in eslint’s rule engine.

Reproduce This Analysis

git clone https://github.com/eslint/eslint
cd eslint
git checkout ee9ff31cee13712d2be2a6b5c0a4a54449fe9fe1
hotspots analyze . --mode snapshot --explain-patterns --force

To run the same analysis on your own codebase, run hotspots analyze . --mode snapshot in any local git repo — no configuration required.

Hotspots highlights structural and activity risk — not “bad code.” Findings are a prioritization aid, not a bug predictor. Editorial policy →

Run this on your own codebase

Hotspots runs locally in under a minute — no account, no data leaves your machine.

macOS
$ brew install Stephen-Collins-tech/tap/hotspots
Linux / cargo
$ cargo install hotspots-cli
Run in any repo
$ hotspots analyze .
★ Star on GitHub

Related Analyses