fd's walk and exec subsystems carry the highest structural risk — 5 functions

In sharkdp/fd, the poll function in src/walk.rs tops the risk rankings with CC 25 and max nesting depth 5, flanked by two exec functions sharing identical complexity scores — all sitting in the debt quadrant.

Stephen Collins ·
oss rust refactoring code-health

Antipatterns Detected

exit_heavy3complex_branching2long_function1deeply_nested1

Key Points

What is an exit-heavy function and why does it matter in fd?

An exit-heavy function has multiple return or early-exit points scattered through its body, meaning execution can terminate in several different ways depending on branching conditions. In fd, three of the top five flagged functions — including poll in the walk subsystem and fmt in the formatting layer — show this pattern. Each exit point is a potential gap in test coverage, because a test suite that only exercises the happy path will miss the behavior at every other return site.

How do I reduce cyclomatic complexity in Rust?

The most direct approach is to extract branches into smaller, named functions or use pattern matching with exhaustive match arms to make each case explicit and independently testable. In Rust specifically, replacing nested if-else chains with combinators like map, and_then, or unwrap_or_else on Result and Option types can dramatically reduce the number of counted paths without changing behavior.

Is fd actively maintained?

The structural analysis at commit 2250bb0 shows all five top-ranked functions sitting in the debt quadrant — none of them are currently in the fire quadrant that would indicate high recent commit activity intersecting with complexity. This suggests the core traversal and execution code is relatively stable at this snapshot, though its accumulated structural complexity means future changes will require careful handling.

How do I reproduce this analysis?

Run the Hotspots CLI against the sharkdp/fd repository at commit 2250bb0 to reproduce the exact scores and rankings shown here.

What does activity-weighted risk mean?

Complexity × recent commit frequency — functions that are hard to understand AND actively changing are the highest priority for refactoring.

Across fd’s 274 analyzed functions, 11 reach critical band — and the top five are all in the debt quadrant, meaning they carry substantial structural complexity that hasn’t been recently touched but represents high blast radius the moment they are. The heaviest single function, poll in src/walk.rs, combines a cyclomatic complexity of 25 with a max nesting depth of 5 and patterns flagged as complex_branching, deeply_nested, and exit_heavy — making it the most expensive function to reason about or modify safely. fd is a fast command-line file finder written in Rust; at this scale, structural debt in core traversal and execution paths is the primary risk to long-term maintainability.

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

FunctionFileRiskCCNDFO
pollsrc/walk.rs14.32554
execute_commandssrc/exec/command.rs9.81334
execute_batchsrc/exec/mod.rs9.61343
runsrc/main.rs9.5615
fmtsrc/fmt/mod.rs9.51411

Hotspot Analysis

poll — src/walk.rs

poll in src/walk.rs almost certainly drives fd’s core directory traversal loop — likely implementing an async or iterator polling interface over the filesystem walk. Its cyclomatic complexity of 25 means there are at least 25 independent execution paths to reason about and test, while a max nesting depth of 5 makes those paths genuinely hard to follow visually. The exit_heavy and deeply_nested patterns compound this: multiple exit points scattered across deep nesting means each path must be traced individually to understand termination behavior. With a risk score of 14.3 and a debt quadrant classification, this function has not been recently active — but its structural weight means any future change here carries the highest blast radius in the codebase.

Recommendation: Before the next feature touches src/walk.rs, add characterization tests that document the existing exit paths — this will catch regressions when CC 25 branches interact unexpectedly. Then consider decomposing poll into smaller, named sub-functions that each handle one phase of the traversal decision, reducing both nesting depth and the number of exit points per logical unit.

execute_commands — src/exec/command.rs

execute_commands in src/exec/command.rs is likely responsible for spawning and managing one or more shell commands against matched file paths — a core piece of fd’s —exec behavior. Its cyclomatic complexity of 13 indicates meaningful branching logic, probably handling argument substitution, error conditions, and process lifecycle variations. At max nesting depth of 3 it is more tractable than poll, but the debt quadrant classification signals this structural complexity has accumulated without recent revision, making it overdue for refactoring before the next round of exec-related work.

Recommendation: Map the 13 cyclomatic paths explicitly in a test suite or documentation comment before modifying this function; given fan-out of 4, verify which callees are affected by any branching change to avoid silent regressions in command dispatch.

execute_batch — src/exec/mod.rs

execute_batch in src/exec/mod.rs likely handles fd’s —exec-batch mode, collecting matched paths and invoking a command with all of them at once rather than one at a time. Sharing an identical cyclomatic complexity of 13 with execute_commands, and flagged for complex_branching with a max nesting depth of 4, it sits one nesting level deeper and is also in the debt quadrant with a risk score of 9.6. The structural similarity between execute_batch and execute_commands suggests the two functions may share logic that has diverged over time — a common source of subtle inconsistency between single and batch execution modes.

Recommendation: Audit execute_batch and execute_commands side-by-side for duplicated branching logic; shared paths between the two functions are strong candidates for extraction into a common helper, which would reduce both CC scores and make future changes to exec behavior apply consistently across both modes.

run — src/main.rs

run in src/main.rs is fd’s CLI entry point — responsible for parsing arguments, building configuration, and dispatching the core file-finding operation. Its cyclomatic complexity of 6 is modest and its max nesting depth of 1 means branching is shallow, but a fan-out of 5 reflects the breadth of coordination it performs. The long_function pattern flagged here is consistent with a function that sequences many concerns: argument validation, config construction, thread setup, and result dispatch are each small individually but accumulate into a single long body. Despite sitting in the debt quadrant with no recent churn, the entry point touches enough of the system that an unintended change here would have wide reach.

Recommendation: Consider splitting run into two phases — argument parsing and validation, then execution dispatch. A dedicated build_config helper would make the setup logic independently testable and reduce the function’s length without changing its coordination role.

fmt — src/fmt/mod.rs

fmt in src/fmt/mod.rs handles output formatting for fd’s matched paths, deciding how results are presented based on user configuration. With a cyclomatic complexity of 14 and a max nesting depth of 1, this function uses broad flat branching rather than deep nesting — likely a sequence of conditional checks or match arms each handling a different output mode. Its fan-out of 1 confirms it is largely self-contained. The exit_heavy pattern is the primary concern: 14 independent paths with multiple potential exit points means test coverage must explicitly exercise each output mode to avoid silent gaps. A change to one output variant can affect other paths in non-obvious ways when exit points are interleaved.

Recommendation: Audit the existing test suite for fmt against each of the 14 cyclomatic paths — missing cases are likely edge-mode formatting options such as color output, null-separator mode, or absolute-path formatting. Unifying exit points through a single return value built by exhaustive match arms would reduce the mental overhead of tracing termination behavior and make each case independently verifiable.

Patterns Found

Antipatterns detected across the top functions in this snapshot:

PatternOccurrences
exit_heavy3
complex_branching2
long_function1
deeply_nested1

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.

Key Takeaways

  • poll in src/walk.rs has a cyclomatic complexity of 25 and max nesting depth of 5 — add characterization tests covering its exit paths before any future traversal work to avoid silent regressions.
  • execute_commands and execute_batch share identical CC scores of 13 and both sit in the debt quadrant — audit them together for diverged shared logic before the next exec feature lands.
  • run in src/main.rs flags the long_function pattern with fan-out of 5 — extracting the configuration-building phase into a dedicated helper would make each setup concern independently testable.
  • fmt in src/fmt/mod.rs carries CC 14 in a flat, exit-heavy structure — audit test coverage for each of its output formatting paths before adding new display modes.
  • All five top-ranked functions are in the debt quadrant, meaning the risk here is not active churn but accumulated structural weight — the priority is establishing test coverage now, while these functions are stable, rather than after the next change destabilizes them.

Reproduce This Analysis

git clone https://github.com/sharkdp/fd
cd fd
git checkout 2250bb0ad13bf2ab93f4d56977811e648b3f62dc
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.

Hotspots highlights structural and activity risk — not “bad code.” Findings are a prioritization aid, not a bug predictor. Editorial policy →

Run this on your own codebase

Hotspots runs locally in under a minute — no account, no data leaves your machine.

macOS
$ brew install Stephen-Collins-tech/tap/hotspots
Linux / cargo
$ cargo install hotspots-cli
Run in any repo
$ hotspots analyze .
★ Star on GitHub

Related Analyses