Chain-combined skill that ensures refactoring is done safely by combining refactor judgment, test coverage verification, and pre-commit review. Encapsulates the decision of WHEN to refactor, HOW to ensure safety, and WHAT verification is needed. Use when user mentions refactoring, code restructuring, improving code quality, or when they want to refactor without breaking behavior. Triggers especially when user says "should I refactor this?", "is it safe to refactor?", "how do I refactor without breaking things?", or "refactor my code".
Safe-refactor is a chain-combined behavioral specification skill that merges:
refactor — When and how to refactor judgmentdoublecheck — Verification and safety checkstest-think — Test coverage decisionsThe core judgment it encapsulates: "How do I refactor this code while ensuring I don't change its behavior?"
This skill embodies the mindset of an experienced developer who knows that refactoring without tests is gambling.
"Refactoring changes the structure of code, not its behavior."
This is the fundamental contract. If behavior changes, it's not refactoring — it's a feature change or bug fix, and needs appropriate process.
┌─────────────────────────────────────────────────────┐
│ SAFE REFACTOR │
│ │
│ Existing Tests ─────┐ │
│ ├──► SAFE TO REFACTOR │
│ Coverage Good? ─────┘ │
│ │
│ Existing Tests ─────┐ │
│ ├──► ADD TESTS FIRST │
│ Coverage Poor? ─────┘ │
└─────────────────────────────────────────────────────┘
Before touching any code, answer these questions:
| Reason | Proceed? | Notes |
|---|---|---|
| "Code is messy" | 😐 Maybe | Define success criteria first |
| "Hard to add feature" | ✅ Yes | Ensure tests cover feature path |
| "Performance" | ✅ Yes | Benchmark before/after |
| "Learning the code" | ✅ Yes | But don't merge until understood |
| "Just feels old" | ❌ No | YAGNI applies to structure too |
Map out the scope:
## Refactoring Scope
### High-Risk Changes (Require Extra Care)
- [ ] Changing function signatures
- [ ] Moving code between modules
- [ ] Changing data structures
- [ ] Modifying shared utilities
### Medium-Risk Changes
- [ ] Extracting functions
- [ ] Renaming variables
- [ ] Reordering code
### Low-Risk Changes
- [ ] Formatting
- [ ] Comment improvements
- [ ] Renaming (non-shared)
# Find all usages
grep -r "functionName" --include="*.ts" src/
# Check if it's a shared utility
# Check how many files depend on it
## Current Test Coverage Analysis
| Module | Coverage | Tests | Action |
|--------|----------|-------|--------|
| utils.ts | 45% | 2 | Add tests before refactor |
| auth.ts | 89% | 12 | Safe to refactor |
| payment.ts | 0% | 0 | MUST add tests first |
Before refactoring, identify what's MOST important to keep working:
┌────────────────────────────────────────────────────────────┐
│ CRITICAL PATH MAP │
│ │
│ User ──► Login ──► Dashboard ──► Action ──► Result │
│ │ │ │
│ │ └──► Analytics │
│ │ │
│ └──► Notifications │
│ │
│ Must keep working: Auth, Core business logic │
└────────────────────────────────────────────────────────────┘
For modules with poor coverage:
┌─────────────────────────────────────────────────────────────┐
│ UNSAFE → SAFE REFACTORING │
│ │
│ 1. BEFORE: Write tests for current behavior │
│ - Don't change behavior, just document it │
│ - Tests should FAIL if behavior changes │
│ │
│ 2. REFACTOR: Make structural changes │
│ - Keep behavior identical │
│ - Tests should STILL PASS │
│ │
│ 3. AFTER: Verify all tests pass │
│ - If tests fail, behavior changed │
│ - Fix the refactor, not the tests │
└─────────────────────────────────────────────────────────────┘
Rule: Commit after each logical step.
# Step 1: Extract function
git commit -m "refactor: extract validateEmail from processUser"
# Step 2: Move to new module
git commit -m "refactor: move validateEmail to utils/validation"
# Step 3: Update imports
git commit -m "refactor: update imports to use utils/validation"
# Step 4: Clean up
git commit -m "refactor: remove duplicate validation"
Never do multiple refactorings in one commit.
"Leave the code cleaner than you found it."
But only clean what you came to clean. Don't get distracted by other issues.
SAFE REFACTOR CHECKLIST
=======================
PRE-REFACTOR
□ Understand why you're refactoring
□ Identify blast radius
□ Ensure tests exist for critical paths
□ Coverage is acceptable (or tests added)
□ Define success criteria
DURING REFACTOR
□ Behavior unchanged (tests pass throughout)
□ Small steps, one logical change at a time
□ Tests still pass after each step
□ No shortcuts or workarounds added
POST-REFACTOR
□ All tests pass
□ Coverage maintained or improved
□ No new code smells introduced
□ Documentation updated (if needed)
□ Peer review (if high-risk)
Run these to confirm behavior is preserved:
# Unit tests
npm test
# E2E tests (if available)
npm run test:e2e
# Manual smoke tests
- Login flow works
- Core feature works
- Error handling works
| Characteristics | Examples |
|---|---|
| Small scope | 1-2 files |
| No shared code | Internal to module |
| Good tests | >80% coverage |
| Well-understood | Already worked with |
Strategy: Refactor with care, verify with tests.
| Characteristics | Examples |
|---|---|
| Medium scope | 3-10 files |
| Some shared code | 2-3 consumers |
| Good tests | 60-80% coverage |
| Partially understood | Some uncertainty |
Strategy: Self-refactor + quick peer review before merge.
| Characteristics | Examples |
|---|---|
| Large scope | 10+ files |
| Highly shared | 10+ consumers |
| Poor tests | <60% coverage |
| Unfamiliar code | Legacy/touch |
| Security sensitive | Auth/data |
Strategy: Pair programming or detailed review required.
Analysis:
- Function used in 15 files
- Medium risk
Steps:
1. [ ] Find all usages (grep)
2. [ ] Write test that calls the function (if none exists)
3. [ ] Rename the function
4. [ ] Update all references
5. [ ] Run tests
6. [ ] Commit
Analysis:
- Single component, 500 lines
- Hooks state management
- High risk
Steps:
1. [ ] Identify logical sections (useEffect, handlers, render)
2. [ ] Write tests for the component
3. [ ] Extract one logical piece at a time
4. [ ] Create child component
5. [ ] Move related state/logic to child
6. [ ] Verify parent still works
7. [ ] Repeat until done
Analysis:
- Shared by 20+ files
- Low test coverage (30%)
- HIGH RISK
Steps:
1. [ ] MUST increase test coverage first
2. [ ] Write tests for all known use cases
3. [ ] Consider if change is truly necessary
4. [ ] If yes, make smallest possible change
5. [ ] Update all consumers
6. [ ] Run full test suite
7. [ ] Mandatory peer review
If you don't have tests, you can't refactor safely. Tests prove behavior is preserved.
The smaller the change, the easier to verify. Large refactors are just many small ones queued up.
Difficult-to-test code usually has design problems. Fix the design, then refactor.
Separate commits for refactoring and feature changes. This makes rollback easier and review clearer.
The cost of tests is low. The cost of breaking production is high.
refactor — The refactoring judgment this builds upondoublecheck — Verification patternstest-think — Test coverage decisionspre-commit-review — Pre-commit verificationdebug-session — If something goes wrong during refactor