Problem: Refactoring Breaks Everything Across Files
You need to rename a function used in 30 files, update API endpoints across your codebase, or migrate to a new library. Traditional find-and-replace is too risky, and manual editing takes hours.
You'll learn:
- How Composer Mode manages multi-file context automatically
- Safe workflows for large-scale refactoring
- When to use Composer vs Chat mode
- Recovery strategies when AI makes mistakes
Time: 12 min | Level: Intermediate
Why Manual Multi-File Editing Fails
Traditional IDEs force you to:
- Manually track which files need changes
- Remember context when switching between files
- Hope find-and-replace catches all cases
- Manually verify nothing broke
Common failures:
- Missed edge cases in conditional imports
- Breaking changes in test files
- Type errors cascading across modules
- Git conflicts from simultaneous edits
Cursor's Composer Mode solves this by maintaining awareness of your entire codebase while making coordinated changes.
Solution
Step 1: Enable Composer Mode
# Open Cursor (install from cursor.sh if needed)
# Keyboard shortcut
Cmd+I (Mac) or Ctrl+I (Windows/Linux)
You'll see: A split-pane interface with:
- Left: Your file tree and editor
- Right: Composer chat panel
- Bottom: File selection pills showing context
Expected: Composer starts with files you have open already included in context.
Step 2: Add Files to Context Strategically
Composer Mode automatically includes:
- Currently open files
- Files referenced in your prompt
- Related files based on imports
Manually add files:
# In Composer, use @ to search files
@src/utils/api.ts @src/components/UserList.tsx
# Or reference by pattern
@**/*.test.ts # All test files
Why this works: Cursor's AI understands file relationships. Adding the entry point (like api.ts) often auto-includes dependents.
Best practice: Start with 5-10 core files. Composer will suggest adding more if needed.
Step 3: Write Specific Refactoring Instructions
# ✅ Good: Specific with constraints
Rename function `fetchUser` to `getUserById` across all files.
Update imports and add JSDoc. Don't change test assertions.
# ⌠Bad: Vague and risky
Update the user functions to be better
Template for large changes:
Task: [What to change]
Scope: [Which files/patterns]
Preserve: [What NOT to change]
Verify: [How to test]
Example:
Task: Migrate from axios to fetch API
Scope: All files in src/api/*.ts
Preserve: Error handling structure, retry logic
Verify: npm test && curl localhost:3000/health
If Composer asks questions: It needs more context. Add the files it requests or clarify constraints.
Step 4: Review Changes Before Applying
Composer shows a diff view for each file:
// src/api/users.ts
- export async function fetchUser(id: string) {
+ export async function getUserById(id: string) {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
Check for:
- Changed only what you asked for
- Didn't modify comments with old function name
- Updated imports in other files
- Test files reflect new names
Red flags:
- Removing error handling you didn't mention
- Changing unrelated formatting
- Adding dependencies you didn't request
Fix issues: Click "Edit" on specific file diffs to refine, or add constraint in Composer:
You removed the try-catch in api.ts. Keep all error handling.
Step 5: Apply and Verify
# Composer applies changes to working directory
# Immediately run tests
npm test
# Check TypeScript compilation
npm run type-check
# Git diff to see full scope
git diff --stat
You should see:
- All tests passing
- No new TypeScript errors
- Git shows only expected files changed
If tests fail:
- Type errors in 1-2 files: Use Composer to fix: "Fix TypeScript errors in @src/utils/api.ts"
- Many failures: Undo with
git checkout .and refine prompt with stricter constraints - Subtle bugs: Add failing test to context and ask Composer to fix
Step 6: Commit Atomically
# Composer changes are unstaged
git add -p # Review each hunk
# Separate logical changes
git commit -m "refactor: rename fetchUser to getUserById"
git commit -m "test: update assertions for getUserById"
Why this matters: If you need to revert, atomic commits let you undo just the breaking part.
Advanced Workflows
Migrate 50 Components to New API
# Composer prompt
Migrate all components in @src/components/**/*.tsx from:
- Old: useQuery hook from react-query
- New: useSWR from swr
Keep same loading/error states. Update imports.
Add @src/components/UserList.tsx as reference implementation.
Strategy:
- Add 1 completed migration as reference (
UserList.tsx) - Let Composer pattern-match across remaining files
- Review in batches of 10 files
Update Environment Variables Across Services
# Add config files to context
@apps/api/.env.example @apps/web/.env.example @docker-compose.yml
Rename POSTGRES_URL to DATABASE_URL everywhere.
Update TypeScript types in @packages/env/src/index.ts
Critical: Always include type definition files to ensure type safety.
Fix Security Vulnerability in 30 Dependencies
@package.json @pnpm-lock.yaml @**/package.json
Upgrade lodash from 4.17.19 to 4.17.21 in all workspaces.
Run pnpm install after. Show me the upgrade plan first.
Safety: Ask for plan before execution on dependency changes.
When NOT to Use Composer
Use regular Chat mode for:
- Exploratory questions ("How does auth work?")
- Single file changes under 50 lines
- Code review without modification
- Explaining existing code
Use manual editing for:
- Formatting-only changes (use Prettier)
- Find-and-replace of strings (use IDE)
- Reverting changes (use git)
Composer excels at:
- Cross-file refactoring (5+ files)
- API migrations
- Updating patterns across codebase
- Adding features that touch multiple layers
Verification Checklist
After any Composer session:
# 1. Type safety
npm run type-check
# or
tsc --noEmit
# 2. Tests
npm test
# 3. Linting
npm run lint
# 4. Build
npm run build
# 5. Git sanity check
git diff --stat
git diff --name-only | wc -l # Should match expected file count
Smoke test critical paths:
# Start dev server
npm run dev
# Test main user flows manually
# - Login
# - Core feature
# - Error states
Common Pitfalls and Fixes
"Composer Changed Too Much"
Problem: Asked to rename function, it refactored entire module structure.
Fix: Be more explicit:
Only rename the function. Don't restructure imports or
add new abstractions. Preserve all existing patterns.
"Some Files Weren't Updated"
Problem: Function still has old name in 5 files.
Solution: Composer missed files not in context.
# Find remaining references
grep -r "fetchUser" src/
# Add missed files explicitly
@src/legacy/user-service.ts @tests/integration/auth.test.ts
Update fetchUser to getUserById in these files too
"Changes Broke Tests"
Problem: Refactoring changed behavior, not just names.
Fix: Add test files to context upfront:
@src/api/users.ts @src/api/users.test.ts
Rename fetchUser to getUserById. Update test descriptions
but don't change assertions or mock data.
"Lost Track of What Changed"
Problem: Applied changes, now unsure what's different.
Prevention:
# Before using Composer
git commit -m "checkpoint before refactor"
# After Composer
git diff HEAD --stat # See what changed from checkpoint
What You Learned
- Composer Mode maintains multi-file context automatically
- Start with specific constraints to avoid over-refactoring
- Always verify with tests + type-check + git diff
- Add reference implementations for pattern matching
- Use atomic commits for easier rollback
Limitation: Composer works best with codebases under 500 files. Larger monorepos may need scoped workspaces.
When to upgrade workflow:
- If changing >100 files regularly: Add pre-commit hooks
- If team uses Composer: Establish review process for AI changes
- If critical system: Require manual review of all diffs
Real-World Example: Migrating Auth Library
# Actual Composer prompt that worked
Task: Migrate from passport.js to @auth/core
Scope: @src/auth/*.ts @src/middleware/auth.ts @src/routes/api/*.ts
Reference: @src/auth/new-auth-example.ts (completed migration)
Preserve:
- All existing route paths
- Session cookie names
- Error messages
- Rate limiting logic
Steps:
1. Update auth initialization in @src/auth/config.ts
2. Replace passport middleware with @auth/core adapters
3. Update route handlers to use new session API
4. Keep all existing tests passing - only update auth calls
Verify with: npm test && curl -X POST localhost:3000/api/login
Result: 23 files changed, 0 tests broken, deployed same day.
Key success factors:
- Included working example file
- Explicit preservation rules
- Clear verification steps
Troubleshooting Quick Reference
| Symptom | Likely Cause | Fix |
|---|---|---|
| Composer ignores files | Not in context | Add with @filename |
| Too many changes | Vague prompt | Add "Only change X, preserve Y" |
| Type errors | Missing type files | Add @**/*.d.ts to context |
| Tests fail | Behavior changed | Rollback, add tests to context, retry |
| Slow responses | Too many files | Split into batches of 10-15 |
| Lost changes | Forgot to apply | Check Composer panel for "Apply" button |
Tested on Cursor 0.43.0, TypeScript 5.5+, Node.js 22.x
Note: Cursor Composer uses Claude Sonnet 4.5 by default. For very large refactors (100+ files), consider splitting into multiple sessions to stay within context limits.