Multi-File Editing
Strategies for making coordinated changes across multiple files safely and efficiently.
Quick Reference
| Strategy | Use When |
|---|---|
| Atomic | All changes must succeed together or none at all |
| Incremental | Changes can be applied progressively with validation |
| Staged | Large refactors needing review checkpoints |
| Order | Description |
|---|---|
| Interfaces first | Update types/interfaces before implementations |
| Contracts first | Change API contracts before consumers |
| Leaf-to-root | Start with files that have no dependents |
Core Principles
1. Understand the Dependency Graph
Before editing, map which files depend on which:
Types/Interfaces (edit first)
|
v
Service/Business Logic
|
v
API Routes/Controllers
|
v
Tests (edit last)
2. Edit in Dependency Order
Always edit in this order:
- Shared types and interfaces - Foundation for all other code
- Utility functions - Used by multiple files
- Core business logic - Services, repositories
- API layer - Routes, controllers, handlers
- Frontend components - Consumers of API
- Tests - Validate the changes
3. Validate at Each Step
After each file edit:
- TypeScript compiles without errors
- Related tests still pass
- No broken imports
Planning Workflow
Step 1: Identify All Affected Files
# Find files using the symbol you're changing
rg -l "SymbolName" --type ts
# Find import references
rg "from.*module-name" --type ts
- List all files that will need changes
- Identify the dependency order
- Note files that can be edited in parallel
Step 2: Create Change Plan
Document your change plan:
## Change Plan: Rename UserData to UserProfile
### Phase 1: Type Definitions
- [ ] src/types/user.ts - Rename interface
- [ ] src/types/index.ts - Update export
### Phase 2: Service Layer
- [ ] src/services/user.service.ts - Update function signatures
- [ ] src/repositories/user.repository.ts - Update return types
### Phase 3: API Layer
- [ ] src/routes/user.routes.ts - Update handlers
### Phase 4: Tests
- [ ] tests/user.service.test.ts - Update test data
- [ ] tests/user.routes.test.ts - Update assertions
Step 3: Execute in Order
- Complete each phase before moving to next
- Run type checker after each phase
- Commit at logical checkpoints
Common Scenarios
Scenario 1: Renaming a Type/Interface
Order of operations:
Edit type definition
// src/types/user.ts // Change: interface UserData -> interface UserProfileUpdate exports
// src/types/index.ts export { UserProfile } from './user';Update all imports (can be parallel)
// Each consuming file import { UserProfile } from '../types';Update usage in each file
Run validation
bun run typecheck bun test
Scenario 2: Adding a Required Field
Order of operations:
Update interface
interface User { id: string; email: string; createdAt: Date; // New required field }Update factory/builder functions
Update database schema (if applicable)
Update API handlers to include field
Update tests with new field
Validate
Scenario 3: Changing Function Signature
Order of operations:
Update function definition
Update all call sites - find with:
rg "functionName\(" --type tsUpdate tests
Validate
Validation Checklist
After completing multi-file changes:
-
bun run typecheckpasses -
bun testpasses - No console errors in browser (for frontend)
- API endpoints respond correctly
- No orphaned imports or exports
Reference Files
| File | Contents |
|---|---|
| STRATEGIES.md | Detailed strategies (atomic, incremental, staged) |
| PATTERNS.md | Common patterns for API changes, migrations |
| COORDINATION.md | Dependency ordering and validation techniques |
Quick Commands
# Find all files using a symbol
rg -l "SymbolName" --type ts
# Find all imports of a module
rg "from.*['\"](.*module-name)" --type ts
# Type check without emitting
bun run typecheck
# Run specific test file
bun test path/to/file.test.ts
# Find files modified in current session
git status --short
Anti-Patterns
| Avoid | Do Instead |
|---|---|
| Editing tests first | Edit source first, tests last |
| Random edit order | Follow dependency order |
| Skipping validation | Validate after each phase |
| Massive single commits | Commit at logical checkpoints |
| Ignoring type errors | Fix type errors before continuing |
When to Use Each Strategy
Use Atomic Changes When:
- Changes must all succeed or all fail
- Rollback would be complex
- Team is waiting on the feature
Use Incremental Changes When:
- Changes can be validated progressively
- You want to catch issues early
- Complex refactor with many files
Use Staged Changes When:
- Large refactor spanning days
- Need team review at checkpoints
- High-risk changes to critical code
See STRATEGIES.md for detailed guidance on each approach.