At commit f4cebd8, the two functions that demand immediate attention in remotion-dev/remotion are both structurally complex and were touched within the last 24 hours. get_frame in packages/compositor/rust/opened_stream.rs carries an activity-weighted risk score of 19.22, driven by a cyclomatic complexity of 53 and two commits in the last 30 days, the most recent just one day ago. The anonymous dispatcher in packages/media-parser/src/emit-available-info.ts scores 18.88 with a cyclomatic complexity of 118 — the highest single value in this dataset — and shares the same recent-touch cadence. I would start with get_frame because it sits at the boundary between Rust and the rest of the stack, but the anonymous function in media-parser is structurally the more extreme case.
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 |
|---|---|---|---|---|---|
get_frame | packages/compositor/rust/opened_stream.rs | 19.2 | 53 | 7 | 20 |
<anonymous> | packages/media-parser/src/emit-available-info.ts | 18.9 | 118 | 6 | 26 |
useGenerationApi | packages/template-prompt-to-motion-graphics/src/hooks/useGenerationApi.ts | 18.1 | 12 | 9 | 21 |
open_stream | packages/compositor/rust/opened_stream.rs | 17.0 | 21 | 6 | 17 |
POST | packages/template-prompt-to-motion-graphics/src/app/api/generate/route.ts | 16.9 | 42 | 4 | 31 |
Large Repo Analysis
remotion 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.
get_frame — packages/compositor/rust/opened_stream.rs
get_frame retrieves a decoded video frame from an open media stream inside remotion’s Rust compositor. With a cyclomatic complexity of 53, it contains at least 53 independent execution paths — each one a required test case and a surface for a conditional to drift out of sync with the others. A nesting depth of 7 means the deepest branches are seven control structures deep, well past the point where a reader can hold the full state space in their head. Fan-out of 20 makes this a structural centre of gravity: 20 distinct functions are called from here, so a change to the interface or semantics of any of them may require a coordinated edit inside get_frame as well.
What makes this a live concern rather than accumulated debt is the activity signal: two commits in the last 30 days, with the most recent landing just one day ago. That is not a dormant function with theoretical blast-radius risk — it is a function being actively changed right now while carrying structural complexity that makes each edit harder to reason about. My recommendation is to begin extracting the seek/decode decision logic and the error-path branches into smaller, named functions. Even splitting the happy path from the error-exit paths would reduce CC materially and make each remaining branch independently testable.
<anonymous> — packages/media-parser/src/emit-available-info.ts
An anonymous function at the top level of emit-available-info.ts is the structural outlier in this entire dataset. Its cyclomatic complexity of 118 is more than double that of get_frame, placing it firmly in the extreme range. Based on the file name, this function is likely a dispatcher or callback that fires whenever new parsed media metadata becomes available — checking which fields have been populated and emitting or routing them accordingly. A design like that accumulates branches naturally: one conditional per supported metadata field or track type, compounding over time as the media-parser gains support for new container formats and codec properties.
With a fan-out of 26, it calls into 26 distinct functions, making it the broadest coupling point in the top hotspots. Six levels of nesting at ND 6, combined with CC 118 and 26 callees, means that reasoning about a single change requires mentally tracing through a large and branchy call graph simultaneously. Like get_frame, it received two commits in the last 30 days and was last modified one day ago. I would prioritize introducing a discriminated-union type or a data-driven dispatch table to replace the conditional chain: that technique commonly reduces CC by an order of magnitude in functions that branch primarily on type or field presence, and it makes each emission path individually testable. Because the function is anonymous, naming it explicitly is the zero-cost first step — it makes stack traces legible and makes coverage reports meaningful.
useGenerationApi — packages/template-prompt-to-motion-graphics/src/hooks/useGenerationApi.ts
useGenerationApi is a React hook in the AI video generation template. Its CC of 12 is modest, but ND 9 is the deepest nesting value in the top five — nine levels of control structure inside a hook that likely manages async API state: loading, error, retry, cancellation, and response parsing all layered on top of each other. Fan-out into 21 callees suggests it’s also coordinating several concerns that could be isolated.
Recommendation: The nesting is the primary issue here, not the branching count. A series of guard clauses at the top — return early on error, on loading, on missing prerequisites — would flatten the structure without restructuring the logic. If the hook is managing multiple independent async concerns, useReducer is often a cleaner model than nested useState chains.
open_stream — packages/compositor/rust/opened_stream.rs
open_stream lives in the same Rust file as get_frame and is almost certainly its counterpart: the function that opens and initialises the media stream before frames can be retrieved. At CC 21 and ND 6, its structural profile is meaningfully simpler than get_frame, but it shares the same file and the same activity window. Two functions in one file — both in the top five by activity-weighted risk — suggests opened_stream.rs as a whole is the current focal point of compositor development. Refactoring get_frame first is the right priority, but open_stream will likely need the same extract-and-name treatment as the file evolves.
POST — packages/template-prompt-to-motion-graphics/src/app/api/generate/route.ts
The POST handler for the AI generation API route carries CC 42 and fan-out into 31 distinct callees — a wide surface for a single HTTP handler. ND 4 is shallow, so the complexity comes from branching breadth rather than nesting depth: validation branches, provider selection, error handling, and response shaping are likely all inline in one function. At risk score 16.9, this sits at the edge of the top five rather than the centre of it, but a handler that calls 31 functions is doing too much in one place. Extracting validation and the provider-dispatch logic into named helpers would reduce both CC and FO and make the handler’s linear flow legible.
Key Takeaways
- Start with
get_frame(packages/compositor/rust/opened_stream.rs): activity-weighted risk 19.22, CC 53, touched within the last day. Extract the seek/decode decision and error-exit branches into named helper functions before the next change lands. - Name and decompose the anonymous dispatcher in
packages/media-parser/src/emit-available-info.ts: CC 118 with a fan-out of 26 makes this the single highest structural complexity point in the dataset. Giving it an explicit name costs nothing; replacing its conditional chain with a data-driven dispatch table is the refactoring that will actually bring CC down. opened_stream.rsas a whole is the hottest file: bothget_frameandopen_streamrank in the top five from a single Rust file — treat the file, not just the functions, as the refactoring unit.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
complex_branching | 5 |
exit_heavy | 5 |
god_function | 5 |
long_function | 5 |
deeply_nested | 4 |
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.
Reproduce This Analysis
git clone https://github.com/remotion-dev/remotion
cd remotion
git checkout f4cebd8179b4fa66cb7476bca30bd4a206306d89
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 →