supabase's type parser and storage explorer are the highest risks — all 5 touched today

A Hotspots analysis of supabase/supabase at commit 00d530d finds five fire-quadrant functions all modified within the last 24 hours, combining structural complexity with live commit activity across the docs layer, UI library, and Studio.

Stephen Collins ·
oss typescript refactoring code-health
Activity Risk20.94Low
Hottest Functionident

Antipatterns Detected

exit_heavy5complex_branching4deeply_nested3long_function3hub_function2god_function2

Every function in this top five was touched within the last 24 hours. That’s not a streak of bad luck — it reflects a codebase under active development, where structural debt and live commit activity are compounding simultaneously. Two of the five functions live in the same file (apps/docs/features/docs/Reference.typeSpec.ts), which also contributes three more functions to the watch tier; that file is worth treating as a unit, not just a source of individual hotspots. And createStorageExplorerState in the Studio carries fan-out into 173 distinct callees — a number that changes the nature of refactoring from “extract a helper” to “redesign the state boundary.”

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

FunctionFileRiskCCNDFO
identpackages/pg-meta/src/pg-format/index.ts20.915710
parseReferenceTypeapps/docs/features/docs/Reference.typeSpec.ts20.73249
DynamicFormapps/ui-library/registry/default/platform/platform-kit-nextjs/components/dynamic-form.tsx20.6581135
parseTypeapps/docs/features/docs/Reference.typeSpec.ts19.82829
createStorageExplorerStateapps/studio/state/storage-explorer.tsx19.1446173

Large Repo Analysis

supabase 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.

ident — packages/pg-meta/src/pg-format/index.ts

The name implies a simple SQL identifier formatter, but ident’s structural profile tells a more complicated story. CC 15 isn’t extreme, but seven levels of nesting inside what should be a formatting utility means the branching is deeply conditional rather than flat — likely handling quoting rules, escape sequences, reserved word checks, and edge cases for different identifier types accumulated over time. What earns it the top risk score is the hub_function pattern: fan-out into 10 distinct callees in a utility that callers probably treat as a leaf node creates an implicit dependency surface that grows with each new formatting rule. Combined with a commit landing within the last day, this function is being edited with complexity it doesn’t need to carry.

Recommendation: Flatten the nesting with guard clauses — each identifier class (quoted, unquoted, reserved) should be an early return or a dispatch to a named helper, not another nested branch. This keeps ident as the entry point while reducing the call depth that makes each change harder to reason about.

parseReferenceType — apps/docs/features/docs/Reference.typeSpec.ts

parseReferenceType is one of two top-five hotspots from a single file in the docs layer — a signal that Reference.typeSpec.ts is under broad active development, not just isolated churn. At CC 32 and four levels of nesting, it’s parsing TypeScript reference types for documentation generation: handling generic parameters, array shapes, union members, and mapped type syntax through a branching tree that has grown with the TypeScript type system itself. Nine callees (FO 9) means it delegates to specialised parsers, but the top-level branching logic still lives here.

The exit_heavy pattern suggests many of those 32 branches terminate with early returns, which keeps nesting shallower than CC 32 might imply — but it also means every new type shape handled here adds both a branch and an exit path. Extracting per-category handlers (one for generic references, one for array shapes, one for template literals) and having parseReferenceType dispatch into them would isolate each grammar rule and make the function’s own CC manageable.

DynamicForm — apps/ui-library/registry/default/platform/platform-kit-nextjs/components/dynamic-form.tsx

ND 11 is the deepest nesting value in this dataset — eleven levels of control structure inside a React component. CC 58 and fan-out into 35 callees confirm that DynamicForm has grown into a component that knows about every field type, every validation rule, and every conditional render path that the platform kit supports. At ND 11, the innermost branches are effectively unreachable during a casual code review — a reviewer tracing logic through eleven nested conditions is working from memory, not comprehension.

The god_function and long_function patterns together suggest this isn’t a form component so much as a form framework embedded in a single function body. The refactoring path here is component decomposition: each field type (text, select, date, file upload, nested object) becomes its own component, and DynamicForm becomes a registry-driven dispatcher. That brings CC and ND down to single digits for each piece while making individual field types independently testable.

parseType — apps/docs/features/docs/Reference.typeSpec.ts

parseType sits four ranks below parseReferenceType but shares the same file, the same activity window, and a nearly identical fan-out (9). With CC 28 and only ND 2, its complexity is wide rather than deep — a flat sequence of branches that each handle a different TypeScript type construct. The hub_function and cyclic_hub patterns together suggest parseType is both the primary entry point for type parsing and a mutual caller with other parsers in the file — a design that makes the call graph non-linear and complicates testing.

The cyclic_hub pattern is the more pressing concern: if parseType and parseReferenceType call each other, adding or modifying a type shape requires auditing both functions and their shared state. Resolving the cycle — typically by introducing an intermediate dispatch type or breaking the mutual dependency through a shared parse context — is a prerequisite for making either function safely decomposable.

createStorageExplorerState — apps/studio/state/storage-explorer.tsx

createStorageExplorerState calls into 173 distinct functions, making it the structural centre of gravity for the Studio’s storage management layer. Nothing else in this top five comes close — the next-highest fan-out is 35 (DynamicForm). FO 173 in a state factory means that nearly every operation the storage explorer can perform — listing buckets, uploading files, managing policies, handling errors — is wired directly into this one function. CC 44 and ND 6 sit on top of that breadth, confirming the function is also heavily conditional, not just wide.

This is less a refactoring target than an architectural boundary question: a state factory with 173 callees is really a module boundary that hasn’t been drawn yet. Splitting along natural sub-domains (bucket management, file operations, upload state, policy management) would distribute the FO across multiple state slices and make each one independently composable. The Zustand create pattern supports this well; createStorageExplorerState as it stands is a case where the tool fits the solution but the solution hasn’t been applied at the right granularity.

Key Takeaways

  • Reference.typeSpec.ts is the file to watch: parseReferenceType and parseType both crack the top five, and three more functions from the same file sit in the watch tier. Treat the file as the refactoring unit, starting with resolving the cyclic dependency between the two top-ranked parsers.
  • DynamicForm (ND 11, CC 58) is the deepest and most complex UI component in this snapshot — component decomposition by field type is the path forward, not incremental cleanup.
  • createStorageExplorerState’s FO 173 is an architectural signal, not just a metric: the storage explorer state needs to be split into sub-domain slices before individual function complexity becomes manageable.

Patterns Found

Antipatterns detected across the top functions in this snapshot:

PatternOccurrences
exit_heavy5
complex_branching4
deeply_nested3
long_function3
hub_function2
god_function2
cyclic_hub1

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/supabase/supabase
cd supabase
git checkout 00d530d54a311c8ea977a5b21a630f4e499dcb26
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 →

Run this on your own codebase

Hotspots runs locally in under a minute — no account, no data leaves your machine.

macOS
$ brew install Stephen-Collins-tech/tap/hotspots
Linux / cargo
$ cargo install hotspots-cli
Run in any repo
$ hotspots analyze .
★ Star on GitHub

Related Analyses