BMAD-METHOD's installer module carries the highest activity risk — 5 functions to address first

A Hotspots analysis of bmad-code-org/BMAD-METHOD at commit 5090cfb, surfacing the top functions by activity-weighted risk score. Five functions in the installer's module management layer — all with cyclomatic complexity between 34 and 73 and nesting depths up to 9 — concentrate the repo's structural and churn risk.

Stephen Collins ·
oss javascript refactoring code-health
Activity Risk19.35Low
Hottest FunctioncollectModuleConfig

Antipatterns Detected

exit_heavy10complex_branching9deeply_nested9god_function9long_function9

Key Points

What is BMAD-METHOD and why does its installer have complex code?

BMAD-METHOD is a structured, agent-driven software development methodology built around prompt templates and modular configuration. Its installer is responsible for resolving, fetching, and configuring a wide variety of official and custom modules — logic that naturally accumulates conditional branches as the number of supported module types grows. The result is a small set of functions that have to handle many different cases, which is what drives the high cyclomatic complexity scores reported here.

What does cyclomatic complexity mean in practice?

Cyclomatic complexity (CC) counts the number of independent execution paths through a function. A function with CC 68 has 68 paths that each need to work correctly — and each is a potential bug surface. In practice, high CC makes a function difficult to test exhaustively, hard to reason about during code review, and risky to modify because a change in one path can unexpectedly affect another.

How does nesting depth affect maintainability?

Max nesting depth (ND) measures how many levels of control flow a reader must track simultaneously to understand a single path through the function. At ND 9, a developer reading the innermost block must hold nine enclosing conditions in mind at once. Research consistently finds that comprehension drops sharply above ND 4–5, which is why functions at ND 9 are strong candidates for extract-method refactoring.

How do I reduce complexity in the BMAD-METHOD installer?

The most direct approach is extract-method refactoring: identify distinct phases within each function — input validation, module type dispatch, configuration assembly, error handling — and move each into a named, single-purpose function. For the branching driven by many module types, a dispatch table or strategy map eliminates branches at the structural level rather than just reorganizing them.

What does activity-weighted risk mean?

Activity-weighted risk multiplies structural complexity by recent commit frequency. A function with high cyclomatic complexity that is rarely touched carries lower priority than one that is equally complex but being actively changed — the latter is where bugs are most likely to be introduced right now.

BMAD-METHOD is an open-source framework for structured, agent-driven software development — a methodology that uses carefully designed prompt templates and modular agent configurations to guide AI-assisted engineering workflows. Its installer tooling is the operational core that makes the methodology usable: it resolves, fetches, and configures modules ranging from official BMAD templates to externally hosted custom agents. That breadth of responsibility — handling many module types, sources, and update strategies in a single codebase — is exactly what concentrates complexity in the five functions identified below.

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
collectModuleConfigtools/installer/modules/official-modules.js19.468923
findModuleSourceByCodetools/installer/modules/custom-module-manager.js17.83499
parseSourcetools/installer/modules/custom-module-manager.js17.640618
cloneExternalModuletools/installer/modules/external-manager.js17.573526
_resolveUpdateChannelstools/installer/ui.js17.562525

Hotspot Analysis

collectModuleConfig — tools/installer/modules/official-modules.js

At a cyclomatic complexity of 68 and a max nesting depth of 9, collectModuleConfig is the most structurally demanding function in this dataset. The name and path suggest it is responsible for assembling configuration objects for each official BMAD module — a task that is structurally expensive when many module types must each be handled with distinct logic. A CC of 68 implies 68 independent execution paths; at ND 9 the innermost logic sits behind nine layers of control flow that a reader must hold simultaneously. A fan-out of 23 means the function also coordinates 23 distinct downstream callees, distributing its logic widely across the module layer. All five structural antipatterns — complex_branching, deeply_nested, exit_heavy, god_function, long_function — are present, which is typical of a function that has absorbed every new module type as a new conditional block rather than as an extension point.

Recommendation: Introduce a per-module-type configuration handler — either a dispatch table or a set of small named functions — so that each module’s configuration logic lives in isolation. This breaks the god-function pattern directly, reduces CC by eliminating the cross-module branching, and brings nesting depth down by removing the outer dispatch conditionals.

findModuleSourceByCode — tools/installer/modules/custom-module-manager.js

findModuleSourceByCode has a lower CC (34) than collectModuleConfig but the same max nesting depth (9), which signals that the complexity here is driven more by deeply nested conditionals than by a large number of sequential branches. A function that reaches ND 9 with CC 34 likely contains several tightly nested if/else chains — each level validating or narrowing a condition — rather than a wide flat branching structure. Its fan-out of 9 is the lowest in the top 5, suggesting the function is relatively self-contained in terms of downstream coupling; the risk comes from its internal structure rather than from the breadth of what it touches.

Recommendation: Apply extract-method refactoring to the innermost nested blocks first, targeting the conditions responsible for driving depth beyond ND 4. Naming these extracted helpers after what they assert or validate will make the remaining structure at each level readable without reference to the inner logic.

parseSource — tools/installer/modules/custom-module-manager.js

parseSource shares a file with findModuleSourceByCode, suggesting both functions operate on the same custom-module data layer. Its CC of 40 with ND 6 and FO 18 points to a function that both branches across many source formats and reaches into 18 downstream callees to handle them — a broader coupling footprint than findModuleSourceByCode despite shallower nesting. Source parsing functions commonly accumulate this shape when handling a growing set of input formats: each new format adds a branch and typically a new callee, so CC and FO grow together. The exit_heavy pattern indicates those branches resolve through many different return points rather than a unified exit path, which makes tracing what the function produces for any given input non-trivial.

Recommendation: Separate format detection from format handling. A small dispatcher that identifies the source type and delegates to a dedicated parser per type keeps CC at the dispatch level low and makes each format’s handling independently testable. This also makes adding new source formats additive rather than requiring edits to a shared branching structure.

cloneExternalModule — tools/installer/modules/external-manager.js

cloneExternalModule has the highest CC in the top 5 at 73, despite a nesting depth of only 5 — a pattern that indicates a large number of sequential or parallel branches rather than deeply nested conditionals. Cloning external modules involves many failure modes, protocol variants, and configuration paths (different VCS hosts, authentication schemes, directory structures, error states), and this function appears to have absorbed them all inline. A fan-out of 26 confirms that the branching is backed by a wide set of downstream callees, each presumably responsible for a specific cloning strategy or side effect.

Recommendation: The 73 execution paths are the priority. Identify the major categories of cloning strategy — by protocol, by host type, by authentication method — and extract each into a dedicated function. A strategy or command pattern would make each path independently named, testable, and replaceable without touching the others. Reducing CC below 20 should be the initial target.

_resolveUpdateChannels — tools/installer/ui.js

_resolveUpdateChannels sits in ui.js rather than in the module management layer, which suggests it is responsible for determining which update channels or notification paths are visible to the user based on the current installer state. With CC 62, ND 5, and FO 25, it mirrors the shape of cloneExternalModule — broad branching across many cases with moderate nesting and wide downstream coupling — but in the UI coordination layer rather than the file-system layer. UI state-resolution functions commonly reach this complexity when they must reflect the combined state of many modules, installation phases, and configuration options simultaneously.

Recommendation: Decompose by concern: separate the logic that determines which channels are eligible from the logic that determines which are active and from the logic that determines how they are presented. Each of these is a distinct decision with its own inputs, and keeping them in a single function conflates what should be three independently testable operations.

Patterns Found

Antipatterns detected across the top functions in this snapshot:

PatternOccurrences
exit_heavy10
complex_branching9
deeply_nested9
god_function9
long_function9

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

  • cloneExternalModule has the highest cyclomatic complexity at CC 73 despite only ND 5, meaning its 73 paths are broad and sequential rather than deeply nested — each additional cloning strategy or error case added inline expands this surface further; extracting per-strategy handlers is the highest-leverage structural fix.
  • collectModuleConfig and findModuleSourceByCode both reach ND 9 — the maximum nesting depth in this dataset — and both live in the module management layer; the concentration of deep nesting in one subsystem signals that the module-type dispatch pattern used here is the root cause, not individual function choices.
  • All five functions share the exit_heavy pattern, meaning they resolve through many different return points; before any refactoring, add characterization tests that capture the current return values for representative inputs so you have a behavioral baseline to verify against after each extraction.

Reproduce This Analysis

git clone https://github.com/bmad-code-org/BMAD-METHOD
cd BMAD-METHOD
git checkout 5090cfb09617eeb9c5fb547d4d10529d9886adcd
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