Initial commit
This commit is contained in:
290
skills/systematic-type-migration/SKILL.md
Normal file
290
skills/systematic-type-migration/SKILL.md
Normal file
@@ -0,0 +1,290 @@
|
||||
---
|
||||
name: Systematic Type Migration
|
||||
description: Safe refactoring workflow for replacing old types with new type-safe implementations through integration-test-first, file-by-file migration with incremental verification
|
||||
when_to_use: when refactoring to new type-safe implementations, replacing components across codebase, migrating to new domain types
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# 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/development.md` - Code structure principles
|
||||
- Read: `${CLAUDE_PLUGIN_ROOT}principles/testing.md` - Testing principles
|
||||
|
||||
**After migration:**
|
||||
- Use: `${CLAUDE_PLUGIN_ROOT}skills/requesting-code-review/SKILL.md` - Request code review
|
||||
|
||||
## Testing This Skill
|
||||
|
||||
See `test-scenarios.md` for pressure tests validating this workflow prevents integration breakage.
|
||||
Reference in New Issue
Block a user