At commit 69026c9, sniffnet’s 767-function Rust codebase carries 9 critical-band functions, and 4 of the top 5 hotspots fall in the fire quadrant — structurally complex code touched within the last 30 days. That combination makes this a ‘why now’ question rather than a backlog item: parse_packets in src/networking/parse_packets.rs leads with an activity-weighted risk score of 14.8, a cyclomatic complexity of 28, and a fan-out of 31, while update in src/gui/sniffer.rs reaches a cyclomatic complexity of 161 — the single most branchy function in the project. I’d start a review sprint with these two and work down the list; the structural debt here is actively intersecting with live development.
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 |
|---|---|---|---|---|---|
parse_packets | src/networking/parse_packets.rs | 14.8 | 28 | 5 | 31 |
update | src/gui/sniffer.rs | 14.7 | 161 | 1 | 58 |
is_local_connection | src/networking/manage_packets.rs | 10.8 | 15 | 6 | 4 |
get_domain_from_r_dns | src/utils/formatted_strings.rs | 10.4 | 11 | 4 | 5 |
return_key_pressed | src/gui/sniffer.rs | 10.3 | 12 | 4 | 3 |
Large Repo Analysis
sniffnet is a large repository. To stay within memory constraints, this analysis used hybrid touch mode: structural complexity — CC, ND, FO — is measured precisely for every function. Git activity is tracked at the function level (via git log -L) only for files with 5 or more commits in the last 30 days; other files use a file-level approximation. Rankings therefore surface functions that are both structurally complex and in the most actively-changing parts of the codebase. Dormant code with high structural complexity will rank lower than it would under a full per-function analysis — to surface it, run hotspots analyze . --per-function-touches on a machine with sufficient memory.
Repository overview
Before getting into individual functions, it helps to see where the risk is concentrated across the codebase.
767 functions analyzed
The dominant quadrant by count is ok (482 functions), which is healthy — most of the codebase is stable and low-complexity. But 39 fire-quadrant functions and 113 debt-quadrant functions represent a meaningful surface of structural risk. The 9 critical-band functions are what I want to focus on here.
High cyclomatic complexity — many independent execution paths, each a potential bug surface and required test case.Deeply Nested×2Deeply Nested
Control structures nested 4+ levels deep, making it hard to reason about the full execution state at inner branches.God Function×2God Function
Calls an unusually large number of distinct functions (high fan-out), making it the structural centre of gravity for a subsystem.Long Function×2Long Function
Function body is too long to review in a single pass; likely contains multiple distinct responsibilities.Exit Heavy×2Exit Heavy
Multiple return or throw paths dispersed through the body — each exit needs separate test coverage.
The dominant antipattern across the top hotspots is complex branching, appearing in four of the five functions. Two of those also qualify as god functions — single functions bearing responsibility for a disproportionate share of the system’s logic. Those two patterns together are the main reason the blast radius of a change to any of these functions is so wide.
parse_packets — parse_packets.rs
The doc comment says it all: this function enters a loop waiting for network packets, and the source excerpt confirms it. It spawns a reverse-DNS lookup thread pool, opens a packet_stream thread, then enters a loop that handles freeze signals, packet timeouts, channel closure checks, and live-capture tick logic — all before the match packet_res arm that processes the actual packet result. That structure produces a cyclomatic complexity of 28, a nesting depth of 5, and a fan-out of 31. The #[allow(clippy::too_many_lines, clippy::too_many_arguments)] suppression at the top is the project itself acknowledging the size problem.
A fan-out of 31 means this single function directly calls 31 distinct other functions. In Rust, where ownership and lifetime constraints already add reasoning overhead, that coupling is expensive: a change to any one of those callees may require auditing parse_packets to verify that borrow lifetimes, channel send/receive ordering, and thread spawn logic are still correct. The file-level external signals show 2 total commits, both tagged as bug fixes — every historical change to this file has been a correction, not a feature. That’s a narrow but directional signal worth noting when prioritizing review.
The concrete first step I’d recommend is decomposing the thread-spawning and channel-setup prologue into a dedicated initialization function, then extracting the freeze-handling logic into its own method. That alone would reduce the effective CC of the main loop body and make the packet-processing path easier to read and test in isolation.
update — sniffer.rs
A cyclomatic complexity of 161 is not a moderate outlier — it is extreme. The source excerpt reveals why: update is the top-level message dispatcher for the Iced GUI runtime, matching on every possible Message variant the application can produce. Each match arm routes to a specific method on self, making this function’s CC essentially the count of distinct UI messages the application supports. The nesting depth is only 1 (the match is flat), but the fan-out of 58 tells the real story: 58 distinct functions are called from this one site.
The exit_heavy and god_function patterns both apply here. Multiple match arms return a Task<Message> directly while others fall through to Task::none(), creating a test-coverage burden — testing any single message-handling path requires instantiating the full Sniffer state.
File-level signals are worth noting: 2 bug-fix commits, a bug-fix fraction of 1.0, and a PR review comment density of 3.0. Every commit to this file has been a bug fix, and reviewers have left comments. That doesn’t mean this function is broken, but it does mean the file has historically required corrective attention, and the structural complexity is a plausible contributing factor.
The practical path forward is not to rewrite update — the flat dispatch pattern is idiomatic for Iced and probably intentional — but to audit the match arms that return Task<Message> directly versus those that delegate to sub-methods. Any arm containing non-trivial inline logic rather than a single delegation call is a candidate for extraction. Long-term, grouping related messages (settings, navigation, capture control) into sub-enums with their own sub-dispatchers would reduce the surface without breaking the Iced contract.
is_local_connection — manage_packets.rs
This function has been touched once in the last 30 days and was last modified 17 days ago — it is actively in the fire quadrant. The source excerpt shows the structural reason for its CC of 15 and nesting depth of 6: it iterates over all interface addresses, then branches on IPv4 vs IPv6, then checks for link-local status, then falls into a subnet mask comparison via octet-level bitwise operations — all nested inside each other.
A nesting depth of 6 in Rust is a meaningful refactoring signal. Each additional level is a layer of conditional state a reader must hold in their head simultaneously, and the bitwise subnet-mask logic sits at the innermost level — precisely where you want the least cognitive overhead. The complex_branching and deeply_nested patterns are both present here.
Three authors have touched this file in the last 90 days, and one-third of the commits are bug fixes. That’s a broader ownership spread than the other hotspots, which adds risk: multiple authors navigating six levels of nesting increases the chance of a subtle logic error at the deeper branches.
My recommendation: extract the IPv4 and IPv6 subnet-match checks into named helper functions — something like is_same_subnet_v4 and is_same_subnet_v6. That would bring the nesting depth of is_local_connection itself down to roughly 3, make each subnet-matching branch independently testable, and make the top-level intent of the function readable at a glance.
get_domain_from_r_dns — formatted_strings.rs
This is the most active function in the top 5 by recent commit frequency: 2 touches in the last 30 days, last changed 15 days ago. The function takes a reverse-DNS string and extracts the meaningful domain suffix — stripping hostnames, numeric prefixes, and handling the edge case where the rDNS result is still an IP address. The source excerpt shows the branching: an early guard for IP-address or empty input, then a split on ., then length checks on the last and second-to-last parts to determine whether to return a two-part or three-part domain, with a fallback using checked_sub to avoid underflow.
With a CC of 11 and nesting depth of 4, the function isn’t structurally severe on either metric in isolation — but the combination, plus the active churn, is what puts it in the fire quadrant. The complex_branching pattern reflects the multiple length-based heuristics for determining TLD depth, and those heuristics are exactly the kind of logic that accumulates edge-case fixes over time. The file has seen 3 total commits with a bug-fix fraction of one-third, and 2 authors have contributed in the last 90 days.
The actionable step here is property-based testing. The function’s branching logic covers at least five distinct cases (IP input, empty input, two-part domain, two-part with long TLD, three-part domain), and each is a required test case implied by the CC of 11. Given that this function is being touched actively right now, a property-test suite over a range of rDNS inputs would catch regressions as the heuristics evolve.
return_key_pressed — sniffer.rs
This function sits in src/gui/sniffer.rs alongside update and shares the same file-level external signals: 2 bug-fix commits, a bug-fix fraction of 1.0, and a PR review comment density of 3.0. It hasn’t been touched in 30 days, but the fire quadrant designation reflects the file’s broader recent activity.
The source excerpt shows what return_key_pressed actually does: it checks a specific combination of application state — whether running_page, settings_page, and modal are all None — and dispatches to start(), reset(), quit(), or clear_all_notifications() depending on which modal is active. With a CC of 12 and nesting depth of 4, the function encodes a moderately complex state machine for a single key press. The complex_branching pattern here is the chain of if / else if guards on modal state.
The low fan-out of 3 means the coupling is narrow — this function calls only three distinct methods — but because those methods include start(), reset(), and quit(), the consequences of a mis-classified modal state are significant. With all historical commits to this file being bug fixes and reviewers leaving comments on PRs, I’d treat this as a function that warrants an explicit state-transition test matrix: for each combination of running_page, settings_page, and modal, assert what return_key_pressed dispatches. That’s a finite, enumerable set given the source, and it would catch the kind of edge-case regression that the PR comment history suggests has occurred before.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
complex_branching | 4 |
deeply_nested | 2 |
god_function | 2 |
long_function | 2 |
exit_heavy | 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.
Reproduce This Analysis
git clone https://github.com/GyulyVGC/sniffnet
cd sniffnet
git checkout 69026c90fd9d18bbb34ec6c643c8655b2aa1808a
hotspots analyze . --mode snapshot --explain-patterns --force --hybrid-touches 5
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 →