Across 18,488 functions analyzed in temporalio/temporal, 6,163 fall into the ‘fire’ quadrant — complex code that is also being actively changed right now — with zero functions currently sitting in dormant structural debt. The top of the list is DescribeTaskQueue in service/matching/matching_engine.go, with a cyclomatic complexity of 101, a nesting depth of 7, and an activity-weighted risk score of 19.94. That’s high enough, combined with 1 touch in the last 30 days and 17 days since its last change, that I’d treat it as an active regression surface rather than a backlog item. I’d start there, then work down through four more functions in the matching and history packages that all show the same shape: deep branching, heavy fan-out, and recent commits landing on top of already-hard-to-reason-about code.
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 |
|---|---|---|---|---|---|
DescribeTaskQueue | service/matching/matching_engine.go | 19.9 | 101 | 7 | 72 |
LoadAndSplitQueryFromReaders | common/persistence/query_util.go | 18.8 | 90 | 7 | 15 |
Invoke | service/history/api/getworkflowexecutionhistory/api.go | 18.6 | 85 | 5 | 56 |
processStartChildExecution | service/history/transfer_queue_active_task_executor.go | 18.5 | 113 | 5 | 70 |
PollTask | service/matching/task_queue_partition_manager.go | 18.4 | 53 | 6 | 29 |
Large Repo Analysis
temporal 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.
<BandChart fire=‘6163’ debt=‘0’ watch=‘12325’ ok=‘0’ caption=‘Risk quadrant distribution across all 18,488 functions analyzed’ />
Every one of the top five functions lands in the ‘fire’ quadrant — complex and currently being changed. There are no dormant, high-complexity functions sitting untouched in this dataset’s top tier; the risk here is immediate, not archival.
<PatternCloud patterns=‘complex_branching:5,deeply_nested:5,exit_heavy:5,god_function:5,long_function:5’ />
All five hotspots share the same five antipatterns: complex branching, deep nesting, many exit points, god-function scope, and excessive length. That’s what a ‘fire’ quadrant function tends to look like structurally in a codebase this large. Multiple early returns (exit-heavy) mean each function has a wide test-coverage surface, since every return path is a distinct case a test suite has to hit for real coverage.
DescribeTaskQueue — service/matching/matching_engine.go
<FunctionCard fn=‘DescribeTaskQueue’ file=‘service/matching/matching_engine.go’ risk=‘19.94’ band=‘critical’ cc=‘101’ nd=‘7’ fo=‘72’ touches=‘1’ />
This is the highest activity-weighted risk in the repository. The excerpt shows a deprecated enhanced-mode branch that clones user data, resolves build IDs, checks a per-build-ID cache, and — on a cache miss — fans out to every partition to gather task queue info. A fan-out of 72 direct calls is enormous; that many dependencies means a change to this function’s internals can ripple into caching, partition management, and versioning logic all at once. The nesting depth of 7 lines up with the cache-check loop nested inside the build-ID loop nested inside the enhanced-mode branch. Notably, the code path explicitly marked ’// This has been deprecated’ is still carrying the bulk of the complexity — maintaining 101 branches of complexity behind a deprecated flag is a sustained cost for functionality the team may already be trying to retire. With 6 commits total on the file and a bug-fix rate of 16.7%, I’d treat this as a strong candidate for extracting the enhanced-mode logic into its own function before the next feature lands on top of it.
LoadAndSplitQueryFromReaders — common/persistence/query_util.go
<FunctionCard fn=‘LoadAndSplitQueryFromReaders’ file=‘common/persistence/query_util.go’ risk=‘18.82’ band=‘critical’ cc=‘90’ nd=‘7’ fo=‘15’ touches=‘1’ />
The excerpt shows a hand-rolled SQL statement tokenizer: a character-by-character scan with a stack tracking open parentheses, dollar-quoted blocks, IF, LOOP, BEGIN, and END keywords, using a switch statement inside a labeled loop with multiple continue and break stmtLoop exits. That’s a textbook source of cyclomatic complexity 90 — every keyword case and every stack-state check is a separate path. This one has only a single commit on record and a single author in the last 90 days, so the complexity is inherent to the parsing logic rather than something that accumulated through repeated patchwork. Given that it’s parsing schema migration SQL (splitting statements on delimiters while respecting nested blocks), I’d want dedicated unit tests around edge cases like unmatched parentheses and dollar-quoted blocks before touching this again — the current structure makes it easy to introduce a subtle tokenizing bug that only shows up on a specific migration file.
Invoke — service/history/api/getworkflowexecutionhistory/api.go
<FunctionCard fn=‘Invoke’ file=‘service/history/api/getworkflowexecutionhistory/api.go’ risk=‘18.56’ band=‘critical’ cc=‘85’ nd=‘5’ fo=‘56’ touches=‘2’ />
This is the entry point for fetching workflow execution history, and the excerpt shows a closure-heavy design: queryMutableState is defined inline with a nine-value return tuple and contains its own retry branch for a CurrentBranchChanged error when only the close event is requested. A fan-out of 56 combined with cyclomatic complexity of 85 tells me this function is coordinating a lot of downstream calls — mutable state queries, namespace validation, version checking, event notification — inside one body. 2 touches in the last 30 days and 7 days since the last change make this the most recently active function in the top five, so any change here lands on code that’s still warm. I’d pull the queryMutableState closure out into a named method; that alone would cut both the nesting and the local variable surface significantly.
processStartChildExecution — service/history/transfer_queue_active_task_executor.go
<FunctionCard fn=‘processStartChildExecution’ file=‘service/history/transfer_queue_active_task_executor.go’ risk=‘18.55’ band=‘critical’ cc=‘113’ nd=‘5’ fo=‘70’ touches=‘4’ />
This carries the highest cyclomatic complexity of the five, at 113, driven by what the code’s own comment calls out directly: ‘8 cases in total’ from the combination of workflow-running state, child-started state, and parent-close-policy value. The excerpt shows the first of those case splits already branching into three sub-cases in a code comment before the logic even executes. A fan-out of 70 means this function is deeply wired into workflow context loading, mutable state, task versioning, and namespace resolution. With 4 touches in the last 30 days and a file-level bug-fix rate of 25%, this is the closest thing to a proven defect-prone area in the top five — a quarter of its commit history has been bug fixes. The 8-case state matrix documented in the comments is a strong candidate for a decompose-conditional refactor: naming each of the 8 states explicitly (even as a lookup table or small state struct) would make the branching self-documenting instead of relying on prose comments to track which combination is being handled.
PollTask — service/matching/task_queue_partition_manager.go
<FunctionCard fn=‘PollTask’ file=‘service/matching/task_queue_partition_manager.go’ risk=‘18.36’ band=‘critical’ cc=‘53’ nd=‘6’ fo=‘29’ touches=‘2’ />
This function decides which task queue a poller should read from, and the excerpt shows nested branching across three axes: whether a deployment is present, whether the partition supports versioning, and whether the queue is sticky. The nesting depth of 6 shows up directly in the sticky-queue branch, which is nested inside the ‘versioning not supported’ branch, inside the ‘V1/V2 versioning requested’ branch. This file’s history includes one recorded revert, worth noting given that PollTask is a hot path for every worker poll — a regression here has broad blast radius across running workers, not just a single call site. With 2 touches in the last 30 days and four distinct authors touching the file in the last 90 days, this function has enough concurrent ownership that I’d want a second reviewer specifically checking the sticky-queue and versioning branches on the next change, since that’s exactly the area with the deepest nesting.
For context, the functions outside the top five in this dataset — things like len in service/matching/bits.go or max in service/matching/fair_level.go — sit in the ‘watch’ quadrant: low complexity, touched once in the last 30 days, and not worth refactoring attention right now. They’re a useful baseline for how much complexity separates a ‘watch’ function from a ‘fire’ one: cyclomatic complexity of 5 versus 53 and up.
Codebase Risk Distribution
All five top hotspots share the same structural patterns (complex_branching, deeply_nested, exit_heavy, god_function, long_function), which is typical of the highest-risk functions in any large codebase — they accumulate every structural signal on the way to the top. More useful context is how the risk is distributed across all 18,488 analyzed functions:
| Band | Functions |
|---|---|
| Critical | 2,627 |
| High | 3,536 |
| Moderate | 9,418 |
| Low | 2,907 |
Hotspot patterns 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/temporalio/temporal
cd temporal
git checkout 0d5e3848bed625907987f73d5eaae8dc77bc6590
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 →