Of 524 functions analyzed in iamkun/dayjs at commit ea2929d, 34 land in the critical band — and the highest-ranked hotspot, format in src/index.js, hasn’t been actively changed recently but carries a cyclomatic complexity of 38 and fans out to 13 callees, meaning the blast radius when it is next touched is substantial. Every top-5 function carries high structural complexity but low recent activity — the risk is accumulated debt, not current churn. With 34 critical and 26 high-band functions across a 524-function codebase, the concentrated risk in src/index.js and the plugin layer deserves attention before the next development push.
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 |
|---|---|---|---|---|---|
format | src/index.js | 12.7 | 38 | 1 | 13 |
<anonymous> | src/plugin/objectSupport/index.js | 12.5 | 15 | 2 | 21 |
translate | src/locale/sl.js | 12.5 | 51 | 2 | 2 |
<anonymous> | src/index.js | 12.4 | 52 | 1 | 6 |
<anonymous> | src/plugin/relativeTime/index.js | 12.3 | 9 | 3 | 14 |
Hotspot Analysis
format — src/index.js
The format function in src/index.js is almost certainly the core date-formatting engine — the central path any caller takes to render a dayjs object as a string. A cyclomatic complexity of 38 means 38 independent execution paths, each a required test case and a potential bug surface; fan-out of 13 means it reaches into 13 other functions, giving it broad coupling across the codebase. It has not been actively touched recently, but the stale_complex and god_function patterns flag it as accumulated structural debt with a high blast radius the moment formatting behavior needs to change. The exit_heavy pattern adds a test-coverage burden — multiple return paths mean any incomplete test suite leaves dark corners.
Recommendation: Add a comprehensive characterization test suite covering all 38 execution paths before any modification, then use extract-method refactoring to break format into smaller, single-responsibility helpers (e.g. one per token type), reducing both CC and fan-out in the core function.
<anonymous> — src/plugin/objectSupport/index.js
The anonymous top-level function in src/plugin/objectSupport/index.js is most likely the plugin registration wrapper that extends dayjs with object-argument support — a common pattern in the dayjs plugin architecture. Despite a moderate cyclomatic complexity of 15, its fan-out of 21 is the highest in the top five, meaning this single function reaches into 21 distinct callees; changes here can produce unexpected ripple effects across a wide surface. Like format, it has not been actively modified recently — but the god_function and exit_heavy patterns indicate it has accumulated responsibility and exit paths that will be costly to reason through when the plugin next needs updating.
Recommendation: Map the 21 fan-out targets to understand which are truly owned by this function versus merely called through it, then extract discrete delegation helpers to reduce coupling and make the plugin’s extension points individually testable.
translate — src/locale/sl.js
The translate function in src/locale/sl.js is the Slovenian locale’s pluralization and string-selection logic — a function that must handle the grammatical complexity of Slovenian noun cases, which is one of the more demanding in any i18n system. A cyclomatic complexity of 51 is high: it places this function among the most branchy in the entire codebase, with 51 independent paths driven almost entirely by grammatical rules rather than fan-out (fan-out is just 2). The exit_heavy pattern confirms that many of those paths terminate in early returns, making full test coverage genuinely difficult. This is pure structural debt — it hasn’t been recently touched, but its complexity means any future locale correction or pluralization fix carries significant regression risk.
Recommendation: Replace the conditional chains with a data-driven lookup table keyed by grammatical category and word form. This collapses most of the 51 branches into a single indexed access, making the pluralization rules easier to audit and extend. Scope any future locale correction narrowly — 51 paths cannot be meaningfully reviewed in a standard PR, so regression tests covering Slovenian noun-case edge cases are essential before any change lands.
<anonymous> — src/index.js
The anonymous function in src/index.js ranked fourth has a cyclomatic complexity of 52 — the highest CC in the top five — with a nesting depth of only 1 and fan-out of 6. That combination (very high CC, low ND, low FO) means the complexity is wide and shallow: many parallel cases handled inline rather than nested or delegated. This is likely the dayjs constructor or parser — the function that initializes a dayjs instance from an input argument and must handle numbers, strings, arrays, objects, and Date instances in separate branches. The stale_complex and long_function patterns confirm it has grown by accretion and hasn’t been recently modified. With 52 independent paths, any incomplete test suite leaves parsing edge cases unverified.
Recommendation: Adopt a dispatch-table approach: consolidate branch clusters into a map from input type to a single-responsibility parsing handler (one per input type: number, string, array, object, Date). This reduces CC by replacing the flat conditional with a lookup and makes each parsing path independently testable.
<anonymous> — src/plugin/relativeTime/index.js
The anonymous function in src/plugin/relativeTime/index.js has a modest CC of 9 but the deepest nesting in the top five (ND 3) and the second-highest fan-out after objectSupport (FO 14). This is most likely the core of the relativeTime plugin — translating a numeric time difference into a locale-aware string like “a few minutes ago.” Its complexity is coordination complexity: it orchestrates 14 callees across threshold logic and locale formatting, with nested conditionals managing the edge cases between thresholds. The god_function pattern flags this as a function that owns too many concerns for its size.
Recommendation: Break the function into two phases: (1) a pure numeric threshold calculator that returns a keyed token (e.g. 'minutes', 'hours'), and (2) a formatting layer that delegates to the active locale. This separation makes the threshold logic independently testable, reduces the fan-out of the core function, and mirrors how well-structured i18n libraries handle relative time.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
exit_heavy | 5 |
god_function | 3 |
long_function | 2 |
stale_complex | 2 |
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
- The format function in src/index.js has CC 38 and fan-out 13 — write characterization tests covering all execution paths before any future formatting change to avoid silent regressions.
- The anonymous function in src/plugin/objectSupport/index.js has a fan-out of 21, the highest in the top five; audit its 21 callees to identify which responsibilities can be extracted into focused helpers before the plugin is next modified.
- The translate function in src/locale/sl.js has CC 51 driven by grammatical branching, not coupling — scope any locale fix narrowly and ensure pluralization edge cases are regression-tested, since 51 paths cannot be meaningfully reviewed in a standard PR review.
- The anonymous constructor/parser in src/index.js (CC 52) handles every input type in a single flat function — a dispatch table keyed by input type would reduce CC and make each parsing path independently testable.
- The relativeTime plugin’s core function has fan-out 14 and nesting depth 3 — separating threshold calculation from locale formatting would reduce coordination complexity and make the threshold logic testable in isolation.
Reproduce This Analysis
git clone https://github.com/iamkun/dayjs
cd dayjs
git checkout ea2929d2c9aaba4a6766d8954e0dc37c24f8e5a2
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 →