Every computer has those files. The ones that everyone knows are dangerous. Those where a "simple" change requires three days of careful testing. The ones that continue to appear in autopsies.
I spent a lot of time noticing that pattern (the same 10% of a codebase that causes 80% of the pain) and wondering why our tools didn't just show us where that 10% was. ESLint can tell you that a function has high cyclomatic complexity. But that doesn't tell you if it's actively being changed, who's touching it, or if it's likely to bite you in the next sprint.
So I built Hotspots, a Rust CLI that finds risky code by combining structural complexity with actual git activity.
The core idea: complexity × change
Raw complexity metrics are useful, but they are incomplete. A complicated feature that hasn't been touched in two years is not your emergency today. The real danger is complexity plus active change: functions that are structurally difficult to reason about and that change periodically.
Hotspots calculates a risk score for each feature in its codebase by combining:
- Structural signals: cyclomatic complexity (CC), nesting depth (ND), fan-out (FO), unstructured outputs (NS)
- Activity signals: git churn in the last 30 days, contact frequency (commit count), recency, call graph influence
The result is an activity-weighted risk score: a prioritized list of features that are both dangerous and active. They are not functions that you should eventually clean up. Features that should interest you in this sprint.
Getting started
Install with a single curl:
curl -fsSL https://raw.githubusercontent.com/Stephen-Collins-tech/hotspots/main/install.sh | sh
Point to any repository and run a snapshot:
access point analysis. --snapshot mode --text format
You will obtain a classified result grouped into risk bands:
Critical (risk ≥ 9.0):
ProcessPlanUpgrade src/api/billing.ts:142 risk 12.4 CC 15 NA 4 FO 8
High (6.0 ≤ risk < 9.0):
validateSession src/auth/session.ts:67 risk 9.8 CC 11 NA 3 FO 7
applySchema src/db/migrations.ts:203 risk 8.1 CC 10 ND 2 FO 5
For a shareable HTML report:
access point analysis. --snapshot mode --html format --output report.html
Explanation mode: Why is this feature risky?
Once you have the list, you'll want to know why something is ranked high and what to do about it. Pass --explain-patterns
and Hotspots annotates each function with anti-pattern tags:
access point analysis. --snapshot mode --json format --explain-patterns > snapshot.json
In v1.2.0, Hotspots detects two levels of patterns:
Level 1 - Structural:
complex_branching
, deeply nested
, heavy_output
, long_function
, god_function
Level 2: relational and temporal:
hub_function
, cyclic_cube
, intermediary
, neighbor_risk
, obsolete_complex
, abandonment magnet
, shotgun_target
, volatile_god
A function labeled god_function
+ cyclic_center
is monolithic and at the center of a dependency cycle - a very different refactoring situation than the one labeled exit_heavy
+ long_function
. Labels make the action obvious.
CI policy checks: Stop regressions before they merge
Identifying critical points is helpful. It is best to prevent new ones from landing. Hotspots has a delta mode that compares the current branch to the baseline and applies configurable policy checks:
access point analysis. --delta mode --policy
Policies you can configure:
- Critical introduction: fails if a new feature reaches the critical band
- Excessive regression: fails if a feature's risk score increases by a large delta
- Rapid growth: signal unusually rapid growth in complexity
- Monitoring/Attention: warns when functions approach thresholds
Add it to CI and the complexity regressions will not exceed the PR before the review. Start it in warning-only mode, get the computer used to the signal, and then switch to blocking when you're ready.
Here is a minimal GitHub Actions step:
- name: access point policy check
run: hotspot analysis. --mode