← Back to Next.js project starter
See how it helps
Decompose modules
Splits tangled code along real responsibility boundaries while keeping public behavior and call sites stable.
module-decomposition
Agents using this skill
When is it useful?
Use when a large module mixes calculations, transport and side effects.
What does it help you do?
- Finds cohesive domain logic and makes implicit state explicit.
- Extracts narrow helpers before moving them across file boundaries.
- Preserves import compatibility and checks for circular dependencies.
Try asking this
Separate invoice calculations from database and email orchestration while retaining existing service behavior and tests. Extract a pure total calculation first and compare invoice outputs. Move orchestration only after dependency boundaries are explicit and existing callers still work.
Detailed documentation
Read the skill’s original instructions, examples and checks in their authored language.
Show less
Module Decomposition
When a module or function grows into a "God object" that mixes transport, validation, business rules, data access and formatting, decomposing it requires choosing boundaries that reduce system complexity rather than scattering the same tangle across ten tiny files.
1. Deep Modules vs. Shallow Fragmentation (John Ousterhout)
- Aim for deep modules: a good module hides substantial internal logic behind a small, simple, stable interface. Callers should need to know what the module accomplishes, not how it sequences internal steps.
- Avoid classitis and pass-through layers: do not split a 400-line function into eight 50-line files that all pass the same 12 parameters to one another or require callers to orchestrate five steps in sequence.
- Measure coupling after the split: if modifying one rule still requires editing all newly split files in lockstep (shotgun surgery), the boundary was drawn along arbitrary lines instead of true cohesion.
2. Finding Natural Cohesion Axes
Separate responsibilities along boundaries that change for different reasons (Parnas's information hiding):
- Pure Domain Calculation vs. Side-Effectful Orchestration (Functional Core, Imperative Shell):
- Extract pricing, permission evaluation, state transitions, diffing, scheduling rules and data transformations into pure functions or domain objects that take plain data in and return decisions/results out without touching I/O.
- Keep the outer service or handler thin: load data via I/O, call the pure domain logic, and persist/emit the resulting effects.
- Transport / Boundary Adaptation vs. Application Logic:
- Separate HTTP/CLI/IPC request parsing, auth context extraction and response serialization from the core use-case service.
- Sub-domain / Aggregate Boundaries:
- When a single class manages unrelated concepts (e.g.,
UserServicehandling authentication, billing subscriptions, avatar uploads and notification preferences), split along cohesive domain capabilities that own distinct data subsets.
- When a single class manages unrelated concepts (e.g.,
3. Breaking Circular Dependencies
When extracting modules reveals circular imports (A -> B -> A):
- Extract shared pure types/utilities to a leaf module (
A -> CandB -> C) when both modules genuinely share lower-level domain primitives. - Invert the dependency (Dependency Inversion): if high-level policy
Acalls low-level mechanismBwhich calls back intoA, define a narrow interface or callback inAthatBimplements or receives. - Merge misplaced fragments: if
AandBcannot function without reaching into each other's private state on every call, they are likely two halves of a single cohesive responsibility that should live together.
4. Step-by-Step Decomposition Workflow
- Prepare in place first: within the existing file, group related functions and state, eliminate shared mutable locals, and turn implicit state into explicit parameters and return values.
- Extract pure helpers locally: verify that extracted functions have narrow signatures and pass all characterization tests before moving them across files.
- Move cohesive units to the new module: keep re-exports or thin delegation in the original module temporarily if external callers depend on the old import path, then migrate callers in verifiable batches.
- Verify interface ergonomics: check that the new module's public exports are minimal, internal helpers remain unexported, and no circular dependencies were introduced.
Included files 1
SKILL.md
