Help us shape the future of Go! Take the 2026 Go Developer Survey

go-hotspot

module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: MIT

README

go-hotspot

Find your highest-risk code. A Go CLI that ranks files by complexity × git churn — the "Your Code as a Crime Scene" methodology (Adam Tornhill / CodeScene), free and offline.

Go Reference

Why?

Code that is both complex AND changed often is where bugs concentrate. go-hotspot makes those files visible — instantly.

$ go-hotspot
────────────────────────────────────────────────────────────
 go-hotspot — code complexity × churn analysis
────────────────────────────────────────────────────────────
 window:    2025-08-10 → 2026-08-10
 commits:   5427
 files:     1543
 recency:   180-day half-life

RANK  PATH                        LANG  COMMITS  CHURN  AUTHORS  CYC   SLOC  SCORE  RISK
1     metaengine/typed_reader.go   Go    29       1593   2        160   788   100.0  critical
2     metaengine/store.go          Go    58       2602   3        69    465   39.0   medium
...

─ insights ─

[high] 41% of churn is concentrated in 3 of 1543 files
        Concentrate refactoring and test coverage on these files first — effort
        elsewhere moves the project's risk profile far less.
        files: metaengine/typed_reader.go, metaengine/store.go, …

[medium] metaengine/store.go and metaengine/schema.go changed together in 31 commits (64% degree)
        These files are structurally locked: one rarely changes without the other.
        Extract the shared concept, merge them, or put an interface between them.
        files: metaengine/store.go, metaengine/schema.go

[low] 3 highly complex file(s) rarely change
        Stable complexity is deferred risk: do not refactor preemptively, but add
        characterization tests now so the next forced change is safe.
        files: metaengine/serializer.go, ...

SCORE is relative: the worst-scoring file in the run is 100.0, matching the RISK bands (critical = 66+). CSV and JSON output carry the raw normalized score instead. For clean script piping use --format csv --no-header (machine formats have no insights/coupling sections); in table format, --no-header suppresses only the summary header while the insights and coupling sections remain — use --no-insights / --no-coupling to drop them individually.

What makes it different

Feature code-inspector noisemap code-maat go-hotspot
Recency weighting None None Time-window Exponential decay
Temporal coupling None None Yes (dead) Yes (native Go)
Go complexity CGo (tree-sitter) go/ast N/A go/ast (zero CGo)
Churn metric Commits Commits Both Commits + lines + recency-weighted
CGo required? Always Never JVM Never
Key innovations
  1. Recency-weighted churn — Old stable code decays naturally. A file that was complex but hasn't been touched in a year drops in rank. No competitor does this.

  2. Temporal coupling — Detects files that change together (hidden dependencies invisible in the code structure). Uses the code-maat formula: degree = sharedCommits / ceil(avg(totalCommits)) × 100.

  3. Zero CGo for Go — Uses go/ast from the standard library for true cyclomatic complexity. No C compiler needed. Code-inspector forces CGo even for Go, which is unnecessary.

  4. Indentation-based complexity — CodeScene's actual production approach. Language-neutral, correlates well with branching/looping. Used as fallback for non-Go languages.

Install

Option 1: Pre-built binary (no prerequisites)

Download the latest archive for your platform from GitHub Releases.

Option 2: go install (requires Go 1.26+ with GOEXPERIMENT=jsonv2)

GOEXPERIMENT=jsonv2 go install github.com/larsartmann/go-hotspot/cmd/go-hotspot@latest

Why GOEXPERIMENT=jsonv2? The graph rendering dependency uses Go's encoding/json/v2, which requires this experiment flag until it stabilizes in a future Go release. Nix users get this automatically via nix run ..

Option 3: Nix

nix run github:larsartmann/go-hotspot

Usage

# Analyze current directory (defaults: last year, .go files, 180-day half-life)
go-hotspot

# Analyze any repository without cd-ing into it
go-hotspot ~/projects/my-repo

# Production code only (exclude tests)
go-hotspot --include-tests=false

# Different time window and recency
go-hotspot --since "6 months ago" --recency 90

# JSON output for CI
go-hotspot --format json --top 10

# Specific path prefix
go-hotspot --paths "metaengine/"

# All file extensions, not just Go
go-hotspot --ext ".go,.py,.ts"

# Curated multi-language code profile (docs/config/lockfiles stay excluded)
go-hotspot --ext auto

# Custom coupling thresholds
go-hotspot --coupling-min-shared 3 --coupling-min-degree 50

# Drop auto-commit daemon cadence from churn (17% of fleet history is daemon noise)
go-hotspot --exclude-message '^chore: auto-commit'
Insights

Every report ends with an insights section that turns the raw ranking into recommended actions: churn concentration (the Pareto core — which few files carry most of the risk), high-churn/low-complexity noise, stable complex files to prepare tests for, single-maintainer hotspots, hotspots that went quiet, and the tightest co-change pair. Insights are relative to the analyzed set, sorted high → low severity, and --no-insights hides them.

Scores in table/markdown output use a 0–100 relative scale (the worst file is 100.0) so the number agrees with the RISK label; CSV and JSON keep the raw normalized score for machine consumers.

Templ files

.templ sources (Go + HTML in one file) get dedicated analysis: markup nesting is presentation structure, not branching logic, so indentation-based estimation is not applied. Indentation and nesting depth count only Go-logic lines (top-level Go, script block bodies, markup control flow), and cyclomatic complexity counts Go decision points on control-flow-shaped lines. Without this, markup indentation dominates the ranking (measured 2026-10-09: project_detail.templ scored CYC 1477 via indentation vs 64 with markup excluded). --ext auto includes .templ with this handling by default.

Flags
Flag Default Description
--since 1 year ago Git date spec for analysis window start
--until Git date spec for analysis window end
--branch HEAD Git revision to analyze
--recency 180 Recency half-life in days (0 = no decay)
--format table Output: table, markdown, csv, json, dot, mermaid, d2
--top 25 Rows to show (0 = all)
--complexity cyclomatic Metric: cyclomatic, indentation, sloc
--churn weighted Metric: weighted, commits, lines
--ext .go Comma-separated file extensions, or auto for the multi-language code profile
--include-tests true Include _test.go files
--include-generated false Include *.gen.go, *.pb.go
--paths Comma-separated path prefixes to include
--no-coupling false Skip temporal coupling analysis
--since-version Analyze commits since this git tag (e.g., v1.0.0)
--sort hotspot Sort: hotspot, stable, churn, commits, complexity, age
--coupling-min-shared 5 Minimum shared commits for coupling
--coupling-min-degree 30 Minimum coupling degree (%)
--output Write report to file instead of stdout
--fail-above 0 Exit with code 2 if max hotspot score exceeds this (0 = disabled)
--fail-risk Exit 2 if max score exceeds absolute band: low=0.01, medium=0.03, high=0.08, critical=0.15 (the RISK column is relative; these are not)
--no-header false Suppress summary header (for script piping)
--functions 0 Show top N functions by hotspot score (Go only, 0 = disabled)
--min-commits 0 Exclude files with fewer commits (0 = no minimum)
--author Show only files touched by this git author
--exclude-message Exclude commits whose subject matches this regex from all metrics (e.g. '^chore: auto-commit')
--no-insights false Hide the actionable insights section
--verbose false Print every skipped file instead of a summary count
[target] . Repository directory to analyze (extra arguments are rejected)
--version Print version information and exit
Exit Codes

go-hotspot follows BSD sysexits.h conventions for CI/CD integration:

Code BSD Name Meaning When
0 — Success Analysis completed without errors
1 EX_USAGE Invalid input Bad flag value or missing argument
2 — Threshold exceeded --fail-above or --fail-risk limit triggered
65 EX_DATAERR Source corrupt Unparseable Go file or read failure
69 EX_UNAVAILABLE Infrastructure Git not installed, not a repo, or output write failed
70 EX_SOFTWARE Internal bug Unexpected error (please report)

Each error prints a structured What/Why/Fix/WayOut message to stderr before exiting.

How it works

go-hotspot combines two signals:

  1. Complexity — For Go files, true McCabe cyclomatic complexity via go/ast (decision points + 1). For other languages, indentation-based complexity (CodeScene's approach: tabs/spaces correlate with branching depth).

  2. Churn — Three modes: raw commit count, raw lines changed, or recency-weighted churn (exponential decay with configurable half-life).

The hotspot score normalizes both dimensions across all files in the project (Tornhill methodology), then multiplies them:

hotspot = normalized(complexity) × normalized(recency-weighted churn)

Library API

The analysis pipeline is currently module-internal (internal/ packages): git.Collect → complexity.Analyze → hotspot.Score → report.Render.

A public library API is a ROADMAP item. Until then, go-hotspot is a CLI tool. To embed the analysis in your own Go project today, vendor the relevant source files from internal/.

history, _ := git.Collect(ctx, git.Options{Since: "1 year ago", HalfLifeDay: 180}, time.Now())
complexities := make(map[string]complexity.FileComplexity)
for path := range history.Files {
    complexities[path] = complexity.Analyze(path)
}
results := hotspot.Score(history, complexities, hotspot.ScoreOptions{
    Complexity: hotspot.MetricCyclomatic,
    Churn:      hotspot.ChurnWeighted,
}, time.Now())

License

MIT

Directories

Path Synopsis
cmd
go-hotspot command
Command go-hotspot analyzes code complexity × git churn to find hotspots.
Command go-hotspot analyzes code complexity × git churn to find hotspots.
examples
basic command
coupling command
internal
complexity
Package complexity computes source code complexity metrics.
Package complexity computes source code complexity metrics.
errors
Package errors provides domain-specific typed errors for go-hotspot, built on top of github.com/larsartmann/go-error-family.
Package errors provides domain-specific typed errors for go-hotspot, built on top of github.com/larsartmann/go-error-family.
git
Package git provides git history analysis for code hotspot detection.
Package git provides git history analysis for code hotspot detection.
hotspot
Package hotspot combines git churn and code complexity into ranked hotspot scores.
Package hotspot combines git churn and code complexity into ranked hotspot scores.
report
Package report renders hotspot analysis results in multiple output formats.
Package report renders hotspot analysis results in multiple output formats.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL