I would start with run_app in src/main.rs: its cyclomatic complexity is 46, against 30 for the next-ranked function, calling_process_cmdline, and 9 for the fifth-ranked function, list_languages. It is also in the fire quadrant, with an activity-weighted risk score of 19.27 and zero days since last change, so this is a live regression risk rather than a backlog item. In dandavison/delta, 37 of 504 functions are critical, and the other four top hotspots are debt-quadrant functions that haven’t been changed in 633 to 756 days.
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
| Function | File | Risk | CC | ND | FO |
|---|---|---|---|---|---|
run_app | src/main.rs | 19.3 | 46 | 7 | 27 |
calling_process_cmdline | src/utils/process.rs | 13.7 | 30 | 4 | 10 |
truncate_str_impl | src/ansi/mod.rs | 13.1 | 14 | 8 | 10 |
show_themes | src/subcommands/show_themes.rs | 13.0 | 15 | 4 | 14 |
list_languages | src/utils/bat/assets.rs | 12.9 | 9 | 4 | 11 |
504 functions analyzed
High cyclomatic complexity — many independent execution paths, each a potential bug surface and required test case.Exit Heavy×6Exit Heavy
Multiple return or throw paths dispersed through the body — each exit needs separate test coverage.God Function×6God Function
Calls an unusually large number of distinct functions (high fan-out), making it the structural centre of gravity for a subsystem.Long Function×5Long Function
Function body is too long to review in a single pass; likely contains multiple distinct responsibilities.Deeply Nested×4Deeply Nested
Control structures nested 4+ levels deep, making it hard to reason about the full execution state at inner branches.Stale Complex×3Stale Complex
High structural complexity but untouched for a long time — structural debt that will bite whoever opens it next.
The quadrant split shapes how I read this list. Of 504 functions, 26 are in the fire quadrant and 93 are in the debt quadrant. My top five has one fire function and four debt functions, so the first item is a ‘change it carefully today’ problem and the other four are structural debt to pay down before someone next has to edit them. None of the five files shows a bug-linked commit or a revert in the file-level history, so I read all five as maintainability and blast-radius concerns rather than evidence of defects.
run_app — main.rs
This is the top-level orchestrator, and the excerpt shows what that costs. Its cyclomatic complexity is 46, nesting depth reaches 7, and it calls 27 distinct functions. The excerpt shows it handling version and help output, setting the calling process for subcommands, dispatching through a long else-if chain of subcommand flags (completion generation, language listing, theme listing and showing, colors, ANSI parsing), then handling the show-config case, building the configuration, and starting pager setup. Argument interpretation, subcommand dispatch and output wiring all live in one body. The function is flagged as complex branching, deeply nested, exit-heavy, a god function and long.
The activity signal makes it urgent. It has 1 touch in the last year and was changed today, so any work in progress lands in a function where every one of those 46 paths is a candidate test case and the many early returns each need their own coverage. The file-level history is quiet: 6 total commits, no bug-linked commits, no reverts, one distinct author in the last 90 days. That reads as a file that is rarely edited but being edited now, in its most complex function.
I recommend extracting the subcommand dispatch chain into its own function that returns an optional result, and pulling the pager and output setup into a second helper. The dispatch chain in the excerpt is a flat run of mutually exclusive flags, so it should lift out without touching the lifetime-sensitive writer block the source comments warn about. Even that one extraction would remove a large share of the branching.
calling_process_cmdline — process.rs
To work out which command invoked delta, this function inspects the process tree: the parent first, then parent-of-parent, then a naive sibling pid, and finally a pid-range search. It has cyclomatic complexity 30, nesting depth 4 and fan-out of 10, and it hasn’t been changed in 633 days. It is generic over a process-interface trait and a closure, with a test-only branch compiled in under the test configuration, so some of its paths exist only for tests.
I would treat this as structural debt with a high blast radius when next touched. The behaviour depends on pid adjacency and process-tree shape across operating systems, which is hard to reproduce in tests, and the multiple return paths mean each strategy needs its own fake. The file has a bug-fix rate of 20% across 5 commits, with no bug-linked commits and no reverts, so I would call that a mild flag, not a defect signal. My first step would be splitting the three lookup strategies (parents, sibling, closest match in the process tree) into separate functions, so the orchestration reads as a short fallback chain.
truncate_str_impl — mod.rs
Here the problem is depth, not breadth. Cyclomatic complexity is a moderate 14, but nesting reaches 8, which is the level I treat as a strong refactoring signal. The excerpt shows a loop over ANSI-aware segments, an inner loop over graphemes, and inside that the double-width handling with optional fill characters and a fallback branch for unexpectedly wide graphemes. The function also calls itself to measure the tail, so the recursion adds a second thing to reason about.
It hasn’t been changed in 756 days. The file-level numbers are the most notable on my list: a bug-fix rate of 66.67% across 3 commits, and a pull-request review comment density of 1.0, the only nonzero value among my five. There are no bug-linked commits and no reverts, and three commits is a small sample, so I would not read this as proof of defects. Still, a deeply nested text-width function with a history skewed toward fixes is where I would want tests around double-width and combining characters before anyone edits it. Extracting the per-grapheme step into a helper that returns whether to continue would flatten the nesting by several levels.
show_themes — show_themes.rs
This subcommand renders a sample diff for each available theme. Cyclomatic complexity is 15 and fan-out is 14, with nesting at 4. The excerpt shows it reading either a built-in sample diff or piped stdin, constructing options and configuration repeatedly from git config, setting up a pager, and then looping over themes with a compound dark/light/color-mode filter and error handling that exits the process on a broken pipe. It is flagged as a god function and exit-heavy, and it hasn’t been changed in 756 days.
The history is thin: 2 commits, no bug-linked commits, no reverts. I would frame this as maintainability debt with limited urgency. The cheap win is lifting the dark/light/color-mode condition into a small named predicate, then pulling the per-theme rendering into its own function. That makes the filter testable without a terminal or pager.
list_languages — assets.rs
The lowest-complexity function on the list, list_languages has cyclomatic complexity 9, nesting depth 4 and fan-out of 11. It collects and sorts syntaxes, then prints them either as a plain list or as aligned columns with line wrapping for long extension lists. The excerpt shows two local constants (loop_through and colored_output) fixed in the function body, which may leave one of the two output modes unused as written, the kind of thing a refactor would surface. It hasn’t been changed in 756 days and the file has a single commit in its history.
Its risk score is high on the strength of fan-out and the god-function pattern, not on branching, which is why it earns a place here at all. It is the entry I would leave for last. If someone is in the file anyway, separating the data gathering from the formatting would make the wrapping logic testable on its own.
What I would do in order
My order of attack: break up run_app first because it is complex and moving, add tests around truncate_str_impl and calling_process_cmdline before anyone next edits them, and treat show_themes and list_languages as opportunistic cleanups.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
complex_branching | 7 |
exit_heavy | 6 |
god_function | 6 |
long_function | 5 |
deeply_nested | 4 |
stale_complex | 3 |
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.
See more analyses with these patterns: complex_branching, deeply_nested, exit_heavy, god_function, long_function, stale_complex.
Reproduce This Analysis
git clone https://github.com/dandavison/delta
cd delta
git checkout 3c2269c6b845913965575f11b0196d70ef5352f3
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.
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 →