paperclip's server layer carries the highest activity risk — 5 functions to address first

Five server-side functions in paperclip — spanning heartbeat orchestration and company data portability — are simultaneously high-complexity and actively changing, creating live regression risk at commit 6a72faf.

Stephen Collins ·
oss typescript refactoring code-health

Antipatterns Detected

complex_branching5deeply_nested5exit_heavy5god_function5long_function5cyclic_hub1middle_man1

Key Points

What is a god function and why does it matter in paperclip?

A god function is one that has taken on too many responsibilities — it handles so many concerns that changes anywhere in it can affect behavior in unrelated parts of the codebase. In paperclip, all five top hotspots are flagged as god functions, with fan-out values ranging from 54 to 249, meaning a single change can ripple across hundreds of callees. This makes confident refactoring or debugging very difficult without comprehensive test coverage in place first.

How do I reduce cyclomatic complexity in TypeScript?

The most direct technique is extract-method refactoring — identify discrete conditional branches or logical phases within a large function and pull each into a named, independently testable function, reducing the number of execution paths in any single unit. For functions like `executeRun` (CC 99) or `importBundle` (CC 101), aim to extract until no single function exceeds a CC of 10–15.

Is paperclip actively maintained?

The data strongly suggests yes — all five top hotspots combine high structural complexity with high recent commit activity. Functions like `runDatabaseBackup` (activity-weighted risk score 20.1) and `companyPortabilityService` (activity-weighted risk score 19.7) are among the most actively changed in the repo right now.

How do I reproduce this analysis?

Run the Hotspots CLI against the paperclipai/paperclip repository at commit 6a72faf using `hotspots analyze` to reproduce these findings.

What does activity-weighted risk mean?

Complexity × recent commit frequency — functions that are hard to understand AND actively changing are the highest priority for refactoring.

paperclipai/paperclip’s highest risk is concentrated in two server-side files — server/src/services/heartbeat.ts and server/src/services/company-portability.ts — where extreme cyclomatic complexity meets active commit churn right now. The top-ranked function, executeRun, has a cyclomatic complexity of 99 and an activity-weighted risk score of 20.5, meaning it is both structurally extreme and actively changing — a live regression risk, not a backlog cleanup item. Across 9,949 total functions analyzed at commit 6a72faf, 612 fall into the critical band, and every one of the top 5 hotspots shares the same dangerous combination: god-function coupling, deep nesting, and high fan-out, all under active development.

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
executeRunserver/src/services/heartbeat.ts20.5995105
runDatabaseBackuppackages/db/src/backup-lib.ts20.145754
companyPortabilityServiceserver/src/services/company-portability.ts19.7936249
importBundleserver/src/services/company-portability.ts19.6101679
heartbeatServiceserver/src/services/heartbeat.ts19.3965232

Hotspot Analysis

executeRun — server/src/services/heartbeat.ts

Based on its name and location in heartbeat.ts, executeRun likely drives the core execution loop for a heartbeat or health-check service — the kind of orchestration code that coordinates multiple downstream operations on each invocation. Its cyclomatic complexity of 99 means there are roughly 99 independent execution paths through this function, each a distinct test case and a distinct failure mode. With a fan-out of 105 and an activity-weighted risk score of 20.5, this function is both maximally coupled and actively changing right now — every commit is navigating 99 potential paths across 105 callees, making silent regressions highly likely.

Recommendation: Write characterization tests to lock down current behavior before touching anything, then apply extract-method refactoring to pull discrete responsibilities — likely individual health-check steps — into named sub-functions with their own test coverage. Target reducing CC below 20 per extracted unit.

runDatabaseBackup — packages/db/src/backup-lib.ts

Living in backup-lib.ts, runDatabaseBackup almost certainly manages the end-to-end database backup process — a workflow that by nature involves many conditional branches for error states, storage targets, and backup strategies. A max nesting depth of 7 is a strong refactoring signal on its own; combined with a cyclomatic complexity of 45 and fan-out of 54, the function is deeply nested, broadly coupled, and exit-heavy — meaning it has multiple return and early-exit paths that each require independent test coverage to verify safely. Its activity-weighted risk score of 20.1 places it among the top two hotspots in the dataset, meaning this backup-critical path is being changed more frequently than almost anything else in the top 5 right now.

Recommendation: The nesting depth of 7 is the most immediate readability problem — flatten the control flow using early-return guards and extract discrete backup phases (e.g., pre-checks, snapshot creation, verification, cleanup) into separately tested functions to reduce both ND and CC in each unit.

companyPortabilityService — server/src/services/company-portability.ts

As a service-level orchestrator in company-portability.ts, companyPortabilityService likely coordinates the full lifecycle of moving or exporting company data — a broad mandate reflected in a fan-out of 249, the highest in the dataset. A cyclomatic complexity of 93 and max nesting depth of 6 confirm that it branches extensively and deeply to handle the many conditions involved in data portability. Its activity-weighted risk score of 19.7 and actively-changing status mean this is not theoretical risk — it is being modified today, and with 249 distinct callees, even a small logic error propagates across a very wide surface.

Recommendation: Map the 249 fan-out dependencies before any refactoring to understand the blast radius, then decompose the service into focused sub-services (e.g., export, validation, import coordination) — each independently testable and with a fraction of the current fan-out.

importBundle — server/src/services/company-portability.ts

importBundle lives alongside companyPortabilityService in company-portability.ts, handling the inbound side of data portability — ingesting and processing imported company data. With a cyclomatic complexity of 101 (the highest in the top 5) and a max nesting depth of 6, it contains more independent execution paths than any other hotspot, meaning more edge cases to cover and more ways for a change to produce unexpected behavior. Its fan-out of 79 and shared location with companyPortabilityService mean changes here interact closely with the broader portability pipeline, compounding the risk of cross-function regressions.

Recommendation: Treat importBundle’s CC of 101 as the primary refactoring target. Map its input variants — different data formats, partial imports, error conditions — then extract each as a separately tested handler. Reducing the function to a top-level dispatcher with CC under 20 per branch is a realistic first milestone.

heartbeatService — server/src/services/heartbeat.ts

heartbeatService is the companion to executeRun in heartbeat.ts, likely defining the service’s configuration, scheduling, or lifecycle management. A cyclomatic complexity of 96 and fan-out of 232 — the second-highest fan-out in the top 5 — indicate a function that both branches extensively and reaches across a very wide set of callees. Max nesting depth of 5, while lower than some hotspots, still reflects multi-level conditional logic that compounds the readability burden when combined with the sheer breadth of its dependencies.

Recommendation: The fan-out of 232 is the most urgent concern — before refactoring, document which callees are genuinely required versus incidental dependencies. Then apply the same extract-method approach used for executeRun: pull discrete responsibilities into named sub-functions, targeting CC below 20 per unit.

Patterns Found

Antipatterns detected across the top functions in this snapshot:

PatternOccurrences
complex_branching5
deeply_nested5
exit_heavy5
god_function5
long_function5
cyclic_hub1
middle_man1

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

  • executeRun in heartbeat.ts has a cyclomatic complexity of 99 and a fan-out of 105 — add characterization tests immediately, before the next commit touches it.
  • runDatabaseBackup has an activity-weighted risk score of 20.1 and a nesting depth of 7 — its exit-heavy branching means test coverage gaps here are the highest-probability source of undetected backup failures.
  • companyPortabilityService calls 249 distinct functions; map that dependency graph before any refactoring attempt to avoid breaking changes across the portability pipeline.
  • importBundle has a cyclomatic complexity of 101 — the highest in the top 5 — making it the most test-coverage-intensive function to refactor safely.
  • heartbeatService reaches 232 distinct callees; its fan-out is the second-highest in the dataset and warrants a dependency audit before any structural changes.

Reproduce This Analysis

git clone https://github.com/paperclipai/paperclip
cd paperclip
git checkout 6a72faf83b3aeb35ee44da51898fe8cbce6f5002
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 →

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