Every one of the top five hotspots in swagger-api/swagger-ui sits in the debt quadrant — high structural complexity, zero commits in the last 30 days, and last modified 50 days ago. That combination means the risk isn’t a live regression today; it’s the blast radius waiting for whoever opens these files next. The most extreme case is sampleFromSchemaGeneric in src/core/plugins/json-schema-5-samples/fn/index.js, which has a cyclomatic complexity of 179 — meaning 179 independent execution paths, each a required test case and a potential bug surface. Across 1,406 total functions, 31 are flagged as critical; all five of the top-ranked ones cluster in the schema inference and validation subsystem, which is the layer that determines what users see when swagger-ui renders parameter types and example values.
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 |
|---|---|---|---|---|---|
getType | src/core/plugins/json-schema-2020-12/fn.js | 19.9 | 23 | 13 | 18 |
sampleFromSchemaGeneric | src/core/plugins/json-schema-2020-12-samples/fn/main.js | 19.7 | 124 | 7 | 35 |
sampleFromSchemaGeneric | src/core/plugins/json-schema-5-samples/fn/index.js | 19.7 | 179 | 7 | 32 |
inferType | src/core/plugins/json-schema-2020-12/fn.js | 19.0 | 29 | 13 | 4 |
validateValueBySchema | src/core/utils/index.js | 18.9 | 91 | 7 | 31 |
Large Repo Analysis
swagger-ui 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.
Repository snapshot
Analyzed at commit a11a4a9. Of 1,406 functions scored, 31 are critical and 118 are high-band. The quadrant picture tells the real story:
1,406 functions analyzed
The dominant signal is structural debt: 146 functions are complex and dormant, versus only 3 in the live-risk “fire” quadrant. The five functions analyzed below account for all five critical-band antipattern hits across the repository.
Calls an unusually large number of distinct functions (high fan-out), making it the structural centre of gravity for a subsystem.Long Function×5Long Function
Function body is too long to review in a single pass; likely contains multiple distinct responsibilities.Complex Branching×5Complex Branching
High cyclomatic complexity — many independent execution paths, each a potential bug surface and required test case.Deeply Nested×5Deeply Nested
Control structures nested 4+ levels deep, making it hard to reason about the full execution state at inner branches.Exit Heavy×5Exit Heavy
Multiple return or throw paths dispersed through the body — each exit needs separate test coverage.
Every top hotspot carries all five of those patterns simultaneously. That’s not a coincidence — it reflects a subsystem that has grown by accretion rather than design.
getType — fn.js (json-schema-2020-12)
This function resolves a JSON Schema 2020-12 node to a display type string — “any”, “never”, array<...>, “object”, “integer”, and so on. From the source excerpt, it handles null schemas, boolean schemas, cycle detection via a WeakSet, array tuple types via prefixItems, and OpenAPI 3.1-specific formats like int32 and float. That breadth of concern is exactly what drives the god-function pattern.
The nesting depth of 13 is the most alarming single number here. The excerpt shows inner closures (getArrayType, inferType) defined inside getType itself, each adding their own branching layers. A nesting depth of 13 means the deepest conditional sits inside at least 13 enclosing control structures — at that depth, local reasoning about what variables are in scope and what invariants hold requires holding the entire call stack in your head simultaneously.
The fan-out of 18 means getType calls 18 distinct functions. In a JavaScript context where some of those calls go through fnAccessor() — a dynamic accessor visible in the excerpt — static analysis almost certainly undercounts the true coupling. Change anything fnAccessor returns and getType’s behavior shifts in ways that no type checker will catch.
getType hasn’t been touched in 50 days and has 0 commits in the last 30. That dormancy is not safety — it means the next developer to extend JSON Schema 2020-12 type support will inherit all 23 branches and 13 nesting levels at once.
Recommendation: Extract getArrayType and inferType into module-level functions with explicit signatures. Each already has a coherent single responsibility; promoting them removes two layers of nesting from getType immediately and makes them independently testable. Target reducing getType’s cyclomatic complexity below 10 by routing each type family (array, object, scalar, const, combining keywords) to a dedicated handler.
sampleFromSchemaGeneric — main.js (json-schema-2020-12-samples)
This is the 2020-12-era implementation of the sample-generation engine — the function responsible for constructing example values that swagger-ui displays in the “Example Value” panel. With a cyclomatic complexity of 124, it has 124 independent execution paths. To achieve meaningful test coverage you would need a test suite that exercises each path at least once; in practice, at CC 124 that is rarely achieved.
The source excerpt shows the function managing oneOf/anyOf merging, XML namespace construction, maxProperties enforcement, readOnly/writeOnly filtering, and allOf combination — all inside a single function body. The fan-out of 35 means it delegates to 35 distinct callees. In a UI rendering context, a regression in this function shows up immediately: users see wrong example values, malformed XML snippets, or missing required properties in the “Try it out” panel.
Compared to its legacy counterpart below, the 2020-12 version is slightly less complex (CC 124 vs 179) but carries a higher fan-out (35 vs 32), suggesting the newer implementation added abstraction without reducing branching proportionally.
The external signals show only 1 total commit and a single author in the last 90 days — this code has had minimal review exposure. That’s not a defect signal, but it does mean the branching logic has not been stress-tested by many eyes.
Recommendation: Identify the top-level dispatch decision — the point where the function decides whether it is handling XML or JSON, and whether it is handling a oneOf/anyOf union versus a plain schema. Split those two axes into separate entry points or a strategy object. Reducing the function to a coordinator that delegates to sampleXmlFromSchema and sampleJsonFromSchema would cut the effective branching roughly in half before touching any leaf logic.
sampleFromSchemaGeneric — index.js (json-schema-5-samples)
This is the legacy (JSON Schema draft-05 / OpenAPI 2.x/3.0) counterpart of the function above, and it is the most complex single function in the repository by cyclomatic complexity. 179 independent execution paths. The source excerpt shows the same structural shape as the 2020-12 version — oneOf/anyOf merging, XML namespace handling, readOnly/writeOnly guards — but with additional inline property-merging logic that the newer version has extracted elsewhere. The inline for...in loop over schemaToAdd.properties inside the oneOf/anyOf branch, with its own chain of continue guards, is a concrete example of the exit-heavy pattern: multiple early returns and continue statements make it difficult to trace which properties end up in the merged schema for a given input.
The fact that two versions of this function exist in parallel — one for the 2020-12 plugin and one for the schema-5 plugin — means a bug fix in one rarely propagates to the other automatically. Any correctness work here should audit both files together.
Like its sibling, this function hasn’t been touched in 50 days and has had zero commits in the last month. The structural debt here is overdue for attention; the next time someone needs to add support for a new schema keyword or fix a sample-generation edge case, they will be working inside a 179-path function.
Recommendation: Before any feature work in this file, map the correspondence between this function and its 2020-12 counterpart. Shared logic — XML namespace construction, maxProperties enforcement, readOnly/writeOnly filtering — should live in shared utilities called by both. That alone would shrink both functions substantially and eliminate a class of divergence bugs.
inferType — fn.js (json-schema-2020-12)
This is the inner closure visible inside getType’s source excerpt — when a schema node does not declare an explicit type, inferType attempts to deduce it from the schema’s constraint keywords. It checks for array-related keywords (prefixItems, items, contains), object-related keywords (properties, additionalProperties, patternProperties), numeric formats, string constraints, and finally the const keyword with its own type-by-value dispatch.
At CC 29 and ND 13 — the same nesting depth as getType, because it is defined inside getType — it already exceeds the threshold where most static analysis tools would flag it as a refactoring target. The nesting depth here is the dominant risk: a developer reading this code must mentally unwind 13 levels of context to reason about the const-type branch at the bottom of the excerpt.
With a fan-out of only 4, inferType does not have a coupling problem — its issue is purely structural complexity and depth. That makes it a cleaner refactoring target than the other functions in this list: the blast radius of extracting it to a module-level function is small.
Recommendation: This function is the easiest win in the top five. Extract inferType to a named module-level export inferTypeFromConstraints(schema). Its fan-out of 4 means the extraction involves no significant dependency surgery. Once extracted, the CC 29 branch chain can be addressed with a lookup-table approach: map keyword presence to type strings, short-circuit at the first match.
validateValueBySchema — utils/index.js
This function validates a user-entered value against a JSON Schema definition — it’s the gatekeeper between what a user types in the “Try it out” form and what swagger-ui will submit as a request. The source excerpt shows it extracting a dozen schema constraints from an Immutable.js record (schema.get(...)), computing a cascade of boolean flags (stringCheck, arrayCheck, arrayListCheck, arrayStringCheck, fileCheck, and more), and then running multi-stage early-return logic.
The exit-heavy pattern is visible directly in the excerpt: the function returns an empty array, a populated errors array, or falls through to further validation at multiple points before reaching the main type-dispatch logic. With cyclomatic complexity of 91 and fan-out of 31, the function is simultaneously hard to trace statically and broadly coupled — changes here affect every parameter type that swagger-ui supports.
The god-function pattern manifests in the breadth of responsibility: null/nullable handling, required-field checking, type-coercion detection (e.g., treating a string value as an array), Immutable.js list handling, file input handling, and JSON object parsing all coexist in a single function. A change to how nullable values are handled risks touching the array-string coercion path.
Living in src/core/utils/index.js — a general-purpose utility barrel — means this function has no natural ownership boundary. The external signals show a single author in the last 90 days and no bug-linked commits, but a 91-path function handling form validation in a UI is exactly where subtle edge-case bugs can hide undetected.
Recommendation: Split validation into a pipeline: first a checkRequired(value, schema, options) stage that handles null/nullable/bypass logic and returns early, then a checkTypeMatch(value, type) stage that replaces the parallel boolean flag array with a type-keyed dispatch map, then constraint validators (checkStringConstraints, checkArrayConstraints, etc.) invoked only for the matched type. Each stage can be unit-tested in isolation against a small set of inputs.
The active layer: parameter-row.jsx
While the top five are all structural debt, the context data surfaces a separate concern worth noting. Three functions in src/core/components/parameter-row.jsx — render, UNSAFE_componentWillReceiveProps, and composeJsonSchema — are in the fire quadrant, each touched once in the last 8 days. The render function carries a cyclomatic complexity of 38 and fan-out of 32, making it the most structurally complex actively-changing function in the repository right now. It doesn’t crack the top five because its weighted risk score of 11.41 is lower than the schema-layer debt functions, but it deserves a watch: active development on a CC-38 render function in a UI component is a live regression surface, and regressions there are immediately visible to users.
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 1,406 analyzed functions:
| Band | Functions |
|---|---|
| Critical | 31 |
| High | 118 |
| Moderate | 468 |
| Low | 789 |
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.
Reproduce This Analysis
git clone https://github.com/swagger-api/swagger-ui
cd swagger-ui
git checkout a11a4a9c9c144842c92ac9e1052aa266c8f1d2d5
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 →