Blog

Essays, playbooks, and product updates.

Essays (52)

Don't Blindly Trust Open Source — Including Mine

Stephen Collins · Sep 27, 2026

I asked Claude to audit hotspots' scoring formula against hotspots-research's own findings. It found real drift, then made a real mistake correcting it, then made the same category…

Go Code Health: Patterns Across 62 Open-Source Repositories

Stephen Collins · Sep 26, 2026

We analyzed 62 popular open-source Go repositories to identify the most common code health antipatterns. Every single repository exhibited exit-heavy functions.

Using Hotspots on Its Own CFG Builder, and Watching a File Leave Critical

Stephen Collins · Sep 10, 2026

The honest test of a risk-scoring tool isn't a benchmark corpus, it's whether the score actually moves when you fix the thing it flagged. Here's what happened when I pointed hotspo…

A Field Guide to Signals That Don't Predict Bugs

Stephen Collins · Sep 9, 2026

A negative result is only wasted effort once. After that it's a fact worth writing down. Here's a survey of the ways I've tried to predict bugs that didn't work, organized by why t…

Does My Bug-Risk Model Still Work Once an AI Is Committing Code?

Stephen Collins · Sep 8, 2026

Churn, ownership concentration, and code coupling are old proxies, built from decades of human development patterns. An LLM doesn't get tired and doesn't lose context between commi…

I Tested a New Risk Formula Against Real CVEs. The First Result Was Too Good to Be True.

Stephen Collins · Sep 8, 2026

A five-signal formula beat my production risk score on 20 of 21 repos when I checked it against real CVEs. The real result, once I found the bug in my own comparison, was 9 of 10 —…

hotspots v1.36.0: risk isn't the only axis

Stephen Collins · Sep 6, 2026

The headline feature here started as a research question, not a product idea: do coupling and ownership actually predict something different from complexity-times-activity, or are …

hotspots v1.35.1–v1.35.4: an audit, twelve fixes, four days

Stephen Collins · Aug 30, 2026

Four releases, twelve PRs, and a pattern once I lined them up: most of these weren't reported by users hitting a wall. They were found by someone going looking — auditing coverage …

Rust Code Health: Patterns Across 27 Open-Source Repositories

Stephen Collins · Aug 27, 2026

I analyzed 27 open-source Rust codebases to identify recurring structural antipatterns. Every single repository contained long functions, and 24 of 27 had god functions.

hotspots v1.35.0: the CLI can finally tell you it's out of date

Stephen Collins · Aug 24, 2026

One feature, three rounds of scope-narrowing: I asked for a self-updating CLI, got talked down to check-and-report, then had that check moved from an explicit subcommand to somethi…

hotspots v1.34.1: a parser hang, a coupling regression, and a hedge I owed the docs

Stephen Collins · Aug 20, 2026

Three merged PRs, one patch release: a real repro against llvm-project, a performance fix that almost undid a validated research finding, and a docs gap I caught before merging.

Go Code Health: Patterns Across 31 Open-Source Repositories

Stephen Collins · Aug 2, 2026

I analyzed 31 open-source Go repositories to find the most common structural antipatterns. Every single repo exhibited exit-heavy functions and deep nesting.

JavaScript Code Health: Patterns Across 47 Open-Source Repositories

Stephen Collins · Aug 2, 2026

I analyzed 47 popular JavaScript repositories for structural antipatterns. The results show remarkably consistent patterns—exit-heavy functions appear in nearly every codebase.

Python Code Health: Patterns Across 37 Open-Source Repositories

Stephen Collins · Aug 2, 2026

I analyzed 37 Python repositories and found complex branching in every single one. Here's what the data reveals about structural risk in Python codebases.

I Shipped a Scoring Bug That Made Improvement Impossible — Here's the Fix

Stephen Collins · Jul 26, 2026

burst_score was computed over a file's entire commit history, which made it a one-way ratchet in the live risk score. A single bursty period years ago could keep a file stuck at CR…

Hotspots v1.25.1–v1.31.0: Explanations, a Regime-Aware Ranker, and a Burst-Score Formula Term

Stephen Collins · Jul 14, 2026

Six versions in four weeks. Most of it is quieter infrastructure — easier installs, a cleaner ranker feature set, tighter CI policy control. The two releases worth reading closely …

Why I let AI train on this

Stephen Collins · Jul 2, 2026

I just flipped ai-train=yes in robots.txt. Here's the reasoning, not just the setting.

How phrasing affects LLM compliance with spec security clauses

Stephen Collins · Jun 29, 2026

I tested five ways to phrase a security requirement across four language models. The phrasing most commonly used in practice was near the bottom on all four of them. The phrasing t…

How hotspots measures itself

Stephen Collins · Jun 28, 2026

A ranking tool that can't measure its own accuracy is just vibes with syntax highlighting. Here's how hotspots keeps itself honest.

When You Don't Have Enough History to Train

Stephen Collins · Jun 24, 2026

Every new repo starts with the same problem: no defect labels, no training signal, no basis for a risk ranking beyond 'how often does this file change?'

The Signal That Evaporated (And the One That Didn't)

Stephen Collins · Jun 24, 2026

Two signals looked nearly identical on paper. One collapsed under temporal holdout. The other survived — and the difference tells you something important about how to design defect…

Tabular Beats LLM, and That's the Right Answer

Stephen Collins · Jun 24, 2026

On most codebases, a small XGBoost model trained on git history features outperforms a fine-tuned 7B LLM at defect risk ranking — not because the LLM is bad, but because the data i…

Spearman ρ Looked Fine. P@10 Was Zero.

Stephen Collins · Jun 24, 2026

Spearman ρ can be positive and meaningful while the top-ranked files contain zero future bugs. Here's how I found that out, why it happens, and how to catch it before it reaches a …

Hotspots v1.25.0: Repo Screener, CI Workflows, and Cleaner TypeScript Names

Stephen Collins · Jun 16, 2026

Three things that should have existed already: a way to know if training is worth running, a one-command CI setup, and TypeScript functions that show up under their actual names.

The Coupling Signal That Kept Inverting Itself

Stephen Collins · Jun 10, 2026

I tried three formulations of co-change coupling for defect risk ranking. Raw co-occurrence inverted on dense codebases. Mutual information made it worse.

Your Transformer Featurizer Only Helps When Your History Is Broken

Stephen Collins · Jun 10, 2026

Transformer embeddings on source code improved defect ranking on exactly the repos where git history had already failed — and added near-zero or negative signal everywhere else. He…

Java Code Health: Patterns Across 10 Open-Source Repositories

Stephen Collins · Jun 6, 2026

I analyzed 10 popular Java repositories and found the same three antipatterns in every single one. Here's what that tells us about Java development practices at scale.

The Defect Prediction Trap

Stephen Collins · Jun 5, 2026

I fine-tuned a model that scored Spearman ρ=+0.678 on defect risk. Then I zeroed out the git history fields and it dropped to −0.142. The model wasn't reading code. It was relaying…

The Metric That Hides Failure

Stephen Collins · Jun 5, 2026

Spearman ρ measures average rank agreement across all files. Precision@K measures whether the files engineers actually review are in the right place. On mature, stable codebases, a…

Which LLM Vendor Wins at Defect Risk Prediction?

Stephen Collins · Jun 5, 2026

When evaluating open-weight models for code analysis, the vendor comparison you run in 2024 might give you the wrong answer in 2025.

When Your Labels Lie: File-Level Bug Attribution Inflates Defect Rates

Stephen Collins · May 31, 2026

Most defect prediction pipelines share a quiet assumption: if a file was touched in a bug-fix commit, every function in that file was 'buggy' — and that assumption silently inflate…

How to Know If Fine-Tuning Will Help Before You Run It

Stephen Collins · May 25, 2026

Before you spend compute on per-repo fine-tuning, one scalar from the codebase's bug-fix history can tell you whether it's worth running. Here's how it works and what it can't pred…

Go Code Health: Patterns Across 12 Open-Source Repositories

Stephen Collins · May 22, 2026

I analyzed 12 Go repositories and found the same structural antipatterns in every single one. Here's what the data shows.

JavaScript Code Health: Patterns Across 19 Open-Source Repositories

Stephen Collins · May 22, 2026

I analyzed 19 open-source JavaScript repositories and found exit-heavy functions in 89% of them. Here's what the data reveals about structural debt in the JavaScript ecosystem.

Python Code Health: Patterns Across 12 Open-Source Repositories

Stephen Collins · May 22, 2026

I analyzed 12 open-source Python repositories and found complex branching in 100% of them. Here's what the data reveals about Python code health patterns.

Rust Code Health: Patterns Across 11 Open-Source Repositories

Stephen Collins · May 22, 2026

I analyzed 11 popular Rust repositories to find the most common code health antipatterns. Every single one had exit-heavy functions and long functions — Rust's ownership model does…

TypeScript Code Health: Patterns Across 44 Open-Source Repositories

Stephen Collins · May 22, 2026

I analyzed 44 TypeScript repositories and found structural antipatterns in nearly all of them. Here's what the data shows about where complexity accumulates.

Hotspots v1.18.0: Compact Storage

Stephen Collins · May 21, 2026

Snapshot history is valuable. Unbounded snapshot growth is not. v1.18.0 ships two compaction levels that shrink your .hotspots directory without losing the history that matters.

Hotspots v1.17.0: Model Risk Map and Monorepo Subsystems

Stephen Collins · May 20, 2026

Hotspots already tells you which functions are risky. v1.17.0 tells you which models those functions cluster around — and makes risk scores meaningful inside monorepo package bound…

Can a Fine-Tuned LLM Learn Which Code Is Risky?

Stephen Collins · May 16, 2026

I spent a few weeks running a controlled experiment: can a small language model, fine-tuned on a specific codebase's history, learn to rank code by defect risk better than a hand-t…

Hotspots now handles monorepos — what changed from v1.11 to v1.15.1

Stephen Collins · Apr 24, 2026

The diff command shipped in v1.11. Since then: four months of OOM fixes, a new SQLite pipeline, hybrid touch mode, and --auto-analyze. Hotspots now handles 28k-function monorepos a…

TypeScript produces less exit-heavy code than Go or Python — and more branching

Stephen Collins · Apr 17, 2026

exit_heavy averages 5.3 in non-TypeScript repos and 4.5 in TypeScript. complex_branching has the highest max-out rate of any pattern in TS. The flip is structural, not stylistic — …

Hub functions are a frontend framework problem — here's the data

Stephen Collins · Apr 17, 2026

hub_function barely appears in Go, Rust, or Python repos. In TypeScript UI framework code it's concentrated in coordinator functions with fan-out values of 37–112. Here's why.

Five structural patterns appear in nearly every TypeScript OSS repo I've analysed

Stephen Collins · Apr 17, 2026

Five structural patterns — god_function, long_function, exit_heavy, complex_branching, deeply_nested — appear in 86–100% of the 28 TypeScript OSS repos I've analysed. Here's what t…

Running Hotspots on expo/expo Without Running Out of Memory

Stephen Collins · Apr 12, 2026

expo/expo crashed Hotspots with an OOM. Here's the full account: what broke, the systematic fixes across memory and CPU, and the new escape-hatch flags that make very large repos p…

hotspots diff: Compare Complexity Between Any Two Branches, Tags, or Commits

Stephen Collins · Mar 26, 2026

Delta mode compares a commit to its parent. That's fine for local development — but CI needs to compare a PR branch to main, and tag comparisons need to reach back further. hotspot…

What Happens When You Run Hotspots on 102,000 Functions

Stephen Collins · Mar 14, 2026

I stress-tested Hotspots against VS Code (102k functions) and found an O(N³) bug, a sampling tail exclusion bug, and a philosophical question about approximation accuracy — all in …

AI Agents Can Pass Tests. They Still Can't Maintain Systems.

Stephen Collins · Mar 11, 2026

AI coding tools have made writing software dramatically easier. A new benchmark shows maintaining it is still the hard part — and the failure modes are predictable.

AI Made Code Cheap. The Bottleneck Is Now Understanding Systems.

Stephen Collins · Mar 8, 2026

AI removed code production as the engineering bottleneck. The new constraint is comprehension - and most developer tooling wasn't built for that problem.

The 20% Rule for Codebases: A Practical Refactoring Playbook

Stephen Collins · Feb 22, 2026

Use this practical worksheet to intersect change and complexity, pick three high‑leverage targets, and add simple guardrails to prevent backslide.

Stop Missing Regressions in PRs: Complexity Policy Checks in CI

Stephen Collins · Feb 22, 2026

Code review misses slow creep. Use Hotspots policies in CI to stop risky complexity changes before they merge — start warn‑only, then flip to blocking.

Introducing Hotspots: Find Risky Functions and Stop Regressions

Stephen Collins · Feb 21, 2026

Meet Hotspots — a lightweight analyzer with explainable risk, shareable reports, and CI policies so you fix what matters and keep it from slipping back.

Repository Analyses

Nightly hotspot reports for popular open-source repos. Browse all →

keras-team/keras

Keras's summary, export and RNN paths carry the highest activity risk

python complex_branchingexit_heavy
Oct 25
locustio/locust

Locust's startup path carries the highest activity risk — 5 functions to address first

python complex_branchingexit_heavy
Oct 24
jmoiron/sqlx

sqlx's scanning and binding core carries the highest risk: 5 untouched functions

go exit_heavygod_function
Oct 23
behave/behave

behave's runner and CLI entry points carry the highest activity risk

python god_functionlong_function
Oct 22
segmentio/kafka-go

kafka-go's record decoding carries the highest activity risk — 5 functions to fix first

go exit_heavygod_function
Oct 22
apache/log4j

log4j's contrib and config code carries the highest risk: 5 untouched functions

java deeply_nestedcomplex_branching
Oct 21
urfave/cli

urfave/cli: run and flag parsing carry the highest activity risk

go complex_branchingexit_heavy
Oct 20
vectordotdev/vector

Vector's ingestion and sink paths carry the highest risk — 5 functions first

rust long_functioncomplex_branching
Oct 19
pugjs/pug

Pug's AST walker carries the highest structural risk — 5 functions to address first

javascript exit_heavylong_function
Oct 18
paramiko/paramiko

Paramiko's transport and client layers carry the highest risk: 5 functions to fix first

python exit_heavygod_function
Oct 17
openssh/openssh-portable

openssh-portable's option parsing and entry points carry the top risk

c complex_branchingdeeply_nested
Oct 16
hashicorp/packer

Packer's plugin installer and build runner carry the highest structural risk

go exit_heavygod_function
Oct 15
swaggo/swag

swag's parsing core carries the highest activity risk — 5 functions to address first

go exit_heavygod_function
Oct 14
charmbracelet/glow

Glow's TUI layer carries the highest activity risk — 5 functions to address first

go exit_heavygod_function
Oct 13
boto/boto3

boto3's resource layer carries the highest activity risk — 5 functions to address first

python god_functionlong_function
Oct 12
checkstyle/checkstyle

Checkstyle's test fixtures and site search carry the highest activity risk

java deeply_nestedcomplex_branching
Oct 11
borgbackup/borg

Borg's repository and archive core: 5 high-risk functions to fix first

python complex_branchingexit_heavy
Oct 10
minio/minio-go

minio-go's request pipeline carries the highest activity risk: 5 functions first

go exit_heavylong_function
Oct 9
wez/wezterm

WezTerm's render and input paths carry the highest risk — 5 functions to fix first

rust complex_branchingexit_heavy
Oct 8
PyCQA/flake8

flake8's config and parsing layer carries the highest activity risk

python complex_branchingexit_heavy
Oct 7
encode/starlette

Starlette's test client and middleware carry the highest activity risk

python exit_heavygod_function
Oct 6
nats-io/nats-server

nats-server's JetStream cluster file holds 3 of the top 5 hotspots

go complex_branchingdeeply_nested
Oct 6
dandavison/delta

Delta's run_app carries the highest activity risk — 5 functions to address first

rust complex_branchingexit_heavy
Oct 5
apache/spark

Spark's Python runtime carries the highest activity risk — 5 functions to address first

scala complex_branchingdeeply_nested
Oct 4