career-ops is a JavaScript pipeline tool with 187 analyzed functions, 33 of which rank as high-priority refactoring candidates. The top hotspot, analyze in analyze-patterns.mjs, carries a cyclomatic complexity of 42, a fan-out of 54, and enough recent change activity to make it more than a cleanup backlog item. Notably, every function in the codebase has recent activity behind it, which means the structural risk in this repo is being exercised by ongoing development rather than sitting dormant.
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 |
|---|---|---|---|---|---|
analyze | analyze-patterns.mjs | 16.6 | 42 | 4 | 54 |
handleKey | dashboard/internal/ui/screens/pipeline.go | 13.5 | 21 | 3 | 8 |
Update | dashboard/main.go | 13.1 | 18 | 2 | 18 |
parseTsvContent | merge-tracker.mjs | 12.9 | 21 | 4 | 11 |
apply | update-system.mjs | 12.7 | 34 | 5 | 18 |
Hotspot Analysis
analyze — analyze-patterns.mjs
Based on its name and file path, analyze is the core pattern-analysis entry point for the career-ops pipeline — the function responsible for driving whatever structural or behavioral analysis the tool performs. Its cyclomatic complexity of 42 means there are at least 42 independent execution paths to reason about and test; its nesting depth of 4 shows meaningful layered branching; and its fan-out of 54 means it directly invokes 54 distinct functions, making it the broadest coupling point in the codebase by a wide margin. Because it is still changing, any update to this god function can affect a wide swath of downstream behavior with little structural guidance on where the failure originated.
Recommendation: Apply extract-method refactoring to decompose analyze into focused sub-functions, each responsible for a single phase of analysis — this alone should cut both CC and fan-out substantially. Before refactoring, add characterization tests that cover the most common execution paths to create a safety net against regressions.
handleKey — dashboard/internal/ui/screens/pipeline.go
From its name and location in the dashboard’s pipeline screen, handleKey almost certainly dispatches keyboard input events within the pipeline UI — a function that branches on every supported key action. Its cyclomatic complexity of 21 reflects the breadth of that dispatch logic, while its nesting depth of 3 and fan-out of 8 suggest a moderate number of helper calls behind those branches. The exit-heavy pattern flag signals multiple return paths, each of which represents a distinct test case that must be covered to prevent silent regressions as new key bindings are added.
Recommendation: Introduce a dispatch table or command pattern to replace the branching key-handling logic, which would reduce CC significantly and make adding new key bindings a data change rather than a structural one. Prioritize this now — with 5 touches in 30 days, the probability of a regression from the next change is high.
Update — dashboard/main.go
In a dashboard entry point, Update is likely the central state-transition function that routes incoming messages and coordinates screen updates. Its cyclomatic complexity of 18 is lower than the top two hotspots but still high enough to make regression testing important, and its fan-out of 18 indicates broad coordination across helpers or view state. The nesting depth of 2 is manageable, so the main risk is breadth rather than deeply layered control flow.
Recommendation: Split the highest-volume message handling paths into focused update helpers and keep Update as a thin router. This should lower CC while preserving the function’s role as the dashboard’s coordination point.
parseTsvContent — merge-tracker.mjs
parseTsvContent appears to turn tab-separated merge-tracking data into structured records. Its cyclomatic complexity of 21 and nesting depth of 4 point to multiple validation and normalization paths inside the parser, while a fan-out of 11 suggests it is also coordinating several helper operations. That combination makes malformed or edge-case input the most likely source of regressions.
Recommendation: Separate tokenization, row validation, and record construction into independently tested helpers. Add fixtures for empty rows, missing columns, escaped delimiters, and invalid values before changing the parser’s control flow.
apply — update-system.mjs
apply is the fifth-ranked hotspot and has the most deeply nested control flow in the table: CC 34, ND 5, and FO 18. That profile is consistent with a long, branching update routine that performs several decisions before delegating to downstream operations. The deeply nested and complex-branching patterns are plausible here because a reader has to track both many paths and several levels of conditional context.
Recommendation: Extract the deepest conditional branches first, especially any validation, planning, or side-effect steps that can become named helpers. Once the branches are isolated, add focused tests around each update scenario so the orchestration in apply can shrink without changing behavior.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
god_function | 4 |
long_function | 4 |
complex_branching | 3 |
exit_heavy | 3 |
deeply_nested | 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
- The
analyzefunction inanalyze-patterns.mjshas a fan-out of 54 — the single highest coupling point in the codebase. Any engineer touching it should map its downstream call graph before making changes, as a regression here can propagate across more than half the codebase’s function surface. handleKeyindashboard/internal/ui/screens/pipeline.gowas touched 5 times in the last 30 days with a cyclomatic complexity of 21 and multiple exit paths — introduce a command dispatch table before the next feature addition to prevent CC from climbing further.- The remaining top-five functions —
Update,parseTsvContent, andapply— all combine double-digit CC with active change, so they should get characterization tests before structural refactoring begins.
Reproduce This Analysis
git clone https://github.com/santifer/career-ops
cd career-ops
git checkout 8e554cc4437c3a58e813378abb9b35e2e08a007e
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 →