Claude
Skills
Sign in
Back

Systematic Type Migration

Included with Lifetime
$97 forever

Safe refactoring workflow for replacing old types with new type-safe implementations through integration-test-first, file-by-file migration with incremental verification

Code Review

What this skill does


# Systematic Type Migration

## Overview

When refactoring components to new type-safe implementations, use this systematic workflow to prevent "works in isolation but broken integration" bugs.

**Core principle:** Integration test FIRST → file-by-file migration → incremental verification → cleanup

## The Problem

**Recurring issue during major refactoring:**
- Old component definitions remain in the codebase
- Some files spawn entities with old types
- Other files query for new types
- Systems become disconnected (queries find zero entities)
- User functionality breaks despite all systems being "properly registered"

**Example:** After introducing type-safe `MovementState` enum, if `setup.rs` spawns entities with the old `MovementState` but `planning.rs` queries for the new `MovementState`, queries will silently fail.

## The Workflow

### Phase 1: Preparation

**Step 1: Document the change**
- Create work directory (e.g., `docs/work/YYYY-MM-DD-type-safe-X`)
- Document scope and goals

**Step 2: Identify all uses**
```bash
# Find all references to the type being replaced
grep -r "ComponentName" src/
rg "OldType" --type rust
```

**Step 3: Create integration test FIRST**
- Write test that exercises **full user flow** (not just isolated system behavior)
- Test should verify end-to-end functionality
- This test MUST pass before starting AND after completion

**Step 4: Run baseline tests**

- Run project test command
- Run project check command
- Capture baseline to compare against

### Phase 2: Implementation

**Step 1: Create new component**
- Implement in canonical location (e.g., `src/components/movement/states.rs`)
- Include all required derives

**Step 2: Do NOT delete old component yet**
- Keep both during migration
- Enables gradual migration without breaking everything
- Allows atomic commits per file

### Phase 3: Migration (Systematic, File-by-File)

For EACH file using the old component:

**Step 1: Update imports**
```rust
// Before
use old_module::OldType;

// After
use new_module::NewType;
```

**Step 2: Update type usage**
- Pattern matching if enum variants changed
- Entity spawning to use new type
- Queries to use new type

**Step 3: Test after each file**
```bash
cargo check  # Or language-specific quick check
```
- Catch type errors immediately
- Don't accumulate errors across files

**Step 4: Commit atomically**
```bash
git add path/to/file.rs
git commit -m "refactor: migrate FileX to new ComponentName"
```
- One commit per file or logical group
- Enables easy rollback if needed

**Common file locations to check:**
- Entity spawning files (e.g., `setup.rs`, `spawners.rs`)
- System logic files (business logic using the component)
- UI display files (rendering component state)
- Component definition files
- Integration test files

### Phase 4: Cleanup

**Step 1: Delete old component definition**
- Remove from original module
- Only after ALL references migrated

**Step 2: Remove obsolete imports**
```bash
# Find unused imports
cargo clippy -- -W unused_imports
```

**Step 3: Remove obsolete helper code**
- Builder functions
- Conversion utilities
- Deprecated APIs

**Step 4: Update exports**
- Remove old type from `mod.rs` public API
- Ensure new type properly exported

### Phase 5: Verification

**Step 1: Compile clean**
```bash
cargo check --all-targets
# Or language-specific equivalent
```

**Step 2: Run all tests**

Run project test command
- All unit tests must pass
- All integration tests must pass

**Step 3: Verify integration test passes**
- The test created in Phase 1 MUST pass
- This verifies end-to-end functionality

**Step 4: Run checks**

Run project check command
- Linting, formatting, type checking

**Step 5: Manual testing**
- Test actual user flows
- Verify UI updates correctly
- Check edge cases

### Phase 6: Documentation

**Step 1: Update pattern docs**
- If new pattern emerged, document it

**Step 2: Document in retrospective**
- Capture lessons learned in work directory
- Note any pitfalls encountered

**Step 3: Update project docs**
- If pattern is important, reference from CLAUDE.md or README

## Key Principles

| Principle | Rationale |
|-----------|-----------|
| **Integration test FIRST** | Prevents "works in parts, broken as whole" |
| **Keep both during migration** | Enables atomic commits per file |
| **File-by-file, not all-at-once** | Easier debugging, clear progress |
| **Incremental verification** | Catch errors immediately (5 min) vs batch (30+ min) |
| **Atomic commits** | Easy rollback if specific change breaks something |

## Prevention: Integration Tests

The best prevention is **integration tests that verify the full user flow**, not just isolated system behavior.

### What Makes a Good Integration Test

**Good integration test:**
```rust
#[test]
fn test_user_can_move_vehicle() {
    // Setup: Spawn entities with realistic component combinations
    let world = setup_test_world();
    let vehicle = spawn_vehicle_with_all_components(&mut world);

    // Act: Trigger user action (click → select → move)
    click_vehicle(&mut world, vehicle);
    issue_move_order(&mut world, target_position);

    // Assert: Verify end result, not internal state
    run_systems_until_complete(&mut world);
    assert!(vehicle_arrived_at_target(&world, vehicle));
}
```

**Bad integration test:**
```rust
#[test]
fn test_planning_system_queries() {
    // Only tests one system in isolation
    // Doesn't verify components are actually compatible
}
```

**Integration test should:**
- Spawn entities with ALL required components (like real usage)
- Exercise multiple systems together (full pipeline)
- Verify user-visible outcome, not internal state
- Use realistic component combinations

## Common Mistakes

### Mistake 1: Skipping Integration Test
**Problem:** Discover breakage during manual testing (too late)
**Solution:** Write integration test FIRST, watch it pass LAST

### Mistake 2: Big-Bang Migration
**Problem:** Migrate all files at once, giant debug session when it fails
**Solution:** File-by-file with `cargo check` after each

### Mistake 3: Deleting Old Type Too Early
**Problem:** Can't compile during migration, hard to debug
**Solution:** Keep both until migration complete

### Mistake 4: Batching Commits
**Problem:** Hard to identify which change broke tests
**Solution:** Atomic commits per file

### Mistake 5: Testing Only Units
**Problem:** Units pass, integration broken (components incompatible)
**Solution:** Integration test MUST exercise full user flow

## Example Workflow

```bash
# Phase 1: Preparation
mkdir -p docs/work/2025-10-23-type-safe-movement-state
grep -r "MovementState" src/ > docs/work/2025-10-23-type-safe-movement-state/references.txt
# Write integration test: tests/movement_integration.rs
# Run project test command to establish baseline

# Phase 2: Implementation
# Create src/components/movement/states.rs with new MovementState
# Keep old src/space/components.rs::MovementState

# Phase 3: Migration (file-by-file)
# File 1: src/space/systems/setup.rs
nvim src/space/systems/setup.rs  # Update import, spawning
cargo check  # Verify
git add src/space/systems/setup.rs
git commit -m "refactor: migrate setup.rs to new MovementState"

# File 2: src/space/systems/planning.rs
nvim src/space/systems/planning.rs  # Update import, queries
cargo check  # Verify
git add src/space/systems/planning.rs
git commit -m "refactor: migrate planning.rs to new MovementState"

# ... repeat for each file ...

# Phase 4: Cleanup
# Delete old MovementState from src/space/components.rs
git add src/space/components.rs
git commit -m "refactor: remove old MovementState definition"

# Phase 5: Verification
cargo check --all-targets
# Run project test command - integration test MUST pass
# Run project check command
# Manual testing

# Phase 6: Documentation
# Write docs/work/2025-10-23-type-safe-movement-state/summary.md
```

## Related Practices

**Before using this skill:**
- Read: `${CLAUDE_PLUGIN_ROOT}principles/develo

Related in Code Review