Module 8: Capstone Project — Migrating a Real Legacy Project
Migration Planning and Safety Nets
Migration Planning and Safety Nets
Capsule description
With the Architecture Map complete (capsule 02), you have the map: you know what's there, where it is, and what needs to change. This capsule teaches you to transform that map into an executable plan and to build the safety net (regression tests) that protects each change. It's the preparation capsule — when you finish, you'll be ready to execute with confidence in capsule 04.
There's a principle that separates this capsule from the previous ones: nothing is modified yet. The plan is designed, the tests are written, the git tags are placed. But the production code isn't touched. That discipline is the difference between a successful capstone project and a "we tried but something broke and we didn't know what".
By the end of the capsule, you'll have: (1) an executable MIGRATION_PLAN.md with phases, checkpoints and rollback, (2) a regression test suite that covers the current critical behavior, and (3) a pre-migration git tag that is your safe return point.
Why Plan + Safety Net Go Before Executing
It's tempting to start refactoring. The Architecture Map identified clear anti-patterns, you know what to change — why not start?
For the same reason a surgeon doesn't operate without anesthesia or a team without a checklist:
Without a plan + safety net:
→ You change code → something breaks
→ Which change broke what? Impossible to know without tests
→ Solution: revert EVERYTHING, rethink, waste time
→ Or worse: push the bug to production
With a plan + safety net:
→ Tests green BEFORE the change (baseline)
→ You change code → tests green → continue
→ If tests red → you KNOW which change caused what
→ Localized rollback in minutes
→ Team confidence intact
The plan + safety net isn't optional preparation — it's the only way to execute the migration without risk.
The Migration Plan
Structure of the plan
Based on the Architecture Map findings, design a plan with phases:
# Migration Plan: [Project]
## Scope
- [What's going to be improved]
- [What stays the same]
## Phases
### Phase 1: Safety Net (Tests)
- Write regression tests for the main endpoints/functions
- Goal: 70%+ coverage on critical code
- Checkpoint: all tests green
### Phase 2: Structure (Refactoring)
- Extract service layer (if it doesn't exist)
- Move modules to the correct directories
- Rename for consistency
- Checkpoint: tests green, improved structure
### Phase 3: Modernization
- Syntax modernization (f-strings, type hints, etc.)
- Pattern modernization (context managers, enums)
- Dead code removal
- Dependency updates
- Checkpoint: tests green, modernized code
### Phase 4: Documentation
- Architecture map "After"
- CHANGE_LOG
- Handoff notes
- Checkpoint: documentation complete
## Rollback Strategy
- Git tag before each phase
- If Phase N fails: revert to Phase N-1
- Tests as the success/failure criterion
## Estimated Time
- Phase 1: 30-45 min
- Phase 2: 45-60 min
- Phase 3: 30-45 min
- Phase 4: 20-30 min
- Total: 2-3 hours
Generating the plan with Claude Code
> "Based on the Architecture Map, generate a migration
plan for this project. Include:
1. Phases with the execution order
2. Checkpoints with a success criterion
3. Rollback strategy per phase
4. Time estimate
Prioritize: tests first, then structure,
then modernization, documentation last."
Safety Net: Regression Tests
Write tests BEFORE any change
> "Write complete regression tests for the project:
1. Test of each endpoint (happy path + error cases)
2. Test of the main business functions
3. Integration test of the main flow
The tests should pass with the CURRENT code.
Don't optimize or fix anything — capture the
behavior exactly as it is."
Verify the safety net
> "Run all the tests and report:
1. Total tests
2. Tests passing
3. Coverage percentage
4. Any test that fails (that's a bug in the test,
not in the code)"
Example Plan: A Real Case
To anchor the capsule, consider a typical Module 8 project (Flask, ~2K lines, 0 tests, mixed tech debt). The plan could look like this:
# Migration Plan: payments-legacy-app
## Scope
- ✅ Improve: structure (extract service layer), syntax modernization,
remove identified dead code
- ❌ Does NOT include: framework change, improvements to the business logic,
changes to the data model
- ❌ Remaining tech debt documented for the backlog (not in this project):
(1) refactoring of the notifications module,
(2) migration to async,
(3) introduction of feature flags
## Phases with Checkpoints
### Phase 1: Safety Net (45 min)
**Goal:** Regression tests that capture the current behavior
**Actions:**
- Tests for the 4 critical endpoints identified in the Architecture Map
- Tests for the 3 central business functions
- 1 integration test of the main flow (POST /orders end-to-end)
**Checkpoint:** all tests passing against the CURRENT code
### Phase 2: Structure Refactoring (60 min)
**Goal:** Extract a service layer, normalize the structure
**Actions (one commit each):**
- Extract `PaymentService` from `routes/payments.py`
- Extract `OrderService` from `routes/orders.py`
- Move helpers from `utils/payments_utils.py` to `services/payment_service.py`
- Rename inconsistent functions (snake_case)
**Checkpoint:** tests green after each commit
### Phase 3: Modernization (45 min)
**Goal:** Modernize syntax and patterns within the new structure
**Actions (one commit per type, M07 capsule 04):**
- Dead code removal (dynamically verified)
- f-strings throughout
- isinstance() instead of type() == X
- Context managers for file I/O
- Type hints on public functions
**Checkpoint:** tests green, module passes pyupgrade --py310-plus
### Phase 4: Documentation (30 min)
**Goal:** Produce the deliverables for handoff
**Actions:**
- Architecture Map AFTER (compare with BEFORE)
- CHANGE_LOG.md with each commit explained
- HANDOFF.md with remaining tech debt
- Before/after metrics
**Checkpoint:** the documentation covers the 4 module 8 deliverables
## Rollback Strategy
```bash
# Tags before each phase
git tag pre-migration # initial state
git tag post-phase-1-tests # after the safety net
git tag post-phase-2-structure # after refactoring
git tag post-phase-3-modernization # after modernization
# If something fails:
git reset --hard <tag> # rollback to the previous phase
Estimated Time
- Phase 1: 45 min
- Phase 2: 60 min
- Phase 3: 45 min
- Phase 4: 30 min
- Total: ~3 hours
"Done" Criteria
- ✅ Tests green
- ✅ Architecture Map AFTER documents the change
- ✅ CHANGE_LOG with before/after metrics
- ✅ Clean Git history (1 commit = 1 type of change)
- ✅ Remaining tech debt documented
This plan is **executable**: each phase has concrete actions, a "done" criterion, and rollback. Without this level of detail, executing is improvising.
---
## Common Traps in Planning + Safety Nets
### 1. "Starting to write tests without understanding the current behavior"
Regression tests capture **the current behavior**, not the ideal one. If the function has a bug, the test must reproduce that bug. If you "fix" the bug while writing the test, you're no longer capturing — you're changing. Capsule 04 is where changes can be made; here you only capture.
### 2. "100% coverage before starting"
Sweet spot: 70-80% on critical code (main paths, money path, auth). Aiming for 100% blocks the project and produces tests of little value for trivial cases. Better a solid 70% than a mediocre 100%.
### 3. "Tests green but with no real content"
`def test_function_exists(): assert function is not None` isn't a test. Each test must **exercise behavior** — a specific input, a verified output. If your test doesn't fail when you comment out the function's logic, it's not a useful test.
### 4. "A plan without a rollback strategy"
"If something fails, I'll revert" isn't a strategy. A strategy is: tags per phase + tests as the success criterion + rollback documented in under 5 minutes. Without this, a failure in phase 3 can destroy 2 hours of work.
### 5. "Underestimating the plan's time"
If your estimate is "2 hours", the plan probably takes 4. Multiply what you estimate by 1.5x and you can still overrun. Capsule 04 executes — and the capstone projects that "get stuck" typically get stuck because the plan underestimated the effort.
---
## Deliverable of This Capsule
1. **MIGRATION_PLAN.md** — A complete plan with phases, checkpoints, rollback (following the example structure above)
2. **tests/** — A regression test suite with 70%+ coverage on critical code
3. **Git tag:** `pre-migration` at the current state of the codebase
---
## Diagnosis: Is Your Plan + Safety Net Ready?
<details>
<summary>Question 1: Does your MIGRATION_PLAN.md have phases with a verifiable "done" criterion?</summary>
**If yes:** capsule 04 can execute.
**If no:** without a "verifiable done", you don't know when to end a phase. Rewrite.
</details>
<details>
<summary>Question 2: Do your regression tests pass against the current code WITHOUT modifying it?</summary>
**If yes:** the safety net is active.
**If you doubt:** run the tests. If they fail, the tests are wrong (they capture behavior that doesn't exist) — fix them before moving on.
</details>
<details>
<summary>Question 3: Did you document how to roll back each phase in under 5 minutes?</summary>
**If yes:** you're going in safely.
**If no:** "git reset to the last commit" isn't a phase rollback. You need specific tags per phase.
</details>
<details>
<summary>Question 4: Does your plan make explicit what tech debt you're NOT going to address?</summary>
**If yes:** bounded scope, clear expectations.
**If no:** you're going to feel pressured to address everything and the project grows out of control.
</details>
---
## Connection with the Next Capsule
**Capsule 04 (Execution)** takes the migration plan and executes it. The tests you wrote here are the safety net that verifies each change. Without a good plan + safety net in this capsule, the execution becomes risky improvisation.
**Capsule 05 (Documentation)** consumes the CHANGE_LOG that's being built from phase 1 — start the log from the first commit, not at the end.
---
## Troubleshooting
### The legacy code is hard to test
Use minimal mocking. If a function has hardcoded dependencies, monkey-patch only what's necessary. The goal is to capture behavior, not to write perfect tests.
### I can't reach 70% coverage
Focus on the most used paths. If the login endpoint has 10 paths, test the 3-4 most common. 70% of what matters > 100% of the trivial.
### The migration plan is too ambitious
Reduce the scope. Better to migrate 60% of the project with quality than 100% in a hurry. Document what isn't addressed as remaining tech debt in HANDOFF.md.
### The tests "capture" a bug in the original code
Document the bug in HANDOFF.md as "issue found, not addressed". Your test should stay green because it captures the current behavior (with the bug). Fixing the bug is **another project**, not this one.