Module 8: Capstone Project — Migrating a Real Legacy Project

Execution: Refactoring, Modernization and Testing

Execution: Refactoring, Modernization and Testing

Capsule description

This is the execution capsule. With the plan ready (capsule 03) and the safety net in place, you execute Phases 2 and 3 of the migration plan: structure refactoring and code modernization. Each change is backed by tests. Each phase has a checkpoint.

This is the capsule where discipline matters more than speed. The temptation to "do everything faster" by combining changes or skipping tests is high — but it's exactly what distinguishes a project that finishes in 3 hours successfully from one that finishes in 8 hours with incidents. You apply everything learned in M04 (multi-file refactoring), M05 (migration where applicable), and M07 (incremental modernization) — but now under the pressure of a real capstone project.

By the end of the capsule, you'll have a codebase that's structurally refactored and modernized, with green tests at each commit, and a clean git history that documents each step. Capsule 05 takes this result and produces the professional documentation.


Why Discipline > Speed in This Capsule

Without discipline:
→ "I'll combine Phase 2 and Phase 3 to save time"
→ You change structure + modernize + update dependencies
→ Tests fail: what caused what? Impossible to know
→ Debugging becomes combinatorial
→ Total time: 6-8 hours

With discipline:
→ Phase 2 separate commits → tests green → continue
→ Phase 3 with one type of change per commit
→ If something fails, you know exactly what
→ Rollback to the previous commit, try another way
→ Total time: 2.5-3 hours

The paradox: going slower (disciplined) arrives sooner because you eliminate the combinatorial debugging. This is the central lesson from Module 7 (capsule 04: incremental vs big bang) applied to the capstone project.


Phase 2: Structure Refactoring

Executing with Claude Code

Follow your migration plan. Example of a typical sequence:

Step 1: Extract Service Layer

> "Extract the business logic from the route handlers
   into a service layer. Create src/services/ with one service
   per domain. The routes should only call the service
   and return the response. Run tests afterward."

Step 2: Move Modules

> "Reorganize the directory structure according to the plan:
   - Utils to src/utils/
   - Models to src/models/
   - Validators to src/validators/
   Update all the imports. Run tests."

Step 3: Rename for Consistency

> "Rename functions and files that don't follow the
   project's conventions. Apply consistent snake_case.
   Run tests."

Phase 2 Checkpoint:

> "Run ALL the tests. Verify that the structure
   is improved but the behavior is identical."

If tests pass → continue to Phase 3. If tests fail → debug and fix before continuing.


Phase 3: Modernization

Executing incrementally

Step 1: Dead Code

> "Remove dead code: unused imports, functions
   never called, unreferenced variables.
   Run tests."

Step 2: Syntax

> "Modernize syntax: f-strings, isinstance(),
   direct iteration (not range(len)).
   Run tests."

Step 3: Patterns

> "Modernize patterns: context managers for open(),
   specific exceptions for bare except.
   Run tests."

Step 4: Type Hints

> "Add type hints to all the public functions.
   Run tests."

Step 5: Dependencies (if applicable)

> "Update deprecated dependencies.
   Run tests."

Phase 3 Checkpoint:

> "Run ALL the tests. Produce a diff summary
   showing what changed in terms of lines, files,
   and structure."

The Execution Rhythm

For EACH step:
1. Announce what you're going to do
2. Execute the change with Claude Code
3. Run tests
4. If green → commit + next step
5. If red → debug + fix → commit + next step
6. NEVER move on with failing tests

Tracking Progress

Keep a real-time checklist:

## Execution Checklist

### Phase 2: Refactoring
- [x] Extract service layer (tests: ✅)
- [x] Move modules (tests: ✅)
- [x] Rename consistency (tests: ✅)
- [x] Phase 2 checkpoint (all tests: ✅)

### Phase 3: Modernization
- [x] Dead code removal (tests: ✅)
- [x] Syntax modernization (tests: ✅)
- [ ] Pattern modernization (tests: )
- [ ] Type hints (tests: )
- [ ] Phase 3 checkpoint (all tests: )

What to Do When Something Fails

The test failed after the refactoring

> "The test_create_order test fails after extracting
   OrderService. The error is: AttributeError 'dict' object
   has no attribute 'total'. What changed in the interface?"

The refactoring caused a circular import

> "Moving user_validator.py to validators/ created a circular
   import with user_service.py. How do I resolve this?"

A step took longer than estimated

Adjust the plan. If Phase 2 took 90 min instead of 60, reduce the scope of Phase 3 to stay within the total estimated time.


Common Traps in Execution

Five mistakes that appear specifically when executing the capstone project. Anticipate them.

1. "Combining steps to go faster"

Symptom: You did extract + move + rename + dead code in a single commit "to finish fast". Tests fail. You don't know which change broke what.

Why it happens: The human instinct to "consolidate work". But combining changes loses the atomicity that makes debugging fast.

How to fix: One type of change per commit. No exceptions. Module 7 capsule 04 develops why incremental wins — this is that lesson applied.

2. "Skipping tests between steps so as not to lose rhythm"

Symptom: You made 3 changes without running tests between them. The fourth change fails. The tests reveal that the problem is from change 1, not 4.

Why it happens: Confidence that "small changes don't break tests". But tests are cheap — they're the only objective signal that "the behavior was preserved".

How to fix: Tests after each commit. If the "test → commit → test → commit" cycle becomes tedious, automate it with a git hook.

3. "Fixing a bug found during the migration"

Symptom: While refactoring, you discover that calculate_tax has a mishandled edge case that's been there for months. You "fix" it while touching the file.

Why it happens: Optimism: "I'm already here, I'll fix it along the way". But now the migration mixes refactoring + bug fix. How do you verify that the tests still capture the behavior? You can't — because the behavior changed.

How to fix: Document the bug in HANDOFF.md as "issue found, not addressed". The bug fix is another project, not this one. This discipline is what separates migration (preserves) from rewrite (changes).

4. "A Claude Code session of 3+ hours"

Symptom: You're 3 hours into the same session. Claude Code starts to forget files, generate inconsistent code, repeat questions.

Why it happens: Context pressure (Module 6, capsule 02). Long sessions accumulate context that dilutes what matters.

How to fix: Sessions of at most 1 hour per phase. Between phases, start a new session loading only CLAUDE.md + the relevant files for the next phase. It's counterintuitive but faster.

5. "Modifying the plan in the middle of execution without documenting it"

Symptom: The plan said "extract 3 services". Halfway through you found a reason to extract only 2. You changed it without documenting. Later you don't know whether you were faithful to the plan or not.

Why it happens: Legitimate adaptation on discovering new information — but without traceability.

How to fix: Any change to the plan is documented in MIGRATION_PLAN.md with a reason. "Phase 2 adjusted: 2 services instead of 3 because PaymentService and RefundService share 80% of the logic — extracted as one." Traceability protects the integrity of the process.


Connection with the Next Capsule

Capsule 05 (Delivery) takes the refactored + modernized project and produces the final documentation: Architecture Map "After", CHANGE_LOG, HANDOFF.md, and before/after metrics.

Important: the CHANGE_LOG.md isn't written at the end — it's built during this capsule. Each commit you make is a log entry. If you leave the documentation for capsule 05, you're going to forget important details.

Incremental documentation during execution

# CHANGE_LOG.md (live during execution)

## Phase 2: Structure Refactoring

### Commit: extract PaymentService from routes/payments.py
- Moved: fee calculation, amount validation, charge logic
- Stayed in the route: only I/O (parse request, return response)
- Tests: ✅ 12/12

### Commit: extract OrderService from routes/orders.py
- ... (same format)

Filling in the CHANGE_LOG as you commit turns capsule 05 into consolidation, not creation.


Troubleshooting

Many tests fail after a change

Solution: The change was probably too large. Revert (git checkout) and split it into smaller steps. If you have a git tag of the previous phase, git reset --hard <tag> takes you to the safe point.

Claude Code loses context in the middle of execution

Solution: Start a new session with CLAUDE.md + the files you're modifying. Don't try to keep a 2-hour session going. Trap #4 develops why.

The refactoring revealed bugs that aren't part of the migration

Solution: Document the bugs as findings but do NOT fix them now. Migration preserves behavior — bug fixing is a separate project. Trap #3 develops why.

Phase 2 took much longer than estimated

Solution: Adjust the scope of Phase 3, not the project. Document the adjustment in MIGRATION_PLAN.md with a reason. The goal is to deliver a coherent project — not to complete the entire original plan.

You get stuck on a circular import after the refactoring

Solution: It's the most common trap when introducing a service layer. The solution isn't to reverse the move — it's to identify the shared dependency and extract it to a common module. Ask Claude Code: "the refactoring created this circular import. Diagnose the cause and propose a resolution without reverting the extract."


Closing the Capsule

By the end of the execution, you should have:

  • ✅ A clean git history with atomic commits (1 type of change = 1 commit)
  • ✅ Green tests at each commit (not "I'll fix it later")
  • ✅ MIGRATION_PLAN.md updated if there were adjustments with a documented reason
  • ✅ CHANGE_LOG.md built live during the execution
  • ✅ Git tags marking the end of each phase

If those 5 items are in place, capsule 05 (Documentation + Handoff) becomes consolidation, not creation. If they're missing, you're going to need to rebuild information you already had — and that weighs twice as much.

Next capsule: 05 — Delivery: Documentation + Handoff — where you produce the Architecture Map AFTER, before/after metrics, and the professional HANDOFF.md that closes the project.

Capsule 05 is the difference between "migrated code" (a technical result) and a "complete portfolio-worthy project" (a professional result). The same code in both cases — a different deliverable.