Every one of ktor’s top five structurally risky functions lives in the same file cluster: the Rust side of ktor-client-webrtc-rs, none touched in the last 30 days, all last modified 63 days ago. That’s not a live regression story — it’s archaeology. Ktor is JetBrains’ Kotlin asynchronous framework for building connected applications; this scan covers 98 functions across the codebase, with 8 landing in the high-risk band and all 8 classified as structural debt rather than active churn. I’d start with read_all in track.rs, not because someone is actively breaking it, but because whoever touches it next inherits cyclomatic complexity of 10 wrapped in a nesting depth of 3, sitting dormant for two months.
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 |
|---|---|---|---|---|---|
read_all | ktor-client/ktor-client-webrtc/ktor-client-webrtc-rs/common/rust/media/track.rs | 8.7 | 10 | 3 | — |
make_peer_connection | ktor-client/ktor-client-webrtc/ktor-client-webrtc-rs/common/rust/lib.rs | 7.8 | 4 | 1 | 3 |
read_rtp | ktor-client/ktor-client-webrtc/ktor-client-webrtc-rs/common/rust/media/track.rs | 7.7 | 6 | 1 | 1 |
remove_track | ktor-client/ktor-client-webrtc/ktor-client-webrtc-rs/common/rust/connection.rs | 6.7 | 7 | 3 | 1 |
from_native | ktor-client/ktor-client-webrtc/ktor-client-webrtc-rs/common/rust/rtc.rs | 6.3 | 18 | 1 | 1 |
<BandChart fire=‘0’ debt=‘8’ watch=‘0’ ok=‘90’ />
The shape of this data is unusual: zero functions in the ‘fire’ quadrant, zero in ‘watch’, and every flagged function sitting in ‘debt’. Nothing in the top ranks is being actively iterated on right now. That’s a meaningfully different problem than a codebase full of churn-driven risk — the danger here is dormant complexity that will surface the next time someone has to touch WebRTC internals, not a currently destabilizing change in flight.
<PatternCloud patterns=‘exit_heavy:2’ />
All five top hotspots live in ktor-client-webrtc-rs, the Rust implementation backing ktor’s WebRTC client module. Every one was last modified 63 days ago, has exactly one commit on record, and has a single author in the last 90 days — consistent with a single initial-implementation commit that hasn’t been revisited since. None carry bug-linked history, reverts, or review-comment density above zero, so I’m not reading these as defective code. I’m reading them as unreviewed structural debt sitting in a subsystem (async media/ICE handling) that’s inherently hard to simplify further.
read_all — track.rs
<FunctionCard fn=‘read_all’ file=‘ktor-client/ktor-client-webrtc/ktor-client-webrtc-rs/common/rust/media/track.rs’ risk=‘8.66’ band=‘high’ cc=‘10’ nd=‘3’ fo=‘0’ touches=‘0’ />
This is the top-ranked function in the whole scan, with an activity-weighted risk score of 8.66 built entirely from structural complexity — zero touches in the last 30 days, 63 days since its last change. The excerpt shows why: an async loop pulling RTP packets from a remote media track, guarded by a try-lock to prevent parallel reads, with a match arm that either forwards a packet to a sink or exits the loop entirely on error. Cyclomatic complexity of 10 with nesting depth of 3 is exactly the kind of async control flow that Kotlin and Rust fan-out metrics tend to undercount — the real branching is in the loop/match/if let stack, not in the calls it makes (fan-out is 0). Because this is dormant, not evolving, my recommendation is a pre-emptive review before it’s needed under pressure: pull the lock-acquisition and packet-forwarding logic into named helper functions so the next person modifying track reading isn’t reasoning about all three nesting levels at once.
make_peer_connection — lib.rs
<FunctionCard fn=‘make_peer_connection’ file=‘ktor-client/ktor-client-webrtc/ktor-client-webrtc-rs/common/rust/lib.rs’ risk=‘7.82’ band=‘high’ cc=‘4’ nd=‘1’ fo=‘3’ touches=‘0’ />
Second on the list at an activity-weighted risk score of 7.82, this is the setup path for a new peer connection — building the ICE configuration, registering codecs, wiring interceptors, and conditionally attaching default audio and video transceivers. The cyclomatic complexity of 4 looks modest, but the function is flagged as exit-heavy: nearly every step (register_default_codecs, building the registry, new_peer_connection, both transceiver calls) returns early through map_err on failure. That’s five-plus distinct exit points to account for in tests, each representing a different partially-constructed state if setup fails midway. It’s been untouched for 63 days with a single commit behind it, so this is a candidate for a focused test pass — specifically, coverage for what happens to already-created resources (the media engine, the registry) if a later step in the chain fails.
read_rtp — track.rs
<FunctionCard fn=‘read_rtp’ file=‘ktor-client/ktor-client-webrtc/ktor-client-webrtc-rs/common/rust/media/track.rs’ risk=‘7.71’ band=‘high’ cc=‘6’ nd=‘1’ fo=‘1’ touches=‘0’ />
This is the single-packet sibling of read_all in the same file, sharing the same try-lock-guarded read pattern and the same exit-heavy flag. Cyclomatic complexity of 6 is lower than its loop-based counterpart, but the two functions are structurally coupled — read_all literally calls read_rtp in a loop per the excerpt. Any refactor of the locking or error-handling logic in one has to be mirrored in the other, or the two will drift out of sync on how they treat a disabled track or a missing sink. Given both are dormant at 63 days since last change, I’d treat them as a single refactoring unit rather than two separate tickets.
remove_track — connection.rs
<FunctionCard fn=‘remove_track’ file=‘ktor-client/ktor-client-webrtc/ktor-client-webrtc-rs/common/rust/connection.rs’ risk=‘6.7’ band=‘high’ cc=‘7’ nd=‘3’ fo=‘1’ touches=‘0’ />
This one scans the connection’s senders looking for a track by ID, then delegates removal to the underlying peer connection. Cyclomatic complexity of 7 with nesting depth of 3 comes from the loop-plus-conditional structure needed to find the matching sender before acting. What stands out in the surrounding excerpt is the sheer number of observer callbacks registered nearby (register_observer wires up state-change, ICE candidate, negotiation, and data-channel handlers all in one place) — a strong signal of broad coupling across the peer-connection lifecycle even though remove_track itself only calls out to one function. At 63 days idle, this is lower urgency than the two track-reading functions above it, but worth a look the next time anyone touches sender/track management in this file.
from_native — rtc.rs
<FunctionCard fn=‘from_native’ file=‘ktor-client/ktor-client-webrtc/ktor-client-webrtc-rs/common/rust/rtc.rs’ risk=‘6.35’ band=‘high’ cc=‘18’ nd=‘1’ fo=‘1’ touches=‘0’ />
The complexity profile here is the odd one out: cyclomatic complexity of 18 against a nesting depth of only 1. That combination almost always means a flat match or switch statement with many arms rather than deep conditional nesting, and the excerpt confirms it — this is an enum-to-enum state mapper (ICE connection state, in the surrounding code also gathering and signaling state) with one arm per variant, including an unreachable!() case for the unspecified state. Flat match statements like this are usually low-risk despite the raw complexity number, because each arm is independent and there’s no interaction between branches. I wouldn’t prioritize restructuring this one; if anything, it’s a case where the complexity number overstates the actual review burden. The real value of catching it here is confirming that ktor’s unreachable!() usage in state mappers is worth grep-ing for across the file, since a future protocol variant addition could silently violate that assumption.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
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: exit_heavy.
Reproduce This Analysis
git clone https://github.com/ktorio/ktor
cd ktor
git checkout 9ff002937e2f992ce7429f486c5459b7c8710a32
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 →