AI on legacy code: how to modernize a codebase older than your team
Jump to section
Paradoxically, AI is most useful where the code is worst. A legacy codebase without documentation, without tests, with tangled dependencies — this is exactly where AI saves the most time. And this is exactly where most teams don't use it, because 'the code is too complex for AI.'
It's the opposite. The more complex and less documented the code, the more value AI delivers. Here's a proven four-step process.
Step 1: Understanding — let AI map the terrain
'Explain what this module does. Draw the data flow from input to output. Identify all dependencies and side effects.' AI reads thousands of lines of code and creates an understandable description in minutes. Manual analysis would take days.
# Prompt for understanding a legacy module:
Read all files in src/billing/
I want to understand:
1. What this module does — main purpose
2. What data comes in and what comes out
3. Dependencies on other modules
4. Side effects (DB writes, API calls)
5. Known problems — code smells, tech debt
Output as a Markdown document. Be concise.This is use case number one for AI on legacy code. A single prompt gives you an overview that would otherwise require hours of studying someone else's code. And unlike human analysis, AI doesn't skip the 'boring' parts.
Step 2: Safety net — generating missing tests
Legacy code typically has no tests. AI can generate them: 'Write unit tests for this module. Cover existing behavior — I don't want to change implementation, I want a safety net for future refactoring.' Tests as documentation of what the code does NOW.
Key point: tests for legacy code aren't meant to prove the code is correct. They're meant to detect changes in behavior. They're an alarm system — they tell you when refactoring broke something.
# Prompt for safety net test generation:
Write unit tests for src/billing/calculator.ts.
Rules:
- Cover ALL existing code paths
- Test CURRENT behavior (even if it might be buggy)
- Include edge cases: null inputs, empty arrays,
extreme values
- Name each test descriptively:
"should return X when Y"
- After writing, run tests and fix failures
These tests serve as a safety net for refactoring.
DO NOT change implementation, just test what it does.Typical result: 15-30 unit tests in 5 minutes. Manually, this would take hours. And now you have a safety net for the next step.
Step 3: Gradual refactoring — small, verifiable steps
'Extract this logic into a separate function. Run tests. Then rename variables to meaningful names. Run tests.' Small, verifiable steps. AI does the mechanical work, tests verify each step.
- Typical refactoring steps for AI:
- Extract function from long method (run tests)
- Rename variables to meaningful names (run tests)
- Add types to existing functions (run tests)
- Remove dead code and unused imports (run tests)
- Split large file into smaller modules (run tests)
- Replace magic numbers with constants (run tests)
# Prompt for gradual refactoring:
Refactor src/billing/calculator.ts:
1. Extract function calculateDiscount from lines 145-210
2. Run tests
3. Rename variables: d -> discount, t -> total,
u -> user
4. Run tests
5. Add TypeScript types to all parameters
6. Run tests
After each step ALWAYS run tests.
If anything fails, fix it before the next step.
DO NOT do more than one step at a time.Step 4: Documentation — capture what you learned
During refactoring, you understood how the module works. This is the ideal time to have AI write documentation: 'Based on our work, write documentation for this module. Include: what it does, how to use it, known limitations, and decisions we made during refactoring.'
What not to delegate to AI
Decisions about WHAT to refactor and in what order. Database migrations. Public API changes. Anything where a mistake means a production incident. For this you need a senior who knows the system. AI is a tool in their hands, not a replacement.
- Delegate to AI: code comprehension, test generation, mechanical refactoring, documentation
- Don't delegate to AI: strategic decisions about priority, DB migrations, public API changes, security-critical sections
- Golden rule: AI handles mechanics, you handle strategy
Real results
On one project, I used this process to refactor an 8,000-line billing module in 3 days. Without AI, it would have taken 2-3 weeks. The key was: AI generated 47 unit tests (step 2) that served as a safety net for every subsequent step. Not a single test failed after refactoring — the module's behavior remained identical.
Legacy code isn't the problem. Legacy code without tests is the problem. AI helps you generate tests faster than anything else — and that transforms dangerous refactoring into safe refactoring.
Want to go deeper? Check out our full course AI for Legacy Codebases at /en/courses/ai-legacy-code
Karel Čech
Developer and AI consultant. I help technical teams adopt AI in their daily workflow — from workshops to long-term strategies.
LinkedIn →Stay ahead with AI insights
Practical tips on AI for dev teams. No spam, unsubscribe anytime.
Liked this post? Dive deeper with our course:
Related posts
AI-assisted refactoring: how to safely change code everyone's afraid of
Every codebase has that file. 2000 lines, no tests, whoever wrote it no longer works here. AI is the ideal tool for this — but only with the right process.
AI for the whole team: shared workspaces, collective agents, and team workflows
Every developer prompts on their own. That's wasteful. AI is much more powerful when the team uses it in coordination — here's how.
Cloud agents in practice: Devin, Codex, and when a cloud AI developer makes sense
Fully autonomous AI developers in the cloud promise a lot. But they handle only specific tasks well. Here's where they work, where they don't, and how to use them effectively.
Ready to start?
Free 30-minute consultation — we'll figure out where AI can level up your team the most.
Book a free consultation