
Refactoring Analysis
- 268 installs
- 552 repo stars
- Updated August 1, 2026
- pedronauck/skills
Assess code structure for smells, coupling, and safe refactor targets before merges, releases, or large feature changes in existing codebases.
About
Refactoring-analysis from pedronauck/skills helps agents systematically evaluate code health, pinpoint refactor opportunities, and propose prioritized, low-risk structural improvements with clear rationale.
- Smell detection
- Coupling analysis
- Incremental plans
- Risk prioritization
- Merge readiness
Refactoring Analysis by the numbers
- 268 all-time installs (skills.sh)
- +11 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #294 of 1,352 Code Review & Quality skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/pedronauck/skills --skill refactoring-analysisAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 268 |
|---|---|
| repo stars | ★ 552 |
| Last updated | August 1, 2026 |
| Repository | pedronauck/skills ↗ |
What it does
Assess code structure for smells, coupling, and safe refactor targets before merges, releases, or large feature changes in existing codebases.
Files
Refactoring Analysis
Perform a systematic analysis of a codebase to identify refactoring opportunities based on Martin Fowler's "Refactoring: Improving the Design of Existing Code" (2nd Edition). Produce a prioritized report with actionable findings.
Procedures
Step 1: Scope the Analysis
1. Determine the analysis target — a specific directory, module, feature area, or the entire project. If the user did not specify, ask which area to focus on. 2. Identify the project's language and paradigm (OOP, functional, mixed) to calibrate which smells and techniques are applicable. 3. If the project follows domain-driven design (DDD) or hexagonal architecture, read references/solid-ddd-context.md for additional SOLID-specific analysis criteria.
Step 2: Explore the Codebase
1. Map the directory structure and identify key modules, entry points, and shared utilities. 2. Read the most critical files (entry points, core business logic, shared modules). 3. Identify the project's conventions: naming, file organization, test patterns, dependency injection approach.
Step 3: Detect Code Smells
Read references/code-smells-catalog.md for the full catalog of detectable smells.
Systematically scan for the following smell categories, in priority order:
1. Bloaters — Long Functions (>15 lines of logic), Large Classes/Modules (>300 lines), Long Parameter Lists (>3 params), Data Clumps, Primitive Obsession. 2. Change Preventers — Divergent Change (one module changed for multiple unrelated reasons), Shotgun Surgery (one change touches 5+ files). 3. Dispensables — Duplicated Code, Dead Code, Speculative Generality, Lazy Elements, Comments as Deodorant. 4. Couplers — Feature Envy, Insider Trading (excessive data sharing between modules), Message Chains (>2 levels deep), Middle Man (>50% delegation). 5. Conditional Complexity — Nested Conditionals (>2 levels), Repeated Switches, Missing Guard Clauses, Complex Boolean Expressions. 6. DRY Violations — Near-identical code blocks, copy-pasted logic with minor variations, repeated parameter groups, duplicated constants or magic numbers.
For each detected smell:
- Record the exact file path and line range
- Classify the smell type (from the catalog)
- Assess severity:
critical|high|medium|low - Note the impact on maintainability, readability, or change cost
Step 4: Map Refactoring Opportunities
Read references/refactoring-techniques.md for the full technique catalog.
For each detected smell, identify the recommended refactoring technique(s):
| Smell | Primary Technique |
|---|---|
| Long Function | Extract Function, Decompose Conditional |
| Duplicated Code | Extract Function, Pull Up Method |
| Long Parameter List | Introduce Parameter Object, Preserve Whole Object |
| Feature Envy | Move Function |
| Data Clumps | Extract Class, Introduce Parameter Object |
| Primitive Obsession | Replace Primitive with Object |
| Large Class/Module | Extract Class, Extract Module |
| Repeated Switches | Replace Conditional with Polymorphism |
| Message Chains | Hide Delegate, Extract Function |
| Nested Conditionals | Replace with Guard Clauses |
| Dead Code | Remove Dead Code |
| Magic Numbers/Strings | Extract Constant |
| Mutable Shared State | Encapsulate Variable, Split Variable |
| Imperative Loops | Replace Loop with Pipeline (map/filter/reduce) |
For each opportunity, draft a concrete before/after code sketch showing the transformation.
Step 5: Assess Coupling and Cohesion
1. Identify modules with high afferent coupling (many dependents — risky to change). 2. Identify modules with high efferent coupling (many dependencies — fragile). 3. Flag circular dependencies between modules. 4. Assess cohesion — modules that mix unrelated responsibilities are candidates for Extract Class or Split Phase.
Step 6: Evaluate DRY Opportunities
1. Search for near-duplicate code blocks (>5 lines of similar structure). 2. Identify repeated constant values (magic numbers, repeated string literals). 3. Find repeated parameter patterns across function signatures. 4. Look for copy-pasted logic with minor variations that could be parameterized. 5. Propose extraction strategies: shared functions, constants files, parameter objects.
Step 7: SOLID Analysis (Domain Projects Only)
<GATE> Only perform this step if the project uses domain-driven design (DDD), hexagonal/clean architecture, or explicitly models a complex business domain. SOLID principles have the most impact in domain-rich, object-oriented codebases. For simple CRUD apps, utility libraries, or purely functional codebases, skip this step and note in the report that SOLID analysis was not applicable. </GATE>
Read references/solid-ddd-context.md for detailed guidance.
Evaluate against SOLID principles with domain-project focus:
- SRP: Classes/modules with multiple reasons to change
- OCP: Areas requiring modification (not extension) for new variants
- LSP: Subclasses that override to throw or no-op (Refused Bequest smell)
- ISP: Interfaces forcing implementers to stub unused methods
- DIP: High-level modules importing low-level implementations directly
Step 8: Prioritize and Generate Report
1. Read assets/refactoring-report-template.md for the output template. 2. Rank all findings by a priority score combining:
- Impact: How much does this hurt readability, maintainability, or change cost?
- Frequency: How often is this pattern encountered in the codebase?
- Effort: How much work to refactor? (low effort + high impact = do first)
3. Group findings into priority tiers:
- P0 — Critical: Blocking future development, causing bugs, or high coupling
- P1 — High: Significant maintenance burden, frequent pain point
- P2 — Medium: Noticeable but manageable, worth addressing opportunistically
- P3 — Low: Minor improvements, cosmetic, litter-pickup candidates
4. Generate the report following the template and save to: docs/_refacs/<YYYYMMDD>-<slug>.md where <slug> is a lowercase-hyphenated summary (e.g., auth-module-cleanup). 5. Create the docs/_refacs/ directory if it does not exist.
Step 9: Present Summary
1. Present a brief executive summary to the user with:
- Total findings count by severity
- Top 3-5 highest-impact opportunities
- Suggested refactoring order (quick wins first, then high-impact)
- Estimated complexity tier for each (trivial / moderate / significant)
2. Ask the user if they want to proceed with any specific refactoring.
Error Handling
- If the analysis target is too broad (>50 files), ask the user to narrow scope or
confirm they want a high-level scan with sampling.
- If the project has no tests, warn that refactoring without test coverage is risky
and recommend adding tests for critical paths before refactoring.
- If the project uses an unfamiliar framework or pattern, note this limitation in
the report rather than guessing.
- If a smell is ambiguous (could be intentional design), flag it as "potential" and
note the context that might justify the current structure.
Refactoring Report Template
Use this template to generate the output document. Save to: docs/_refacs/<YYYYMMDD>-<slug>.md
Replace all {{placeholders}} with actual values. Remove sections that don't apply.
---
BEGIN TEMPLATE:
# Refactoring Analysis: {{project-or-module-name}}
> **Date**: {{YYYY-MM-DD}}
> **Scope**: {{description of what was analyzed — directory, module, feature area}}
> **Analyzed by**: AI-assisted refactoring analysis (Martin Fowler's catalog)
> **Language/Stack**: {{e.g., TypeScript, React, Node.js}}
> **Test Coverage**: {{known / unknown / none — flag risk if none}}
---
## Executive Summary
{{2-4 sentences summarizing the overall health of the analyzed code and the most
impactful findings. Lead with the biggest opportunity.}}
| Severity | Count |
|----------|-------|
| 🔴 Critical (P0) | {{n}} |
| 🟠 High (P1) | {{n}} |
| 🟡 Medium (P2) | {{n}} |
| 🔵 Low (P3) | {{n}} |
| **Total** | **{{n}}** |
### Top Opportunities (Quick Wins + High Impact)
| # | Finding | Location | Effort | Impact |
|---|---------|----------|--------|--------|
| 1 | {{title}} | `{{file:line}}` | {{trivial/moderate/significant}} | {{description}} |
| 2 | {{title}} | `{{file:line}}` | {{trivial/moderate/significant}} | {{description}} |
| 3 | {{title}} | `{{file:line}}` | {{trivial/moderate/significant}} | {{description}} |
---
## Findings
### P0 — Critical
{{Repeat the finding block below for each P0 finding. Remove this section if no P0.}}
#### F{{number}}: {{Finding Title}}
- **Smell**: {{smell name from catalog — e.g., Duplicated Code, Feature Envy}}
- **Category**: {{Bloater / Change Preventer / Dispensable / Coupler / Conditional Complexity / DRY Violation}}
- **Location**: `{{file_path}}:{{start_line}}-{{end_line}}`
- **Severity**: 🔴 Critical
- **Impact**: {{What maintenance/readability/change-cost problem does this cause?}}
**Current Code** (simplified):{{relevant code snippet — keep to essential lines, max 20 lines}}
**Recommended Refactoring**: {{technique name — e.g., Extract Function, Introduce Parameter Object}}
**After** (proposed):{{refactored code sketch — showing the structural change, not a complete implementation}}
**Rationale**: {{Why this refactoring? What does it improve? Reference Fowler if relevant.}}
---
### P1 — High
{{Same finding block structure as P0. Remove if no P1.}}
#### F{{number}}: {{Finding Title}}
- **Smell**: {{smell name}}
- **Category**: {{category}}
- **Location**: `{{file_path}}:{{start_line}}-{{end_line}}`
- **Severity**: 🟠 High
- **Impact**: {{description}}
**Current Code** (simplified):{{code snippet}}
**Recommended Refactoring**: {{technique}}
**After** (proposed):{{refactored sketch}}
**Rationale**: {{explanation}}
---
### P2 — Medium
{{Same finding block structure. Remove if no P2.}}
#### F{{number}}: {{Finding Title}}
- **Smell**: {{smell name}}
- **Category**: {{category}}
- **Location**: `{{file_path}}:{{start_line}}-{{end_line}}`
- **Severity**: 🟡 Medium
- **Impact**: {{description}}
**Current Code** (simplified):{{code snippet}}
**Recommended Refactoring**: {{technique}}
**After** (proposed):{{refactored sketch}}
**Rationale**: {{explanation}}
---
### P3 — Low
{{For P3, a condensed table format is acceptable instead of full finding blocks.}}
| # | Smell | Location | Technique | Notes |
|---|-------|----------|-----------|-------|
| F{{n}} | {{smell}} | `{{file:line}}` | {{technique}} | {{brief note}} |
---
## Coupling Analysis
### Module Dependency Map
{{Describe the coupling structure. If helpful, include a Mermaid diagram:}}
graph LR A[Module A] --> B[Module B] A --> C[Module C] B --> C C --> D[Module D]
### High-Risk Coupling
| Module | Afferent (dependents) | Efferent (dependencies) | Risk |
|--------|----------------------|------------------------|------|
| {{module}} | {{n}} | {{n}} | {{high/medium/low}} |
### Circular Dependencies
{{List any circular dependency chains found, or "None detected."}}
---
## DRY Analysis
### Duplicated Code Clusters
| Cluster | Locations | Lines | Extraction Strategy |
|---------|-----------|-------|-------------------|
| {{description}} | `{{file1:lines}}`, `{{file2:lines}}` | {{n}} | {{strategy}} |
### Magic Values
| Value | Occurrences | Suggested Constant Name | Files |
|-------|-------------|------------------------|-------|
| {{value}} | {{n}} | `{{CONSTANT_NAME}}` | {{files}} |
### Repeated Patterns
{{Describe any repeated parameter groups, similar function signatures, or
copy-paste variations found.}}
---
## SOLID Analysis
{{Include this section only if the project uses DDD/hexagonal/clean architecture.
Otherwise, include the "Skipped" note from references/solid-ddd-context.md.}}
> **Context**: {{architectural context}}
| Principle | Finding | Location | Severity | Recommendation |
|-----------|---------|----------|----------|----------------|
| {{S/O/L/I/D}} | {{finding}} | `{{location}}` | {{severity}} | {{recommendation}} |
---
## Suggested Refactoring Order
Recommended sequence based on impact, effort, and dependency between refactorings:
### Phase 1: Quick Wins (trivial effort, immediate clarity)
1. {{action}} — `{{location}}`
2. {{action}} — `{{location}}`
### Phase 2: High-Impact Structural Changes
1. {{action}} — `{{location}}`
2. {{action}} — `{{location}}`
### Phase 3: Deeper Architectural Improvements
1. {{action}} — `{{location}}`
### Prerequisites
- {{Any test coverage needed before refactoring}}
- {{Any dependencies between refactorings — "do X before Y"}}
---
## Risks and Caveats
- {{Note any areas where the current structure might be intentional}}
- {{Flag if test coverage is insufficient for safe refactoring}}
- {{Note any ambiguous findings marked as "potential"}}
- {{Acknowledge framework constraints that limit refactoring options}}
---
## Appendix: Smell Distribution
| Category | Count | % |
|----------|-------|---|
| Bloaters | {{n}} | {{%}} |
| Change Preventers | {{n}} | {{%}} |
| Dispensables | {{n}} | {{%}} |
| Couplers | {{n}} | {{%}} |
| Conditional Complexity | {{n}} | {{%}} |
| DRY Violations | {{n}} | {{%}} |
| SOLID Violations | {{n}} | {{%}} |
| **Total** | **{{n}}** | **100%** |END TEMPLATE
Refactoring Analysis Checklist
Verify each item before finalizing the report.
---
Scope & Context
- [ ] Analysis target is clearly defined (directory, module, or feature area)
- [ ] Language and paradigm identified (OOP, functional, mixed)
- [ ] Project conventions understood (naming, structure, patterns)
- [ ] Test coverage status assessed and noted in report
Smell Detection — Completeness
- [ ] Scanned for Bloaters: Long Functions, Large Classes, Long Parameter Lists,
Data Clumps, Primitive Obsession
- [ ] Scanned for Change Preventers: Divergent Change, Shotgun Surgery
- [ ] Scanned for Dispensables: Duplicated Code, Dead Code, Speculative Generality,
Lazy Elements, Comments as Deodorant
- [ ] Scanned for Couplers: Feature Envy, Insider Trading, Message Chains, Middle Man
- [ ] Scanned for Conditional Complexity: Nested Conditionals, Repeated Switches,
Complex Booleans
- [ ] Scanned for DRY Violations: Magic Numbers, Copy-Paste Variations, Repeated
Parameter Groups
Refactoring Mapping
- [ ] Each smell has a mapped refactoring technique from the catalog
- [ ] Before/after code sketches provided for P0 and P1 findings
- [ ] Technique names use Fowler's canonical names
Coupling & Cohesion
- [ ] Module dependency structure analyzed
- [ ] High afferent coupling modules identified (risky to change)
- [ ] High efferent coupling modules identified (fragile)
- [ ] Circular dependencies checked
- [ ] Cohesion assessed (mixed responsibilities flagged)
DRY Analysis
- [ ] Near-duplicate code blocks identified
- [ ] Magic numbers and strings cataloged
- [ ] Repeated parameter patterns found
- [ ] Extraction strategies proposed
SOLID (If Applicable)
- [ ] Confirmed project uses DDD/hexagonal/clean architecture before analyzing
- [ ] Or: noted "SOLID analysis skipped" with rationale
- [ ] Each principle evaluated with concrete findings (not generic advice)
Prioritization
- [ ] All findings have severity: critical / high / medium / low
- [ ] Priority considers impact × frequency ÷ effort
- [ ] Quick wins identified (low effort, high clarity improvement)
- [ ] Suggested refactoring order accounts for dependencies between changes
Report Quality
- [ ] Report follows template from
assets/refactoring-report-template.md - [ ] Executive summary is concise (2-4 sentences) and leads with biggest opportunity
- [ ] Top opportunities table filled with 3-5 entries
- [ ] File paths are exact (file:line format)
- [ ] Code snippets are real (not fabricated), simplified to essential lines
- [ ] Rationale explains "why" not just "what"
- [ ] Risks and caveats section acknowledges ambiguity and limitations
- [ ] Saved to
docs/_refacs/<YYYYMMDD>-<slug>.md
Integrity
- [ ] No fabricated findings — every smell references real code
- [ ] Ambiguous cases flagged as "potential" with context
- [ ] Framework-specific patterns not falsely flagged (e.g., Redux boilerplate is expected)
- [ ] Intentional design choices acknowledged (e.g., Strategy pattern is not Feature Envy)
Code Smells Catalog
Complete catalog based on Martin Fowler's "Refactoring" (2nd Edition, Chapter 3), supplemented by refactoring.guru taxonomy and modern TypeScript/React patterns.
---
Bloaters
Smells where code grows too large to work with effectively.
Long Function
- Heuristic: >15 lines of logic, multiple levels of abstraction, or requires
comments to explain sections.
- Why it matters: Short functions with good names are self-documenting. Fowler:
"Classes with short methods live longest."
- Fix: Extract Function, Replace Temp with Query, Introduce Parameter Object,
Decompose Conditional, Split Loop, Replace Loop with Pipeline.
- Modern variant: React components with >5 lines before
return— extract to
custom hooks.
Large Class / Large Module
- Heuristic: >300 lines, many fields/exports, multiple unrelated responsibilities.
- Why it matters: Violates Single Responsibility. Hard to understand, test, and change.
- Fix: Extract Class, Extract Module, Extract Superclass, Replace Type Code with
Subclasses.
- Modern variant: God components in React, barrel files that re-export everything.
Long Parameter List
- Heuristic: >3 parameters in a function signature.
- Why it matters: Hard to call correctly, easy to swap arguments.
- Fix: Replace Parameter with Query, Preserve Whole Object, Introduce Parameter
Object, Remove Flag Argument, Combine Functions into Class.
- Modern variant: React components with >5 props — consider composition or context.
Data Clumps
- Heuristic: Same 3+ parameters or fields appear together repeatedly
(e.g., startDate/endDate, x/y/z, host/port/protocol).
- Why it matters: Signals a missing abstraction.
- Fix: Extract Class, Introduce Parameter Object, Preserve Whole Object.
Primitive Obsession
- Heuristic: Raw strings/numbers for domain concepts — money as
number, email
as string, status as string literal instead of enum.
- Why it matters: No validation, no behavior, easy to mix up (passing orderId
where userId expected).
- Fix: Replace Primitive with Object, Replace Type Code with Subclasses.
- Modern variant (TypeScript): Use branded types, discriminated unions, or
Zod schemas instead of raw primitives.
// Smell
function getUser(id: string): User
// Better
type UserId = string & { __brand: 'UserId' }
function getUser(id: UserId): User---
Change Preventers
Smells that make changes expensive — touching one thing forces changes elsewhere.
Divergent Change
- Heuristic: One module is modified for multiple unrelated reasons (e.g., a file
changes for both database schema updates AND UI rendering changes).
- Why it matters: Violates SRP. Merge conflicts, unclear ownership.
- Fix: Split Phase, Move Function, Extract Function, Extract Class.
Shotgun Surgery
- Heuristic: A single logical change requires edits in 5+ files scattered across
the codebase.
- Why it matters: High risk of missing a spot, expensive to change.
- Fix: Move Function, Move Field, Combine Functions into Class, Combine Functions
into Transform, Inline Function, Inline Class.
---
Dispensables
Code that could be removed to make the codebase cleaner.
Duplicated Code
- Heuristic: Same or near-identical code blocks in 2+ locations. Apply the
Rule of Three — "The first time, just do it. The second time, wince. The third time, refactor."
- Why it matters: Bug fixes applied to one copy but not others. Changes
require updating multiple locations.
- Fix: Extract Function, Pull Up Method, Slide Statements, Form Template Method.
Dead Code
- Heuristic: Unreachable code, unused variables, unused imports, commented-out
code blocks, unused function parameters.
- Why it matters: Adds noise, confuses readers, increases bundle size.
- Fix: Remove Dead Code. Use IDE/linter warnings (TypeScript
noUnusedLocals,
ESLint no-unused-vars).
Speculative Generality
- Heuristic: Abstractions, hooks, or parameters built for scenarios that never
materialized — abstract classes with one subclass, unused function parameters, overly generic interfaces.
- Why it matters: Complexity without value. YAGNI — You Aren't Gonna Need It.
- Fix: Collapse Hierarchy, Inline Function, Inline Class, Change Function
Declaration, Remove Dead Code.
Lazy Element
- Heuristic: A function, class, or variable that does too little to justify its
existence — a function that just calls another function, a class with one method.
- Why it matters: Unnecessary indirection.
- Fix: Inline Function, Inline Class, Collapse Hierarchy.
Comments as Deodorant
- Heuristic: Comments that explain what code does rather than why. If the
code needs a comment to be understood, the code should be refactored.
- Fowler: "When you feel the need to write a comment, first try to refactor
the code so that any comment becomes superfluous."
- Fix: Extract Function (name it what the comment says), Change Function
Declaration, Introduce Assertion.
---
Couplers
Smells related to excessive coupling between modules.
Feature Envy
- Heuristic: A function accesses another module's data or methods more than its
own. It "envies" the other module's features.
- Why it matters: Logic is in the wrong place; changes to the envied module
ripple unnecessarily.
- Fix: Move Function, Extract Function + Move.
- Exception: Strategy and Visitor patterns intentionally separate behavior from data.
Insider Trading (Inappropriate Intimacy)
- Heuristic: Two modules exchange data too freely, creating tight coupling.
One module reaches deep into another's internals.
- Why it matters: Changes to one module's internals break the other.
- Fix: Move Function, Move Field, Hide Delegate, Replace Subclass with Delegate.
Message Chains
- Heuristic: Chains like
a.getB().getC().getD()— client depends on the
navigation structure between objects.
- Why it matters: Changes to any intermediate class break the chain.
- Fix: Hide Delegate, Extract Function, Move Function.
Middle Man
- Heuristic: >50% of a class's methods just delegate to another class.
- Why it matters: Unnecessary indirection with no added value.
- Fix: Remove Middle Man, Inline Function, Replace Superclass with Delegate.
---
Conditional Complexity
Smells related to complex branching logic.
Nested Conditionals
- Heuristic: >2 levels of if/else nesting.
- Fix: Replace Nested Conditional with Guard Clauses (early returns).
// Smell
if (user) {
if (user.isActive) {
if (user.hasPermission) {
doThing()
}
}
}
// Better
if (!user) return
if (!user.isActive) return
if (!user.hasPermission) return
doThing()Repeated Switches
- Heuristic: Same switch/case or if/else chain appears in multiple places,
branching on the same type or status field.
- Fix: Replace Conditional with Polymorphism — each variant gets its own
class/strategy, or use an object map.
// Smell: scattered switch on userType
// Better: const handlers = { admin: handleAdmin, user: handleUser }Complex Boolean Expressions
- Heuristic: Boolean expressions with 3+ clauses, especially with mixed
AND/OR/NOT without clear grouping.
- Fix: Consolidate Conditional Expression — extract to a named function.
// Smell
if (age > 18 && hasLicense && !isSuspended && country === 'US')
// Better
if (canDrive(applicant))---
DRY Violations
Patterns that violate "Don't Repeat Yourself."
Magic Numbers and Strings
- Heuristic: Literal values used directly in logic without named constants.
Same literal appears in 2+ places.
- Fix: Extract Constant with a descriptive name.
// Smell
if (retries > 3) ...
setTimeout(fn, 30000)
// Better
const MAX_RETRIES = 3
const TIMEOUT_MS = 30_000Copy-Paste Variations
- Heuristic: Code blocks that are 80%+ identical with minor variations in
values, field names, or types.
- Fix: Extract Function with parameters for the varying parts. If the
variations are complex, use Combine Functions into Transform or Strategy pattern.
Repeated Parameter Groups
- Heuristic: Same set of 3+ parameters passed to multiple functions.
- Fix: Introduce Parameter Object or Combine Functions into Class.
---
Modern / TypeScript-React Specific Smells
Prop Drilling
- Heuristic: Props passed through 3+ intermediate components that don't use them.
- Fix: Extract Context, use state management (Zustand, Jotai), or composition.
God Component
- Heuristic: A single React component handles routing, data fetching, state
management, and rendering.
- Fix: Extract Custom Hook (state/effects), Extract Component (UI sections),
layered architecture.
Props Copied to State
- Heuristic:
useState(props.value)— copies props into local state, causing
sync issues.
- Fix: Derive from props directly, or use
keyprop to force remount.
Excessive any Types
- Heuristic: TypeScript's
anyused to bypass type safety instead of proper
types, generics, or discriminated unions.
- Fix: Replace with proper types. Use
unknown+ type guards if truly dynamic.
Boolean State Explosion
- Heuristic: Multiple boolean state variables that represent mutually exclusive
states (isLoading, isError, isSuccess).
- Fix: Use discriminated unions:
// Smell
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
const [data, setData] = useState<Data | null>(null)
// Better
type State =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: Data }
| { status: 'error'; error: Error }Imperative Loops
- Heuristic: For/while loops doing map, filter, or reduce work.
- Fix: Replace Loop with Pipeline.
// Smell
const results = []
for (const item of items) {
if (item.active) {
results.push(item.name)
}
}
// Better
const results = items.filter(i => i.active).map(i => i.name)Refactoring Techniques Catalog
Complete catalog of refactoring techniques from Martin Fowler's "Refactoring" (2nd Edition). Organized by chapter and category. Use this as a reference when mapping smells to fixes.
---
Quick Reference: Smell → Technique
| Smell | Primary Technique(s) |
|---|---|
| Long Function | Extract Function, Decompose Conditional, Split Loop |
| Duplicated Code | Extract Function, Pull Up Method, Slide Statements |
| Long Parameter List | Introduce Parameter Object, Preserve Whole Object, Remove Flag Argument |
| Feature Envy | Move Function, Extract Function + Move |
| Data Clumps | Extract Class, Introduce Parameter Object |
| Primitive Obsession | Replace Primitive with Object, Discriminated Unions |
| Large Class/Module | Extract Class, Extract Superclass, Split Phase |
| Repeated Switches | Replace Conditional with Polymorphism, Object Map |
| Message Chains | Hide Delegate, Extract Function |
| Nested Conditionals | Replace Nested Conditional with Guard Clauses |
| Dead Code | Remove Dead Code |
| Magic Numbers | Extract Constant |
| Mutable Shared State | Encapsulate Variable, Split Variable |
| Imperative Loops | Replace Loop with Pipeline (map/filter/reduce) |
| Middle Man | Remove Middle Man, Inline Function |
| Shotgun Surgery | Move Function, Move Field, Combine Functions into Class |
| Divergent Change | Extract Class, Split Phase |
| Speculative Generality | Inline Function, Collapse Hierarchy, Remove Dead Code |
| Comments as Deodorant | Extract Function (name = comment), Rename Variable |
| Temporary Field | Extract Class, Introduce Special Case |
---
A First Set of Refactorings
Extract Function
The most common refactoring. Pull a code fragment into its own function named after what it does. Even 2-3 lines benefit if naming adds clarity.
- When: Code needs a comment to explain, or logic is at a different
abstraction level than surrounding code.
- Reverse: Inline Function.
Inline Function
Remove a function and put its body back into callers.
- When: Function body is as clear as its name, or excessive indirection.
Extract Variable
Give a complex expression a self-describing name.
- When: An expression is hard to understand at a glance.
- Reverse: Inline Variable.
Change Function Declaration
Rename a function, add/remove parameters, or change parameter types.
- When: A name doesn't communicate intent, or the parameter list needs adjustment.
Encapsulate Variable
Wrap data access behind getter/setter functions.
- When: Widely accessed mutable data needs controlled access.
Introduce Parameter Object
Group recurring parameter clusters into a single object.
- When: Same 3+ parameters appear together in multiple function signatures.
Combine Functions into Class
Group functions that operate on the same data into a class or module.
- When: Several functions pass the same data between each other.
Combine Functions into Transform
Gather derived-data calculations into a single transform function.
- When: Multiple places compute derived values from the same source data.
Split Phase
Separate code into phases with distinct responsibilities, connected by a data structure passed between phases.
- When: Code does two different things — e.g., parsing + calculation.
---
Encapsulation
Encapsulate Record
Replace raw objects/records with classes that control access.
- When: Data structure is widely used and changes need to be tracked.
Encapsulate Collection
Return copies or read-only proxies instead of raw collections.
- When: Internal collection can be mutated by external code.
Replace Primitive with Object
Wrap a primitive in a domain-specific type.
- When: A primitive carries domain meaning (money, email, phone, ID).
- TypeScript: Use branded types or Zod schemas.
Replace Temp with Query
Convert a temporary variable to a function call.
- When: A temp is computed once and used in multiple places.
Extract Class
Split a class with multiple responsibilities into two.
- When: A subset of fields/methods form a coherent group.
Hide Delegate
Create wrapper methods to prevent clients from navigating through an object chain.
- When: Client code uses
a.getB().getC()chains. - Reverse: Remove Middle Man.
Substitute Algorithm
Replace a method body with a clearer algorithm.
- When: There's a simpler way to do the same thing.
---
Moving Features
Move Function / Move Field
Move a function or field to the module that uses it most.
- When: A function accesses more data from another module than its own.
- The bread and butter of refactoring — Fowler.
Move Statements into Function
Merge duplicated setup/teardown into the called function.
- When: Same statements always precede or follow a function call.
Replace Inline Code with Function Call
Replace code with a call to an existing function that does the same thing.
- When: Code duplicates what a library or utility function already does.
Slide Statements
Move related code lines together.
- When: Related declarations or logic are scattered within a function.
Split Loop
Separate a loop that does two things into two loops.
- When: A loop computes two unrelated things in one pass. Clarity > micro-optimization.
Replace Loop with Pipeline
Replace imperative loops with map/filter/reduce/flatMap.
- When: A loop initializes a result, iterates, conditionally adds to result.
Remove Dead Code
Delete unreachable or unused code.
- When: Code is never called. Version control remembers it if needed.
---
Simplifying Conditional Logic
Decompose Conditional
Extract condition, then-branch, and else-branch into named functions.
- When: A conditional block is complex enough to need comments.
// Before
if (date.before(SUMMER_START) || date.after(SUMMER_END))
charge = quantity * winterRate + winterServiceCharge
else
charge = quantity * summerRate
// After
if (isSummer(date))
charge = summerCharge(quantity)
else
charge = winterCharge(quantity)Consolidate Conditional Expression
Combine related conditions into a single named check.
- When: Multiple conditions yield the same result.
Replace Nested Conditional with Guard Clauses
Use early returns for edge cases, keeping the happy path un-nested.
- When: >2 levels of if/else nesting.
- Key insight: Two flavors — (1) both branches equally likely: use if/else,
(2) one is the "normal" path: use guard clauses.
Replace Conditional with Polymorphism
Replace switch/if chains with subclass or strategy overrides.
- When: The same conditional appears in multiple places.
- Modern alternative: Object maps or function maps.
const handlers: Record<Status, Handler> = {
pending: handlePending,
active: handleActive,
closed: handleClosed,
}
handlers[status]()Introduce Special Case (Null Object)
Replace scattered null/undefined checks with a Special Case object.
- When: Many places check for the same special value.
Introduce Assertion
Make implicit assumptions explicit with assertions.
- When: Code assumes a condition but doesn't verify it.
---
Refactoring APIs
Separate Query from Modifier
Functions should either return a value OR have side effects — not both.
- When: A function both modifies state and returns a value.
Remove Flag Argument
Replace boolean/flag parameters with separate explicit functions.
- When: A function takes a boolean that changes its behavior.
// Smell
setDimension(name, value, isMetric)
// Better
setMetricDimension(name, value)
setImperialDimension(name, value)Preserve Whole Object
Pass the whole object instead of extracting several values from it.
- When: Multiple values from the same object are passed as separate parameters.
Replace Constructor with Factory Function
Use a factory when construction needs flexibility.
- When: Constructor limitations (naming, return type) are problematic.
---
Dealing with Inheritance
Pull Up Method / Pull Up Field
Move shared method/field from subclasses to superclass.
- When: Multiple subclasses have the same method/field.
Push Down Method / Push Down Field
Move method/field from superclass to specific subclass.
- When: Only one subclass uses the method/field.
Replace Subclass with Delegate
Use composition instead of inheritance for variation.
- When: Subclassing creates coupling or the "is-a" relationship doesn't hold.
- Modern preference: Favor composition over inheritance.
Replace Superclass with Delegate
Replace inheritance with delegation.
- When: The "is-a" relationship is incorrect (e.g., Stack extends List).
Collapse Hierarchy
Merge a superclass and subclass that are too similar.
- When: A subclass adds no meaningful behavior.
---
When to Refactor (Fowler's Workflows)
Preparatory Refactoring
"Make the change easy, then make the easy change." Restructure before adding a feature.
Comprehension Refactoring
When reading code, refactor to make the understanding explicit.
Litter-Pickup Refactoring
"Always leave the code better than you found it." (Boy Scout Rule)
The Rule of Three
First time: just do it. Second time: wince. Third time: refactor.
Long-Term Refactoring
Large changes spanning weeks. Use Branch by Abstraction to keep the system working.
Fowler's Core Position
"Refactoring is not an activity you set aside time to do. Refactoring is something you do all the time in little bursts."
SOLID & DDD Context for Refactoring Analysis
Important Disclaimer
SOLID principles have the most impact in domain-rich, object-oriented codebases — particularly those using Domain-Driven Design (DDD), hexagonal architecture, or clean architecture. In simpler projects (CRUD apps, utility libraries, scripts), applying SOLID rigorously can lead to over-engineering.
"SOLID impacta principalmente a inversão de dependência, extensibilidade, substituição
sendo algo que vai funcionar em projeto orientado ao domínio, tipo DDD... pelo menos
uma arquitetura hexagonal pra poder aplicar DIP... OCP, LSP e ISP muito em domain model."
— Rodrigo Branas
Rule: Only recommend SOLID-based refactorings when the project has a complex domain model with clear bounded contexts, entities, value objects, or domain services. Note this context in the report.
---
When SOLID Analysis Applies
Perform SOLID analysis when the project exhibits at least 2 of:
- Domain entities or value objects (not just DTOs)
- Bounded contexts or explicit module boundaries
- Repository pattern or ports/adapters architecture
- Domain events or event-driven architecture
- Aggregate roots or domain services
- Hexagonal / clean / onion architecture layers
---
SOLID Principles — Detection and Refactoring
S — Single Responsibility Principle (SRP)
Detection heuristics:
- A class/module changes for multiple unrelated reasons (= Divergent Change smell)
- File has imports from many unrelated domains
- Class name includes "And", "Manager", "Handler" doing multiple things
- >5 public methods that group into 2+ unrelated clusters
Refactoring: Extract Class, Split Phase, Move Function
O — Open/Closed Principle (OCP)
Detection heuristics:
- Adding a new variant (e.g., payment type, notification channel) requires modifying
existing code instead of extending
- Switch/if chains on type codes that grow with each new variant
- Core logic mixed with variant-specific behavior
Refactoring: Replace Conditional with Polymorphism, Strategy Pattern, Replace Type Code with Subclasses
Context: Most valuable in domain layers where new business rules and variants are frequently added.
L — Liskov Substitution Principle (LSP)
Detection heuristics:
- Subclass overrides a method to throw
NotImplementedErroror returnnull
(= Refused Bequest smell)
- Subclass narrows the contract (rejects inputs the parent accepts)
- Subclass has side effects the parent doesn't specify
Refactoring: Replace Subclass with Delegate, Push Down Method, Extract Interface
I — Interface Segregation Principle (ISP)
Detection heuristics:
- Interfaces with >7 methods where implementers stub or no-op several of them
- "Fat" interfaces that force unrelated capabilities together
- Classes implementing an interface but only using 2-3 of its methods
Refactoring: Extract Interface (split into focused interfaces), Role Interfaces
Context: Most relevant in domain model interfaces — repositories, services, domain event handlers.
D — Dependency Inversion Principle (DIP)
Detection heuristics:
- High-level domain modules directly importing low-level infrastructure
(database drivers, HTTP clients, file system)
- Domain logic coupled to specific framework or library APIs
- No abstraction layer between domain and infrastructure
- Tests require spinning up real infrastructure instead of using test doubles
Refactoring: Extract Interface (port), Inject Dependencies, Introduce Adapter
Context: Requires at least hexagonal architecture to apply meaningfully. In a flat Express/Next.js handler, DIP may be over-engineering.
---
DDD-Specific Refactoring Opportunities
When the project uses DDD patterns, also evaluate:
Aggregate Boundaries
- Aggregates that are too large (>5 entities) — consider splitting
- Aggregates that reference other aggregates by object reference instead of ID
- Cross-aggregate transactions that should be eventual consistency
Value Objects
- Domain concepts represented as primitives that should be Value Objects
(= Primitive Obsession, but in DDD context)
- Mutable objects that should be immutable Value Objects
Domain Events
- Direct coupling between bounded contexts that should communicate via events
- Synchronous calls across context boundaries that should be async
Anti-Corruption Layer
- External system models leaking into the domain
- Missing translation layer between contexts
---
Report Template Additions for SOLID/DDD
When SOLID analysis is performed, add a section to the report:
## SOLID Analysis
> **Context**: This project uses [DDD / hexagonal / clean] architecture with
> [bounded contexts / domain entities / etc.]. SOLID analysis is applicable.
### Findings
| Principle | Finding | Location | Severity | Recommendation |
|-----------|---------|----------|----------|----------------|
| SRP | ... | ... | ... | ... |
### Domain Model Health
- Aggregate boundaries: [assessment]
- Value object coverage: [assessment]
- Cross-context coupling: [assessment]If SOLID analysis is NOT applicable, add:
## SOLID Analysis
> **Skipped**: This project does not use domain-driven design or a layered
> architecture pattern. SOLID-specific analysis was not performed. For projects
> with complex business domains, consider adopting hexagonal architecture to
> benefit from SOLID principles — particularly Dependency Inversion (DIP) for
> testability and Open/Closed (OCP) for extensibility.import re
import sys
import argparse
def validate_metadata(name, description):
errors = []
# 1. Validate Name Length
if not (1 <= len(name) <= 64):
errors.append(f"NAME ERROR: '{name}' is {len(name)} characters. Must be between 1-64.")
# 2. Validate Name Characters (lowercase, numbers, single hyphens)
if not re.match(r"^[a-z0-9]+(-[a-z0-9]+)*$", name):
errors.append(
f"NAME ERROR: '{name}' contains invalid characters. "
"Use only lowercase letters, numbers, and single hyphens. "
"No consecutive hyphens, and cannot start/end with a hyphen."
)
# 3. Validate Description Length
if len(description) > 1024:
errors.append(
f"DESCRIPTION ERROR: Description is {len(description)} characters. "
"Must be 1,024 characters or fewer."
)
# 4. Check for Third-Person Perspective (Basic Heuristic)
first_person_words = {"i", "me", "my", "we", "our", "you", "your"}
desc_words = set(re.findall(r'\b\w+\b', description.lower()))
found_forbidden = first_person_words.intersection(desc_words)
if found_forbidden:
errors.append(
f"STYLE WARNING: Description contains first/second person terms: {found_forbidden}. "
"Use third-person imperative (e.g., 'Analyzes...', 'Detects...')."
)
if errors:
print("\n".join(errors), file=sys.stderr)
sys.exit(1)
else:
print("SUCCESS: Metadata is valid and optimized for discovery.")
sys.exit(0)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Validate agent skill metadata (name and description) against the agentskills.io spec."
)
parser.add_argument("--name", required=True, help="Skill name (1-64 chars, lowercase, numbers, single hyphens).")
parser.add_argument("--description", required=True, help="Skill description (max 1024 chars, third-person).")
args = parser.parse_args()
validate_metadata(args.name, args.description)