Across 1,174 analyzed functions in alacritty/alacritty, 48 land in the critical band, and the top five share one pattern: high structural complexity paired with almost no recent change activity. The worst offender, box_drawing in the text renderer, carries a cyclomatic complexity of 150 and hasn’t been modified in 269 days — not a live regression risk, but a time bomb sitting in the box-drawing glyph rasterizer, waiting to detonate on whoever touches it next. I’d treat this list as a debt inventory to work through before the next big feature push touches these files, not a fire drill.
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 |
|---|---|---|---|---|---|
box_drawing | alacritty/src/renderer/text/builtin_font.rs | 15.5 | 150 | 4 | 21 |
handle_event | alacritty/src/event.rs | 14.5 | 101 | 5 | 36 |
key_input | alacritty/src/input/keyboard.rs | 13.1 | 22 | 2 | 25 |
regex_search_internal | alacritty_terminal/src/term/search.rs | 12.8 | 27 | 6 | 16 |
execute | alacritty/src/input/mod.rs | 12.6 | 173 | 3 | 57 |
<BandChart fire=‘1’ debt=‘203’ watch=‘3’ ok=‘967’ />
<PatternCloud patterns=‘long_function:6,god_function:5,stale_complex:4,complex_branching:3,exit_heavy:2,deeply_nested:2’ />
The quadrant split tells the real story here: 203 functions sit in the debt quadrant against a single function in the fire quadrant. Alacritty’s structural risk is overwhelmingly dormant-but-complex, not actively churning. That single fire-quadrant function, motion in alacritty_terminal/src/vi_mode.rs, carries an activity-weighted risk score of 9.67 with 1 touch in the last 30 days and a cyclomatic complexity of 53 — worth a mention because it’s the one place in this dataset where complexity and live editing overlap, but it didn’t make the top five and isn’t a top-priority item today.
All five of my top hotspots score in the debt quadrant. Each is complex enough to be dangerous whenever someone next has to modify it, but none are being actively edited right now. I’m framing each one below by its blast radius, not its churn.
box_drawing — alacritty/src/renderer/text/builtin_font.rs
<FunctionCard fn=‘box_drawing’ file=‘alacritty/src/renderer/text/builtin_font.rs’ risk=‘15.46’ band=‘critical’ cc=‘150’ nd=‘4’ fo=‘21’ touches=‘0’ />
This is the top-ranked function in the whole scan, and the numbers explain why. A cyclomatic complexity of 150 against a max nesting depth of 4 and fan-out of 21 puts it solidly in ‘god function’ territory — the source excerpt confirms it: a match over character ranges (diagonal box-drawing glyphs like ╱, ╲, ╳) that branches into per-character canvas construction, followed by a second large match handling dash and gap variants, each with its own local closures for line-drawing math. It hasn’t been touched in 269 days and saw zero commits in the last 30 days, with only 6 total commits and zero linked bug fixes on record — this reads as maintainability debt rather than a known-defective area. The risk isn’t that it’s buggy today; it’s that the next person who has to add a new box-drawing character or fix a rendering edge case has to hold 150 branches of glyph-specific geometry in their head at once. I’d extract each character-family branch (diagonals, dashes, rounded corners, etc.) into its own named function before the next glyph gets added here.
handle_event — alacritty/src/event.rs
<FunctionCard fn=‘handle_event’ file=‘alacritty/src/event.rs’ risk=‘14.53’ band=‘critical’ cc=‘101’ nd=‘5’ fo=‘36’ touches=‘0’ />
Cyclomatic complexity of 101, nesting depth of 5, and a fan-out of 36 make this the broadest-reaching function in the set. The excerpt shows a nested match over WinitEvent and then EventType — cursor blink timeout handling, bell command spawning, clipboard store and load, title updates, color requests — each branch touching a different subsystem through self.ctx. That fan-out of 36 distinct calls means a change to any one event type risks rippling into display state, PTY writes, or clipboard handling elsewhere in the file. It’s been dormant for 179 days, but the file’s history shows a bug-fix rate of 26.7% and one prior revert across 15 total commits, plus a review-comment density of 7.875 — meaningfully higher scrutiny than the other functions here, suggesting reviewers have already flagged this area as tricky. I’d start by splitting the terminal-event branch out of the top-level match into its own handler function; it’s the largest single arm and self-contained enough to extract without touching the winit-level dispatch.
key_input — alacritty/src/input/keyboard.rs
<FunctionCard fn=‘key_input’ file=‘alacritty/src/input/keyboard.rs’ risk=‘13.14’ band=‘critical’ cc=‘22’ nd=‘2’ fo=‘25’ touches=‘0’ />
Lower nesting (2) and moderate complexity (22) compared to the others, but a fan-out of 25 and a string of early returns — IME preedit check, key-release short-circuit, hint-selection loop, inline-search capture, key-binding dispatch, search-input loop, vi-mode guard — each exiting the function before reaching the final byte-writing logic. That’s the ‘exit-heavy’ pattern showing up directly in the excerpt: seven distinct early-return points before the main input path runs, meaning seven separate conditions a test suite has to cover to reach full path coverage. It hasn’t changed in 288 days, the longest gap in the top five, and the file carries a bug-fix rate of 50% across 10 commits with one prior revert — half of this function’s history has been fix-related, which raises the stakes on any future change here even though nothing recent points to an active author working on it. I’d consolidate the early-return guards into a single ordered predicate chain or a should_handle_as_text helper, cutting the exit count without touching the byte-construction logic at the bottom.
regex_search_internal — alacritty_terminal/src/term/search.rs
<FunctionCard fn=‘regex_search_internal’ file=‘alacritty_terminal/src/term/search.rs’ risk=‘12.76’ band=‘critical’ cc=‘27’ nd=‘6’ fo=‘16’ touches=‘0’ />
The nesting depth of 6 stands out here — the deepest in the top five — and the excerpt shows why: a DFA-driven search loop with a macro_rules! state-reset block, byte-order branching for search direction, and inner per-byte match arms checking match and dead states, all inside an outer labeled loop ('outer: loop). This is Rust’s ownership and lifetime machinery layered on top of already-deep control flow — the borrow of regex.cache and regex.dfa across mutable state transitions makes this harder to safely refactor than the complexity number alone suggests. It’s gone 462 days without a change, the longest dormancy in the set, on a file with only 3 total commits and no linked bug fixes, so there’s no historical defect signal pointing at this function specifically — it’s complexity that has simply never needed to be touched. If it does need to change, I’d budget time to extract the reset-and-rescan logic out of the macro into a standalone method first, since debugging a macro_rules!-driven state machine six levels deep is its own tax on anyone unfamiliar with it.
execute — alacritty/src/input/mod.rs
<FunctionCard fn=‘execute’ file=‘alacritty/src/input/mod.rs’ risk=‘12.61’ band=‘critical’ cc=‘173’ nd=‘3’ fo=‘57’ touches=‘0’ />
This is the largest function by raw complexity in the entire dataset — cyclomatic complexity of 173, fan-out of 57 — even though its nesting depth (3) is the shallowest of the top five. The excerpt shows why: a single flat match over the Action enum, one arm per action variant (Esc, Command, Hint, ToggleViMode, ViMotion, and a long run of Vi(...) sub-actions for selection toggling and search navigation), each arm calling out to a different context method. That’s the textbook ‘god function’ shape — not deeply nested, just enormously wide, dispatching to 57 distinct callees. Any new key action or vi-mode command has to be added here, so this function’s blast radius grows with every feature the project adds to its action model. It’s been untouched for 467 days, the file’s history includes one revert across 11 commits, and a bug-fix rate of 27.3%. Given how mechanically uniform the match arms are, this is a strong candidate for splitting into per-category dispatch functions — one for Vi actions, one for Mouse or Search actions, one for everything else — reducing the fan-out per function without changing behavior.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
long_function | 6 |
god_function | 5 |
stale_complex | 4 |
complex_branching | 3 |
exit_heavy | 2 |
deeply_nested | 2 |
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/alacritty/alacritty
cd alacritty
git checkout d692748d3f61253ebe9f5094320120d22f6a046f
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 →