From claude-mods
Performs safe refactoring: extract functions/components/hooks (React/Vue/Svelte), rename symbols/files, move/restructure code, inline, detect dead code/unused imports with test-driven methodology.
npx claudepluginhub 0xdarkmatter/claude-modsThis skill is limited to using the following tools:
Comprehensive refactoring skill covering safe transformation patterns, code smell detection, dead code elimination, and test-driven refactoring methodology.
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Comprehensive refactoring skill covering safe transformation patterns, code smell detection, dead code elimination, and test-driven refactoring methodology.
What kind of refactoring do you need?
│
├─ Extracting code into a new unit
│ ├─ A block of statements with a clear purpose
│ │ └─ Extract Function/Method
│ │ Identify inputs (params) and outputs (return value)
│ │
│ ├─ A UI element with its own state or props
│ │ └─ Extract Component (React, Vue, Svelte)
│ │ Move JSX/template + related state into new file
│ │
│ ├─ Reusable stateful logic (not UI)
│ │ └─ Extract Hook / Composable
│ │ React: useCustomHook, Vue: useComposable
│ │
│ ├─ A file has grown beyond 300-500 lines
│ │ └─ Extract Module
│ │ Split by responsibility, create barrel exports
│ │ Watch for circular dependencies
│ │
│ ├─ A class does too many things (SRP violation)
│ │ └─ Extract Class / Service
│ │ One responsibility per class, use dependency injection
│ │
│ └─ Magic numbers, hardcoded strings, env-specific values
│ └─ Extract Configuration
│ Constants file, env vars, feature flags
│
├─ Renaming for clarity
│ ├─ Variable, function, or method
│ │ └─ Rename Symbol
│ │ Update all references (IDE rename or ast-grep)
│ │
│ ├─ File or directory
│ │ └─ Rename File + Update Imports
│ │ git mv to preserve history, update all import paths
│ │
│ └─ Module or package
│ └─ Rename Module + Update All Consumers
│ Search for all import/require references
│ Consider re-exporting from old name temporarily
│
├─ Moving code to a better location
│ ├─ Function/class to a different file
│ │ └─ Move + Re-export from Original
│ │ Leave re-export for one release cycle
│ │
│ ├─ Files to a different directory
│ │ └─ Restructure + Update All Paths
│ │ Use IDE refactoring or find-and-replace
│ │
│ └─ Reorganize entire directory structure
│ └─ Incremental Migration
│ Move one module at a time, keep tests green
│
├─ Simplifying existing code
│ ├─ Function is too simple to justify its own name
│ │ └─ Inline Function
│ │ Replace call sites with the body
│ │
│ ├─ Variable used only once, right after assignment
│ │ └─ Inline Variable
│ │ Replace variable with expression
│ │
│ ├─ Deep nesting (> 3 levels)
│ │ └─ Guard Clauses + Early Returns
│ │ Invert conditions, return early
│ │
│ └─ Complex conditionals
│ └─ Decompose Conditional
│ Extract each branch into named function
│
└─ Removing dead code
├─ Unused imports
│ └─ Lint + Auto-fix (eslint, ruff, goimports)
│
├─ Unreachable code branches
│ └─ Static analysis + manual review
│
├─ Orphaned files (no imports point to them)
│ └─ Dependency graph analysis (knip, ts-prune, vulture)
│
└─ Unused exports
└─ ts-prune, knip, or manual grep for import references
Run through this checklist before starting any refactoring:
Pre-Refactoring
[ ] All tests pass (full suite, not just related tests)
[ ] Working tree is clean (git status shows no uncommitted changes)
[ ] On a dedicated branch (not main/master)
[ ] CI is green on the base branch
[ ] You understand what the code does (read it, don't assume)
[ ] Characterization tests exist for untested code you will change
During Refactoring
[ ] Each commit compiles and all tests pass
[ ] Commits are small and focused (one refactoring per commit)
[ ] No behavior changes mixed with structural changes
[ ] Running tests after every change (use --watch mode)
Post-Refactoring
[ ] Full test suite passes
[ ] No new warnings from linter or type checker
[ ] Code review requested (refactoring PRs need fresh eyes)
[ ] Performance benchmarks unchanged (if applicable)
[ ] Documentation updated (if public API changed)
| Pattern | When to Use | Key Considerations |
|---|---|---|
| Extract Function | Block of code has a clear single purpose, used or could be reused | Name should describe WHAT, not HOW. Pure functions preferred. |
| Extract Component | UI element has own state, props, or rendering logic | Props interface should be minimal. Avoid prop drilling. |
| Extract Hook/Composable | Stateful logic shared across components | Must start with use. Return stable references. |
| Extract Module | File exceeds 300-500 lines, has multiple responsibilities | One module = one responsibility. Barrel exports for public API. |
| Extract Class/Service | Object handles too many concerns | Dependency injection over hard-coded dependencies. |
| Extract Configuration | Magic numbers, environment-specific values, feature flags | Type-safe config objects over loose constants. |
| What to Rename | Method | Pitfalls |
|---|---|---|
| Variable/function | IDE rename (F2) or ast-grep | String references (logs, error messages) not caught by IDE |
| Class/type | IDE rename + update file name to match | Serialized data may reference old name (JSON, DB) |
| File | git mv old new + update all imports | Import paths in test files, storybook, config files often missed |
| Directory | git mv + bulk import update | Barrel re-exports, path aliases in tsconfig/webpack |
| Package/module | Rename + re-export from old name | External consumers need deprecation period |
| Scenario | Strategy | Safety Net |
|---|---|---|
| Single file move | git mv + update imports + re-export from old path | rg 'old/path' to find all references |
| Multiple related files | Move together, update barrel exports | Run type checker after each move |
| Directory restructure | Incremental: one directory per PR | Keep old paths working via re-exports |
| Monorepo package split | Extract to new package, update all consumers | Version the new package, pin consumers |
Step 1: Automated Detection
│
├─ TypeScript/JavaScript
│ ├─ knip (comprehensive: files, deps, exports)
│ │ └─ npx knip --reporter compact
│ ├─ ts-prune (unused exports)
│ │ └─ npx ts-prune
│ └─ eslint (unused vars/imports)
│ └─ eslint --rule 'no-unused-vars: error'
│
├─ Python
│ ├─ vulture (dead code finder)
│ │ └─ vulture src/ --min-confidence 80
│ ├─ ruff (unused imports)
│ │ └─ ruff check --select F401
│ └─ coverage.py (unreachable branches)
│ └─ coverage run && coverage report --show-missing
│
├─ Go
│ └─ staticcheck / golangci-lint
│ └─ golangci-lint run --enable unused,deadcode
│
├─ Rust
│ └─ Compiler warnings (dead_code, unused_imports)
│ └─ cargo build 2>&1 | rg 'warning.*unused'
│
Step 2: Manual Verification
│ ├─ Check if "unused" code is used via reflection/dynamic import
│ ├─ Check if exports are part of public API consumed externally
│ ├─ Check if code is used in scripts, tests, or tooling not in the scan
│ └─ Check if code is behind a feature flag or A/B test
│
Step 3: Remove with Confidence
│ ├─ Remove in small batches, not all at once
│ ├─ One commit per logical group of dead code
│ └─ Keep git history -- you can always recover
| Smell | Heuristic | Refactoring |
|---|---|---|
| Long function | > 20 lines or > 5 levels of indentation | Extract Function, Decompose Conditional |
| God object | Class with > 10 methods or > 500 lines | Extract Class, Split by responsibility |
| Feature envy | Method uses another object's data more than its own | Move Method to the class whose data it uses |
| Duplicate code | Same logic in 2+ places (> 5 similar lines) | Extract Function, Extract Module |
| Deep nesting | > 3 levels of if/for/while nesting | Guard Clauses, Early Returns, Extract Function |
| Primitive obsession | Using strings/numbers where a type would be safer | Value Objects, Branded Types, Enums |
| Shotgun surgery | One change requires editing 5+ files | Move related code together, Extract Module |
| Dead code | Unreachable branches, unused exports/imports | Delete it (git has history) |
| Data clumps | Same group of parameters passed together repeatedly | Extract Parameter Object or Config Object |
| Long parameter list | Function takes > 4 parameters | Extract Parameter Object, Builder Pattern |
Refactoring Untested Code
│
├─ Step 1: Write Characterization Tests
│ │ Capture CURRENT behavior, even if it seems wrong
│ │ These tests document what the code actually does
│ └─ Goal: safety net, not correctness proof
│
├─ Step 2: Verify Coverage
│ │ Run coverage tool, ensure all paths you will touch are covered
│ └─ Add more tests if coverage is insufficient
│
├─ Step 3: Refactor in Small Steps
│ │ One transformation at a time
│ │ Run tests after EVERY change
│ └─ If tests fail, undo and try smaller step
│
├─ Step 4: Improve Tests
│ │ Now that code is cleaner, write better tests
│ │ Replace characterization tests with intention-revealing tests
│ └─ Add edge cases discovered during refactoring
│
└─ Step 5: Commit and Review
│ Separate commits: tests first, then refactoring
└─ Reviewers can verify tests pass on old code too
| Tool | Language | Use Case | Command |
|---|---|---|---|
| ast-grep | Multi | Structural search and replace | sg -p 'console.log($$$)' -r '' -l js |
| jscodeshift | JS/TS | Large-scale AST-based codemods | jscodeshift -t transform.js src/ |
| eslint --fix | JS/TS | Auto-fix lint violations | eslint --fix 'src/**/*.ts' |
| ruff | Python | Fast linting and auto-fix | ruff check --fix src/ |
| goimports | Go | Organize imports | goimports -w . |
| clippy | Rust | Lint and suggest improvements | cargo clippy --fix |
| knip | JS/TS | Find unused files, deps, exports | npx knip |
| ts-prune | TS | Find unused exports | npx ts-prune |
| vulture | Python | Find dead code | vulture src/ --min-confidence 80 |
| rope | Python | Refactoring library | Python API for rename, extract, move |
| IDE rename | All | Rename with reference updates | F2 in VS Code, Shift+F6 in JetBrains |
| sd | All | Find and replace in files | sd 'oldName' 'newName' src/**/*.ts |
| Gotcha | Why It Happens | Prevention |
|---|---|---|
| Refactoring and behavior change in same commit | Tempting to "fix while you're in there" | Separate commits: refactor first, then change behavior |
| Breaking public API during internal refactor | Renamed/moved exports consumed by external code | Re-export from old path, deprecation warnings |
| Circular dependencies after extracting modules | New module imports from original, original imports from new | Dependency graph check after each extraction |
| Tests pass but runtime breaks | Tests mock the refactored code, hiding the break | Integration tests alongside unit tests |
| git history lost after file move | Used cp + rm instead of git mv | Always git mv, verify with git log --follow |
| Renaming misses string references | IDE rename only catches code references, not configs/docs | rg 'oldName' across entire repo after rename |
| Over-abstracting (premature DRY) | Extracting after seeing only 2 occurrences | Rule of three: wait for 3 duplicates before extracting |
| Extracting coupled code | New function has 8 parameters because code is entangled | Refactor coupling first, then extract |
| Dead code removal breaks reflection/plugins | Dynamic imports, dependency injection, decorators | Grep for string references, check plugin registries |
| Performance regression after extraction | Extra function calls, lost inlining, cache misses | Benchmark before and after for hot paths |
| Merge conflicts from large refactoring PR | Long-lived branch diverges from main | Small PRs, merge main frequently, or use stacked PRs |
| Type errors after moving files | Path aliases, tsconfig paths, barrel exports not updated | Run type checker after every file move |
| File | Contents | Lines |
|---|---|---|
references/extract-patterns.md | Extract function, component, hook, module, class, configuration -- with before/after examples in multiple languages | ~700 |
references/code-smells.md | Code smell catalog with detection heuristics, tools by language, complexity metrics | ~650 |
references/safe-methodology.md | Test-driven refactoring, strangler fig, parallel change, branch by abstraction, feature flags, rollback | ~550 |
| Skill | When to Combine |
|---|---|
testing-ops | Write characterization tests before refactoring, test strategy for refactored code |
structural-search | Use ast-grep for structural find-and-replace across codebase |
debug-ops | When refactoring exposes hidden bugs or introduces regressions |
code-stats | Measure complexity before and after refactoring to quantify improvement |
migrate-ops | Large-scale migrations that require systematic refactoring |
git-ops | Branch strategy for refactoring PRs, stacked PRs, bisect to find regressions |