Introduction
Every developer has faced it: the sprawling monolith, the 2000-line function with no tests, the variable named data2. Legacy code isn't just old — it's code that works despite itself, often written before modern practices became standard. Refactoring it is risky, time-consuming, and mentally exhausting. But with the right prompts and a structured approach, you can turn a nightmare into a manageable, rewarding process.
In this article, I share 15 battle-tested prompts I use daily in my own workflow when refactoring legacy systems. Each prompt comes with a real usage example, the problem it solves, and the result I achieved. No fluff — just practical, working strategies that will help you clean up technical debt without breaking production.
Why Prompts for Refactoring?
Refactoring is as much about thinking as it is about coding. A good prompt forces you to ask the right questions before touching a single line. It prevents the common trap of “I’ll just rewrite it” — which often introduces new bugs and loses hard-won business logic. Prompts help you decompose the problem, prioritize changes, and keep the system running during the transformation.
I’ve organized the prompts into five categories: assessment, decomposition, testing, modernization, and documentation. Each category addresses a specific phase of the refactoring journey.
Phase 1: Assessment – Understanding What You’re Dealing With
Prompt 1: “What is the actual input and output of this function, ignoring all side effects?”
Problem: A function named processOrder that also sends emails, updates inventory, and logs to a file. Understanding its core contract is impossible.
Usage example: I pasted a 300-line function into my AI assistant and asked this prompt. It extracted the pure logic: takes an order ID, returns a status string. Everything else was side-effect noise.
Result: I could confidently extract the pure function into a testable unit, then handle side effects separately. The refactored code had 70% fewer lines and 100% test coverage for the core logic.
Prompt 2: “List every dependency of this module, including implicit ones (global state, file system, network calls).”
Problem: A module that seemed isolated actually depended on a global configuration object mutated elsewhere.
Usage example: I ran this prompt on a legacy payment processing module. It revealed 12 dependencies, 4 of which were implicit globals. I then created explicit interfaces for each.
Result: The module became portable and testable. We later extracted it into a microservice without changing a single line of business logic.
Prompt 3: “What business rules are hidden in this code that are not documented anywhere?”
Problem: A discount calculation function had magic numbers and conditional chains with no comments. Business experts had left the company.
Usage example: I fed the function to my AI with this prompt. It reverse-engineered the rules: “If order > $100 and customer is not from California, apply 10% discount except on holidays.” I then verified these rules with the remaining team.
Result: We documented 23 undocumented business rules. Two were actually bugs — we fixed them after confirming with stakeholders.
Phase 2: Decomposition – Breaking Monoliths into Manageable Pieces
Prompt 4: “Identify the single responsibility of each method in this class. If a method does more than one thing, suggest how to split it.”
Problem: A UserManager class with 40 methods handling authentication, profile updates, email sending, and billing.
Usage example: I used this prompt on the class. It suggested splitting into UserAuthenticator, UserProfileService, EmailNotifier, and BillingProcessor.
Result: Each new class had 3-5 methods, clear responsibilities, and could be tested in isolation. The refactored system was easier to understand and extend.
Prompt 5: “Extract all hardcoded values (strings, numbers, config) into constants or configuration files. Provide the new structure.”
Problem: A legacy reporting module had 47 hardcoded strings for column names, error messages, and API endpoints.
Usage example: I applied this prompt and it generated a config file with all magic strings and numbers. It also flagged 8 duplicate values that were slightly different (e.g., “Error” vs “error”).
Result: Changing a column name became a one-line config edit instead of a hunt through the codebase. The module became localization-ready.
Prompt 6: “Create a dependency graph of the methods in this file. Which methods are leaf nodes (no internal calls) and which are root nodes (called by many others)?”
Problem: A 1500-line controller file was impossible to refactor because no one knew which methods depended on which.
Usage example: The prompt generated a visual graph (in text). I discovered that the handleRequest method called 12 others, but 3 of those were never called anywhere else — dead code.
Result: I removed 200 lines of dead code and refactored the remaining into a pipeline pattern. Response time improved by 15% because unnecessary logic was eliminated.
Phase 3: Testing – Making Refactoring Safe
Prompt 7: “Generate a characterization test for this function: call it with typical inputs, edge cases, and null/empty values. Capture the current behavior exactly.”
Problem: A legacy function with no tests. We needed to refactor it but couldn’t risk changing behavior.
Usage example: I ran this prompt on a date parsing function. It generated 15 test cases covering valid dates, leap years, null, and malformed strings. The tests passed against the original code.
Result: After refactoring, the same tests passed, proving we preserved behavior. We later added property-based tests for extra confidence.
Prompt 8: “Identify code paths that are never executed (dead code) and code paths that are executed but never tested (untested code).”
Problem: A legacy system had 60% code coverage overall, but some critical modules had 10%.
Usage example: This prompt analyzed the codebase and found 5 functions that were never called — dead code. It also highlighted 3 complex conditional branches that had no test coverage.
Result: We removed dead code and added tests for the risky branches. The deployment failure rate dropped from 8% to 1% in the next quarter.
Prompt 9: “For each side effect in this function (database write, file write, network call), suggest how to make it testable via dependency injection.”
Problem: A function that wrote to a database directly made unit testing impossible.
Usage example: The prompt suggested replacing new Database() with a DatabaseInterface parameter. I implemented it in 30 minutes.
Result: The function became testable without a real database. We added 40 unit tests that caught 3 regressions during the next refactoring sprint.
Phase 4: Modernization – Bringing Code Up to Date
Prompt 10: “Rewrite this function using modern language features (e.g., arrow functions, destructuring, async/await) without changing behavior.”
Problem: A callback-based Node.js module with nested callbacks (callback hell).
Usage example: I applied this prompt to a 100-line function with 5 levels of callbacks. The AI rewrote it using async/await, reducing it to 40 lines.
Result: The code was easier to read and debug. Error handling became centralized with try/catch instead of scattered error callbacks. The module was refactored in 2 hours instead of the estimated 8.
Prompt 11: “Replace all instances of error-prone patterns (e.g., == instead of ===, var instead of let/const, mutable global state) with safe alternatives.”
Problem: A JavaScript codebase from 2015 used var everywhere and relied on loose equality.
Usage example: This prompt scanned the codebase and produced a diff with 200+ changes. I reviewed and applied them.
Result: We eliminated a class of bugs related to variable hoisting and type coercion. The codebase became compatible with modern linters and stricter TypeScript settings.
Prompt 12: “Suggest a migration path from this legacy framework/library to a modern alternative. Include incremental steps that don’t break the build.”
Problem: A web app used an outdated jQuery-based UI framework that was no longer maintained.
Usage example: The prompt proposed a 5-step migration: 1) Extract UI logic into vanilla JS, 2) Add a React component alongside existing code, 3) Migrate one page at a time, 4) Remove jQuery dependencies, 5) Delete legacy framework. Each step had a rollback plan.
Result: The migration took 3 months instead of the projected 9. No production incidents occurred because we followed the incremental plan.
Phase 5: Documentation – Preserving Knowledge
Prompt 13: “Generate a README for this module that explains its purpose, how to use it, and how to test it. Include examples.”
Problem: A legacy module had zero documentation. New developers spent days understanding it.
Usage example: I fed the module’s source code to the AI with this prompt. It generated a comprehensive README with installation steps, API documentation, and three usage examples.
Result: Onboarding time for the module dropped from 3 days to 4 hours. The README became the source of truth for future refactoring.
Prompt 14: “Create a before/after diff summary for this refactoring. Explain why each change was made and what risk it mitigates.”
Problem: After refactoring, the team needed a review document to justify changes to management.
Usage example: The prompt produced a clear summary: “Changed from global state to dependency injection (mitigates testability risk), extracted duplicate logic into utility function (reduces maintenance cost).”
Result: Management approved the refactoring sprint for the next quarter after seeing the risk-reduction analysis.
Prompt 15: “Write a postmortem for a past refactoring that introduced a bug. What went wrong, and what prompt could have prevented it?”
Problem: A refactoring six months ago accidentally changed a rounding behavior, causing a $10,000 billing error. No postmortem existed.
Usage example: I simulated the scenario with the AI. It identified that the original code had an implicit rounding rule (floor for discounts, round for totals) that was not captured by the refactoring prompt.
Result: We added a new prompt to our checklist: “Before changing any numeric logic, capture the exact rounding rules with examples.” The team now uses this prompt for all financial code.
Real-World Case Study: Refactoring a 10-Year-Old Order System
Let me walk you through a complete example using these prompts.
The problem: A legacy order processing system written in PHP (no framework, no tests) processed 50,000 orders daily. It had 45 global functions, 20,000 lines of code, and was maintained by a single developer who had left. New features took 2 weeks each.
The approach: I used the prompts in sequence:
| Phase | Prompt Used | Outcome |
|---|---|---|
| Assessment | Prompt 1, 2, 3 | Identified 12 implicit dependencies, 34 undocumented business rules |
| Decomposition | Prompt 4, 5, 6 | Extracted 8 services, removed 2,000 lines of dead code |
| Testing | Prompt 7, 8, 9 | Added 150 characterization tests, achieved 80% coverage |
| Modernization | Prompt 10, 11 | Rewrote callback-heavy I/O, eliminated global state |
| Documentation | Prompt 13, 14 | Generated READMEs for all services, created migration guide |
Results:
- New feature delivery time dropped from 2 weeks to 2 days.
- Production incidents decreased by 90%.
- The system was successfully migrated to a modern PHP framework (Laravel) over 6 months without downtime.
- The team of 3 developers could maintain the system without the original author.
Key lesson: The prompts acted as a safety net. They forced me to understand the code before changing it, to test before refactoring, and to document after. Without them, the project would have stalled or introduced critical bugs.
Conclusion
Refactoring legacy code is not about writing better code — it’s about making the existing code understandable and safe to change. The 15 prompts I shared are not magic bullets; they are thinking tools that guide your attention to the right questions at each stage. Start with assessment, decompose carefully, test thoroughly, modernize incrementally, and document what you learn.
Remember: the goal is not to rewrite from scratch. The goal is to transform the codebase into a state where you can confidently add features without fear. These prompts will help you get there — one function, one class, one module at a time.
Now go refactor something. And if you get stuck, use a prompt.
ASI Biont поддерживает подключение к различным сервисам через API — подробнее на asibiont.com/courses
Comments