Across expo/expo’s 15,835 analyzed functions, 835 are rated critical — and the top of that list is dominated by expo-router’s navigation layer. The highest-ranked hotspot, useNavigationBuilder, sits in the ‘fire’ quadrant with an activity-weighted risk of 19.8 and cyclomatic complexity of 59, meaning it is both structurally hard to reason about and actively being changed right now, a combination that creates live regression risk on every commit. The second-ranked function, BaseNavigationContainer, compounds that risk with a max nesting depth of 10 — the deepest in the top five — while also carrying an activity-weighted risk of 19.4.
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 |
|---|---|---|---|---|---|
useNavigationBuilder | packages/expo-router/src/react-navigation/core/useNavigationBuilder.tsx | 19.8 | 59 | 5 | 66 |
BaseNavigationContainer | packages/expo-router/src/react-navigation/core/BaseNavigationContainer.tsx | 19.4 | 24 | 10 | 37 |
interactiveDashboard | tools/src/commands/CIInspectCommand.ts | 19.1 | 22 | 12 | 25 |
getDirectoryTree | packages/expo-router/src/getRoutesCore.ts | 19.0 | 58 | 6 | 30 |
parsePlistXML | packages/@expo/plist/src/parse.ts | 18.9 | 38 | 12 | 8 |
Large Repo Analysis
expo 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.
Codemod / Tooling Files in Results
The function interactiveDashboard in tools/src/commands/CIInspectCommand.ts and parsePlistXML in packages/@expo/plist/src/parse.ts appear in the top five but fall outside expo-router’s application runtime. interactiveDashboard is an internal tooling command — likely a CLI dashboard for CI inspection — rather than product code, so its structural complexity has limited impact on end users. parsePlistXML is a plist file parser in the @expo/plist utility package; its CC of 38 and ND of 12 reflect the inherent branching required to handle the plist XML format’s many node types, but it is a lower-level utility with narrow scope. To exclude internal tooling from future analyses, add { "exclude": ["tools/"] } to your .hotspotsrc.json; to exclude vendored or bundled utility packages, add "packages/@expo/plist/" to the same list.
Hotspot Analysis
useNavigationBuilder — packages/expo-router/src/react-navigation/core/useNavigationBuilder.tsx
Based on its name and location in expo-router’s react-navigation core, useNavigationBuilder is almost certainly the React hook responsible for constructing and wiring up a navigator — connecting route state, child descriptors, navigation actions, and context for a given navigator type. Its cyclomatic complexity of 59 means there are at minimum 59 independent execution paths through this hook, each one a required test case and a potential bug surface. The fan-out of 66 — the highest in the entire top five — classifies it as a hub function: it calls 66 distinct functions, meaning a subtle change here can produce ripple effects across virtually every part of the navigation stack. With an activity-weighted risk of 19.8 in the ‘fire’ quadrant, this is not a dormant debt item; it is actively changing right now, which makes each of those 59 paths a live regression risk.
Recommendation: Before the next feature change, add characterization tests that cover the most common navigator configurations to lock in current behavior. Then begin extract-method refactoring to decompose the hook’s responsibilities — state wiring, action dispatch, and child descriptor construction are likely separable concerns that could each become independently testable units.
BaseNavigationContainer — packages/expo-router/src/react-navigation/core/BaseNavigationContainer.tsx
As the foundational container component for all navigation in expo-router, BaseNavigationContainer is likely responsible for providing navigation context, managing top-level navigation state, and coordinating ref forwarding and lifecycle events across the navigator tree. Its max nesting depth of 10 is a strong refactoring signal — ND 8+ is where control flow becomes genuinely difficult to trace, and at ND 10 this function exceeds that threshold significantly. Despite a cyclomatic complexity of 24 being lower than useNavigationBuilder, the combination of ND 10, fan-out of 37, and an activity-weighted risk of 19.4 in the ‘fire’ quadrant makes it the second most urgent hotspot in the repo — it is both structurally opaque and actively changing.
Recommendation: Target the deepest nesting branches first: identify the conditional blocks responsible for the ND 10 reading and extract them into named helper functions with single, clear responsibilities. This will reduce visual nesting immediately and make the control flow auditable during active development.
getDirectoryTree — packages/expo-router/src/getRoutesCore.ts
Located in expo-router’s route-building core, getDirectoryTree is likely responsible for traversing the file system structure that expo-router uses to derive its route configuration — converting directory and file names into a navigable route tree. A cyclomatic complexity of 58 places it just below useNavigationBuilder as one of the most branch-heavy functions in the codebase, and its fan-out of 30 indicates it delegates to a broad set of utilities during that traversal. With an activity-weighted risk of 19.0 in the ‘fire’ quadrant, it is being actively changed — likely as expo-router’s file-based routing features evolve — meaning each of its 58 execution paths is a potential source of regression on new commits.
Recommendation: Map the function’s major branching axes — likely file type classification, special route segment handling, and recursive descent — and extract each into a focused, independently testable helper. The exit-heavy pattern flagged here suggests many early-return guards; reviewing whether those guards can be unified or reordered will reduce the effective path count and lower the CC score measurably.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
complex_branching | 5 |
deeply_nested | 5 |
exit_heavy | 5 |
long_function | 5 |
god_function | 4 |
hub_function | 1 |
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
useNavigationBuilderhas a fan-out of 66 — the highest in the top five — meaning it is a hub that couples to a large fraction of the navigation stack; any change there should be preceded by a targeted test suite covering all major navigator types.BaseNavigationContainer’s max nesting depth of 10 exceeds the strong-refactoring-signal threshold of ND 8 and is actively changing (activity-weighted risk 19.4); flatten the deepest conditional blocks before adding new navigation lifecycle features.getDirectoryTreeingetRoutesCore.tshas CC 58 and is in the ‘fire’ quadrant — as expo-router’s file-based routing continues to expand, this function will accumulate more paths; decomposing it now reduces the risk surface before the next routing feature lands.
Reproduce This Analysis
git clone https://github.com/expo/expo
cd expo
git checkout 4b854eb668377ff198a580b497b31dbb806111f6
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.
Hotspots highlights structural and activity risk — not “bad code.” Findings are a prioritization aid, not a bug predictor. Editorial policy →