Refactoring prompts with type-safety locks
Migration plans, not migrations
When the change spans more than one file or more than one language version, the right move is to ask for a plan first — not the migrated code. A plan is cheap to review. Migrated code spread across files is expensive to review and even more expensive to back out.
Here's a small Python 2 to 3.11 case:
Give me a 5-step migration plan to move this small Python 2 script to Python 3.11. Each step is one bullet. Then list 3 specific lines from the script that will break, with exact line content and the Python 3 replacement.
Script:
# inventory.py def report(items): print "Found %d items" % len(items) for i, item in items.iteritems(): print "%s: %s" % (i, item) if __name__ == "__main__": raw = raw_input("Path: ") print "Reading", raw
Claude Sonnet 4.5 returned:
# 5-Step Migration Plan
- Update all `print` statements to use `print()` function syntax with parentheses
- Replace `raw_input()` with `input()` for user input
- Change dictionary `.iteritems()` method to `.items()` (which returns an iterator in Python 3)
- Test the script with Python 3.11 interpreter to verify functionality
- Update the shebang line (if present) to point to Python 3.11 and consider adding type hints for modernization
# 3 Specific Breaking Lines
Line 3:
- Python 2: `print "Found %d items" % len(items)`
- Python 3: `print("Found %d items" % len(items))`
Line 5:
- Python 2: `for i, item in items.iteritems():`
- Python 3: `for i, item in items.items():`
Line 9:
- Python 2: `raw = raw_input("Path: ")`
- Python 3: `raw = input("Path: ")`
Captured from Claude Sonnet 4.5 (claude-sonnet-4-5) on 2026-04-27. Re-runs may differ slightly.
The output is a one-page document you can use as a checklist. Each step is one PR if you want to break it up. Each "breaking line" is a verifiable claim — you can grep for iteritems and confirm the script has exactly one occurrence.
Now the actual migration becomes a separate prompt per step:
Apply step 2 from the plan: replace
raw_inputwithinputininventory.py. Output a unified diff. Do not change anything else.
Each step is a small prompt with a small diff. Each diff is reviewable in five seconds. If a step looks wrong, you skip it without losing the others.
The plan-first pattern works at any scale. For a small script, the plan is five steps. For a real migration (Python 2 to 3 across 50 files, or React class components to hooks across a frontend), the plan looks the same — phases instead of steps, and each phase becomes its own PR.
The 5-step migration plan, visualised as a timeline:
Python 2 → 3.11 migration timeline
All print statements get parens; one PR, easy to revert
Replace input-reading calls; smallest semantic shift
Dict iteration API change; touch every loop
Verify behaviour matches the Python 2 baseline
Add type hints, pin python_requires>=3.11
Three patterns to copy from this prompt:
| Pattern | What it does |
|---|---|
| Asking for both the plan AND specific breaking lines | Bridges abstract steps to verifiable claims |
| "Each step is one bullet" | Forces atomic changes, not paragraphs |
| "Exact line content and replacement" | Lets you grep before believing |
A useful safety: when the plan claims "X lines will break," run the grep. If the count is off, the plan is partially wrong, and you should iterate before applying any of it. The model is fast but not always right about what's in your codebase — it's only ever working from what you pasted.
Next up: how to make the model write your PR description for you. :::
Sign in to rate