Across 20,580 TypeScript functions in appsmithorg/appsmith, 1,045 fall into the critical band — and four of the five highest activity-weighted risk scores belong to the same file: app/client/public/libraries/echarts@5.4.3.js. The top function, cv, carries an activity-weighted risk score of 24.42 with a cyclomatic complexity of 107, and it sits in the “fire” quadrant — meaning it is both structurally extreme and was touched 15 days ago. That combination makes it a live regression concern, not a backlog cleanup item. The rank-3 entry, evaluateTree, is the only genuine application code in the table — it warrants its own attention after the ECharts bundle is excluded.
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 |
|---|---|---|---|---|---|
cv | app/client/public/libraries/echarts@5.4.3.js | 24.4 | 107 | 4 | 52 |
yu | app/client/public/libraries/echarts@5.4.3.js | 21.5 | 29 | 3 | 15 |
evaluateTree | app/client/src/workers/common/DataTreeEvaluator/index.ts | 18.8 | 61 | 6 | 43 |
tc | app/client/public/libraries/echarts@5.4.3.js | 18.7 | 22 | 4 | 16 |
Vl | app/client/public/libraries/echarts@5.4.3.js | 18.7 | 23 | 5 | 17 |
Large Repo Analysis
appsmith 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
Four of the five functions in the top hotspots table — cv, yu, tc, and Vl — come from app/client/public/libraries/echarts@5.4.3.js, a minified third-party bundle committed directly to the repository. The fifth, evaluateTree, is application code in the data evaluation worker and is not affected by the exclusion. Minified files produce artificially extreme structural metrics because bundlers merge, inline, and rename symbols in ways that inflate cyclomatic complexity and fan-out far beyond what the original source would show. To exclude this directory and any other committed bundles in public/libraries/, add the following to your .hotspotsrc.json: { "exclude": ["app/client/public/libraries/"] }. The longer-term fix is to manage ECharts as a versioned npm dependency so it never appears in the git-tracked source tree.
Before getting into individual functions, the headline finding deserves to be stated plainly: four of the five entries in the top hotspots table resolve to app/client/public/libraries/echarts@5.4.3.js. The scoring is real — the structural complexity metrics are computed from what the parser sees — but for those four the actionable insight is architectural, not functional. evaluateTree is the exception and is addressed separately below.
cv — echarts@5.4.3.js
Bottom line: a cyclomatic complexity of 107 is extreme by any measure, and cv touched 15 days ago in the fire quadrant means the scorer treats this as a live, high-priority surface. With 107 independent execution paths and fan-out of 52 — meaning cv calls into 52 distinct functions — this is structurally the centre of gravity for the entire bundle as the scorer sees it. The complex_branching and exit_heavy patterns both fire here: 107 paths means 107 minimum test cases to achieve branch coverage, and multiple early-return exits compound that burden further. An activity-weighted risk score of 24.42 is the highest in the repository at this commit.
In reality, cv is almost certainly a minified identifier — a single letter produced by a bundler’s name-mangling pass — not a meaningful function name. The 1 commit touching this file in the last 30 days, with a single author and zero bug-linked commits or reverts in the external signals, suggests the “activity” here is likely an upstream version bump or a manual bundle refresh rather than iterative development on the logic itself. The correct response is not to refactor cv; it is to exclude this file from analysis and pull ECharts via a package manager so the minified source never lives in the repo.
yu — echarts@5.4.3.js
yu scores an activity-weighted risk of 21.49 with a cyclomatic complexity of 29 — moderate-to-high on its own, but amplified by the exit_heavy pattern. Twenty-nine execution paths through a minified function with 15 fan-out callees means the scorer flags it as a broad coupling point inside the bundle. Like cv, it was last changed 15 days ago in the same single commit, with one author and no historical defect signals. The risk score is real but the source is the same root cause: a committed minified bundle inflates every structural metric because minifiers merge and inline logic aggressively. I would not open this function in an editor — I would add the exclude rule.
evaluateTree — workers/common/DataTreeEvaluator/index.ts
evaluateTree is the one entry in this table that belongs to actual application logic, and it deserves more than a note about an exclusion rule. Cyclomatic complexity of 61 and maximum nesting depth of 6 place it firmly in deeply_nested and complex_branching territory: 61 independent execution paths through a function with six levels of conditional logic means every new widget type or binding edge case adds another branch to an already-dense decision tree. Fan-out of 43 — calling into 43 distinct functions — makes it a structural hub inside the evaluation worker, where a change can ripple in unpredictable directions across a wide surface of callees. After excluding the ECharts bundle, this function will rank first.
The decomposition path is clear: identify the top-level dispatch mechanism — likely a switch or if/else if chain over node types or evaluation modes — and extract each arm into a named handler with a single responsibility. That move reduces both CC and ND simultaneously and distributes the fan-out concentration across the new handlers. TypeScript discriminated unions are a natural fit for making the compiler enforce exhaustiveness on whatever the dispatch key turns out to be.
tc — echarts@5.4.3.js
tc places fourth with an activity-weighted risk of 18.7, a cyclomatic complexity of 22, and fan-out of 16. The complex_branching pattern applies: 22 execution paths is consistent with a minified symbol produced by collapsing several small module-level handlers into a single renamed identifier. Nesting depth of 4 is moderate but expected for minified output. Same single-commit activity history as cv and yu — a bundle refresh rather than iterative development on the logic itself. The exclusion rule removes it; no editor tab needed.
Vl — echarts@5.4.3.js
Vl closes the table at a risk score of 18.7, matching tc closely. Cyclomatic complexity 23 and nesting depth 5 with fan-out 17 give it essentially the same structural profile as tc — another bundler symbol accumulating merged branching from the original ECharts source. No independent defect history, no iterative commit pattern. Same recommendation as every other ECharts entry: add the exclusion rule and this function disappears from all future analysis runs.
Key Takeaways
- Exclude the ECharts bundle immediately. Add
"app/client/public/libraries/"to your.hotspotsrc.jsonexclude list. Four of the five top hotspots disappear, leavingevaluateTreeas the clear rank-1 priority — and the critical-band count of 1,045 will drop to reflect only application code. - Audit what else lives in
app/client/public/libraries/. If ECharts is committed as a flat JS file, other libraries may be too. Migrating tonpm/yarndependencies eliminates the analysis noise and keeps version bumps auditable through a lockfile rather than a binary diff. - After exclusion, re-run the analysis to find the real application hotspots. The 3,540 fire-quadrant functions and 1,045 critical-band functions in the current snapshot are dominated by this one file. The next run will surface the actual application logic that warrants refactoring attention.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
complex_branching | 4 |
cyclic_hub | 4 |
exit_heavy | 3 |
deeply_nested | 2 |
god_function | 1 |
long_function | 1 |
middle_man | 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.
Reproduce This Analysis
git clone https://github.com/appsmithorg/appsmith
cd appsmith
git checkout 99b2c28a83037941d2593fe1f7f4caba994d9ff7
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 →