How I use Claude Code to refactor legacy code — without breaking everything
Legacy code refactoring is one of the highest-risk, highest-reward activities in software development. One wrong move and you've broken a system that's been running for years. But with the right workflow, Claude Code becomes an extremely powerful refactoring partner.
Here's the exact workflow I use.
The problem with legacy refactoring
Legacy codebases are hard to refactor for several reasons:
- Hidden dependencies: Functions called from 20 different places
- No tests: You don't know what you broke until production fails
- Tribal knowledge: The original author left 3 years ago
- Coupling everywhere: Change one thing, break three others
Claude Code is excellent at untangling this — but sessions get long fast. When you're tracing dependencies across 50 files, you'll hit rate limits at the worst possible moment.
Step 1: Map before you touch anything
Before writing a single line of new code, I ask Claude to create a dependency map:
List every function in UserService.js. For each function:
1. What does it do (one sentence)
2. What other functions does it call
3. What functions call it
4. What data does it read/write
Don't suggest changes yet. Just map.
This creates a mental model Claude will reference throughout the session. It also reveals the blast radius of any change before you make it.
Step 2: Identify the seams
A "seam" is a place where you can change behavior without touching the calling code. I ask Claude to find them:
Looking at the dependency map, where are the natural seams in this codebase?
Specifically: where could we extract a function/class without changing any calling code?
Rank by: (1) safest to extract, (2) most valuable to extract
This gives you a prioritized refactoring roadmap.
Step 3: Write characterization tests first
Before touching legacy code, characterization tests capture current behavior:
For the calculatePrice() function, write characterization tests that:
1. Document what it currently does (not what it should do)
2. Cover edge cases visible in the code
3. Will fail if behavior changes unexpectedly
These tests are our safety net — not our spec.
This is where sessions get token-heavy. Claude needs to read the function, understand inputs/outputs, generate tests, and verify they pass. Long sessions = rate limit risk.
When that happens mid-refactor, I use SimplyLouie as a fallback API endpoint — same Claude model, ✌️$2/month, works with the standard ANTHROPIC_BASE_URL environment variable:
export ANTHROPIC_BASE_URL=https://simplylouie.com/api/v1
Step 4: Extract one seam at a time
Never try to refactor everything in one session. Extract one seam:
Extract the tax calculation logic from calculatePrice() into a new TaxCalculator class.
Requirements:
1. Don't change any behavior
2. Keep the existing calculatePrice() signature identical
3. The new class should be independently testable
4. Show me the diff so I can review before you write any files
Key insight: show me the diff first. Claude shows you what it wants to change before making changes. You catch mistakes before they happen.
Step 5: Verify nothing broke
After each extraction:
Run the characterization tests we wrote earlier.
If any fail, show me exactly what changed and why.
Don't continue until all tests pass.
This creates a tight feedback loop: extract → test → verify → next seam.
Step 6: Strangler fig pattern for large rewrites
For larger rewrites, the strangler fig pattern is safest:
I want to replace the old OrderProcessor with a new implementation.
Step 1: Create NewOrderProcessor alongside the old one (don't delete old)
Step 2: Route 5% of traffic to new implementation via feature flag
Step 3: Add comparison logging to verify both produce identical output
Step 4: Gradually increase to 100% once verified
Step 5: Delete old implementation only after 7 days at 100%
Start with Step 1 only.
Breaking it into explicit steps prevents Claude from jumping ahead and deleting things before you're ready.
Step 7: Document the new architecture
Once refactoring is complete:
Write a REFACTORING.md that explains:
1. What we changed and why
2. The new architecture diagram (ASCII)
3. How to extend the new design
4. What to watch out for (gotchas we discovered)
This becomes the tribal knowledge for the next developer.
The CLAUDE.md additions for legacy work
Add these to your project's CLAUDE.md:
## Legacy Code Guidelines
- Always map dependencies before touching legacy code
- Write characterization tests before any refactoring
- Extract one seam per session — don't try to refactor everything at once
- Show diffs before writing files
- Run tests after every extraction
- Use strangler fig pattern for large rewrites
Handling session length
Legacy refactoring sessions are some of the longest I run with Claude Code. You're reading 50+ files, generating tests, verifying behavior — easily 3-4 hours per major refactor.
When you hit rate limits mid-refactor:
- Save your progress notes (ask Claude to summarize the session state)
- Switch to backup endpoint
- Resume with the summary as context
For the backup endpoint, I use SimplyLouie — ✌️$2/month, no rate limit surprises, 7-day free trial. The ANTHROPIC_BASE_URL swap takes 30 seconds.
The complete workflow summary
- Map — dependency map before touching anything
- Seams — find safe extraction points
- Tests — characterization tests as safety net
- Extract — one seam at a time, diff first
- Verify — run tests after every extraction
- Strangler — for large rewrites, route gradually
- Document — REFACTORING.md for the next developer
Legacy code refactoring is where Claude Code really shines — it can hold the entire dependency map in context while methodically untangling years of accumulated complexity. The key is not rushing and verifying at every step.
Running Claude Code sessions for long refactors? SimplyLouie is a ✌️$2/month Claude API proxy — same model, no lockouts, works with your existing ANTHROPIC_BASE_URL config.
Top comments (0)