DEV Community

Atlas Whoff
Atlas Whoff

Posted on

AI-Assisted Refactoring: Characterize, Test, Then Change -- Without Breaking Things

Why Most Developers Fear Refactoring

Refactoring fails when there are no tests, unclear scope, or both.
The solution isn't less refactoring -- it's a disciplined approach.

Here's the AI-assisted refactoring workflow that actually works.

Step 1: Characterize Before You Change

Before touching anything, document what the code currently does:

Claude Code prompt:
"Read app/lib/pricing.ts and write a description of what each function does,
 including edge cases and assumptions. Don't change any code.
 This is documentation for a refactoring I'm about to do."
Enter fullscreen mode Exit fullscreen mode

This catches hidden assumptions that break when you refactor.

Step 2: Generate Tests First

"For the function calculatePrice() in app/lib/pricing.ts,
 generate comprehensive tests that cover:
 - Normal cases
 - Edge cases (zero, negative, maximum values)
 - Error conditions
 - All the branches I can see in the code

 Use Jest. Make the tests pass with the CURRENT implementation.
 We'll refactor after the tests are green."
Enter fullscreen mode Exit fullscreen mode

Step 3: Refactor in Small Steps

// Instead of rewriting everything at once:

// Step A: Extract constants (no logic change)
const BASE_RATE = 0.0299
const ENTERPRISE_MULTIPLIER = 0.85

// Step B: Extract helper function (no logic change)
function applyVolumeDiscount(subtotal: number, quantity: number): number {
  if (quantity >= 100) return subtotal * 0.9
  if (quantity >= 50) return subtotal * 0.95
  return subtotal
}

// Step C: Simplify main function using helpers
// Run tests after EACH step -- never accumulate untested changes
Enter fullscreen mode Exit fullscreen mode

The Claude Code Refactoring Prompt

"Refactor the following function. Requirements:
 - Do NOT change any behavior -- tests must still pass
 - Goals: reduce nesting, extract named helpers, improve readability
 - After each logical change, pause and tell me what you changed
 - If you're uncertain about behavior, ask instead of assuming

 [paste function]"
Enter fullscreen mode Exit fullscreen mode

Detecting Regressions Automatically

# Before refactoring: capture baseline
npm test -- --coverage > test-baseline.txt

# After each refactor step:
npm test
# If any test fails, stop and revert

# Compare coverage:
npm test -- --coverage
# Coverage should not decrease
Enter fullscreen mode Exit fullscreen mode

Refactoring Patterns That Pay Off

// 1. Early returns (eliminate nesting)
// Before:
function process(user: User) {
  if (user) {
    if (user.isActive) {
      if (user.plan === 'pro') {
        // 10 lines of logic
      }
    }
  }
}

// After:
function process(user: User) {
  if (!user) return
  if (!user.isActive) return
  if (user.plan !== 'pro') return
  // 10 lines of logic -- unindented, clear
}

// 2. Replace magic numbers with named constants
// Before: if (attempts > 3)
// After:  if (attempts > MAX_LOGIN_ATTEMPTS)

// 3. Extract complex conditions
// Before: if (user.plan === 'pro' && !user.suspended && user.credits > 0)
const canUseFeature = user.plan === 'pro' && !user.suspended && user.credits > 0
// After:  if (canUseFeature)

// 4. Replace switch with map (when logic is data)
// Before: switch (plan) { case 'free': return 10; case 'pro': return 100; }
const PLAN_LIMITS: Record<string, number> = { free: 10, pro: 100, enterprise: 10000 }
// After:  return PLAN_LIMITS[plan] ?? 0
Enter fullscreen mode Exit fullscreen mode

When to Stop Refactoring

Stop when:
- Tests pass
- Code is readable on first read
- No function is > 30 lines
- No nesting deeper than 3 levels

Don't keep refactoring because it 'could be cleaner'.
Every change is a risk. Stop when it's good enough.
Enter fullscreen mode Exit fullscreen mode

The /refactor Skill

The Ship Fast Skill Pack includes /refactor -- runs this exact workflow in Claude Code: characterize, test, refactor step-by-step, verify.

$49 one-time at whoffagents.com

Top comments (0)