Code Migration Prompt Templates

AI prompt templates for code migrations. Upgrade frameworks, languages, and dependencies safely.

Overview

Migrations are some of the riskiest work in software, you're changing foundations while the building stands. These prompts help you plan and execute migrations safely, whether upgrading frameworks, switching databases, or rewriting services. The key is incremental change with rollback plans at every step.

Best Practices

1

Specify exact versions: 'React 16 to 18' not just 'upgrade React', breaking changes vary by version

2

Include your current code patterns so migration advice fits your actual codebase

3

Describe your test coverage, migrations without tests are walking blindfolded

4

Mention production constraints: Can you have downtime? Can you run old and new in parallel?

5

Ask for an incremental plan, not a big-bang approach, smaller steps are safer

Prompt Templates

1. Framework Migration Plan

Create a migration plan for upgrading/changing frameworks:

**Current state:**
- Framework: [CURRENT FRAMEWORK AND VERSION]
- Codebase size: [FILES, COMPONENTS, ETC.]
- Dependencies: [KEY DEPENDENCIES THAT MIGHT BE AFFECTED]

**Target state:**
- Framework: [TARGET FRAMEWORK AND VERSION]
- Why migrating: [REASONS - security, features, support]

**Constraints:**
- Downtime tolerance: [NONE / MAINTENANCE WINDOW / etc.]
- Timeline: [DEADLINE IF ANY]
- Team capacity: [WHO CAN WORK ON THIS]
- Test coverage: [CURRENT COVERAGE LEVEL]

**Provide:**
1. Breaking changes to address
2. Migration strategy (incremental vs. big-bang)
3. Step-by-step plan with checkpoints
4. Rollback strategy at each step
5. Testing approach
6. Risk assessment
Current: React 16.8 with class components, 200 components, Redux for state
Target: React 18 with hooks, considering Redux Toolkit
Why: React 16 EOL, want concurrent features, new hires prefer hooks
Constraints: No downtime, 2 engineers part-time, 60% test coverage
Timeline: Complete within 6 months
## React 16.8 → React 18 Migration Plan

### Breaking Changes to Address

| Change | Impact | Components Affected |
|--------|--------|--------------------|
| `ReactDOM.render` → `createRoot` | High | 1 file (entry point) |
| Automatic batching | Medium | Components with multiple setState |
| Strict Mode double-render | Medium | Components with side effects in render |
| `componentWillMount` etc. removed | High | ~40 class components (estimate) |
| React 17 event delegation | Low | Should be transparent |

### Migration Strategy: Incremental (Recommended)

**Why not big-bang:**
- 200 components too risky to migrate at once
- 60% coverage means 40% untested code
- Part-time capacity = long elapsed time if blocked

### Phase 1: Foundation (Weeks 1-4)

**Step 1.1: Upgrade React 16.8 → 17**
```bash
npm install react@17 react-dom@17
```
- React 17 is a "stepping stone" release
- Minimal breaking changes, mostly warnings
- Run full test suite
- **Rollback:** `npm install react@16.8 react-dom@16.8`

**Step 1.2: Fix React 17 warnings**
- Update lifecycle methods (componentWillMount → componentDidMount)
- Address any console warnings
- **Checkpoint:** All tests pass, no console warnings

**Step 1.3: Add React 18 strict mode to ONE route**
```jsx
// Test strict mode on low-traffic page first
<StrictMode>
 <SettingsPage />
</StrictMode>
```
- Identify side effects that break with double-render
- Fix issues in contained scope

### Phase 2: React 18 Upgrade (Weeks 5-8)

**Step 2.1: Upgrade to React 18**
```bash
npm install react@18 react-dom@18
```

**Step 2.2: Update entry point**
```jsx
// Before
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));

// After
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
```

**Step 2.3: Expand StrictMode gradually**
- Add to more routes week by week
- Fix issues as they surface
- **Checkpoint:** Full app in StrictMode, tests pass

### Phase 3: Class → Hooks Migration (Weeks 9-20)

**Strategy:** Migrate incrementally, prioritize by:
1. Components with bugs/frequent changes (migration improves them)
2. Simple components (quick wins)
3. Complex components (save for last)

**Step 3.1: Create hooks migration pattern**
```jsx
// Wrapper pattern for incremental migration
function UserProfileWrapper(props) {
 // New hooks logic
 const user = useUser(props.userId);
 const [editing, setEditing] = useState(false);
 
 // Pass to existing class component
 return <UserProfileClass {...props} user={user} editing={editing} />;
}
```

**Step 3.2: Migrate 5-10 components per week**
- Start with leaf components (no children)
- Write tests before migrating if coverage is low
- PR per component for easy rollback

### Phase 4: Redux → Redux Toolkit (Weeks 21-24)

**Only after React 18 stable.**

```javascript
// Before (vanilla Redux)
const reducer = (state = initialState, action) => {
 switch (action.type) {
 case 'users/fetch':...
 }
};

// After (Redux Toolkit)
const usersSlice = createSlice({
 name: 'users',
 initialState,
 reducers: {
 fetch: (state, action) => {... }
 }
});
```

**Approach:** Migrate one slice at a time. Old and new Redux can coexist.

### Rollback Strategy

| Phase | Rollback Method | Time to Rollback |
|-------|-----------------|------------------|
| React 17 | npm install react@16.8 | 5 minutes |
| React 18 | npm install react@17 | 5 minutes |
| Component hooks | Revert PR | 2 minutes |
| Redux Toolkit | Revert slice PR | 2 minutes |

**Key:** Every change is a separate PR that can be reverted independently.

### Testing Approach

1. **Before each phase:** Run full test suite, note baseline
2. **During migration:** Test migrated components in isolation
3. **After each step:** Run full regression
4. **Manual testing:** Critical paths (checkout, signup) after each phase

**Coverage gap mitigation:**
- Write tests for components BEFORE migrating them
- Use migration as opportunity to improve coverage to 80%

### Risk Assessment

| Risk | Likelihood | Impact | Mitigation |
|------|------------|--------|------------|
| Breaking production | Medium | High | Feature flags, gradual rollout |
| Timeline slip | High | Medium | Phase 4 is optional, can defer |
| Team burnout | Medium | Medium | Rotate who does migration work |
| Third-party lib incompatible | Low | High | Check compatibility before starting |

When upgrading major framework versions, switching frameworks, or modernizing legacy code

  • Migrate through intermediate versions when possible, React 16→17→18 is safer than 16→18
  • Run old and new in parallel when possible (feature flags, gradual rollout)

2. Database Migration

Help me plan a database migration:

**Current database:**
- Type: [POSTGRESQL / MYSQL / MONGODB / etc.]
- Version: [CURRENT VERSION]
- Size: [DATA VOLUME]
- Schema: [KEY TABLES/COLLECTIONS]

**Target database:**
- Type: [TARGET DATABASE]
- Version: [TARGET VERSION]
- Why: [REASONS FOR MIGRATION]

**Application:**
- Language/Framework: [WHAT ACCESSES THE DB]
- ORM: [IF USING ONE]
- Query patterns: [OLTP / OLAP / MIXED]

**Constraints:**
- Downtime tolerance: [MAX ACCEPTABLE]
- Data loss tolerance: [NONE / SOME]
- Consistency requirements: [STRONG / EVENTUAL]

**Provide:**
1. Data mapping strategy
2. Migration approach (cutover vs. dual-write)
3. Step-by-step execution plan
4. Data validation strategy
5. Rollback plan
Current: MySQL 5.7, 500GB, e-commerce data (users, orders, products)
Target: PostgreSQL 15
Why: MySQL 5.7 EOL, want JSON support, better query planner
Application: Python/Django with Django ORM
Constraints: Max 1 hour downtime, zero data loss, strong consistency for orders
## MySQL 5.7 → PostgreSQL 15 Migration Plan

### Data Mapping Strategy

| MySQL | PostgreSQL | Notes |
|-------|------------|-------|
| `INT AUTO_INCREMENT` | `SERIAL` or `IDENTITY` | Use `IDENTITY` (SQL standard) |
| `DATETIME` | `TIMESTAMP` | Consider `TIMESTAMPTZ` for timezone |
| `TINYINT(1)` | `BOOLEAN` | Django handles this |
| `TEXT` | `TEXT` | Same |
| `JSON` | `JSONB` | Use JSONB for indexing |
| `ENUM` | `VARCHAR` + CHECK | Or create custom TYPE |
| `DOUBLE` | `DOUBLE PRECISION` | Same |

**Django ORM impact:** Minimal, Django abstracts most differences. Update `DATABASES` setting.

### Migration Approach: Dual-Write with Cutover

```
 Phase 1 Phase 2 Phase 3
 (Parallel) (Sync) (Cutover)
 
MySQL [Primary] ───────[Primary]─────── [Read-only] ──→ [Retired]
 │ │ 
 │ │ Sync 
 ▼ ▼ 
PostgreSQL [Shadow] ─────── [Shadow] ─────── [Primary]
```

### Execution Plan

#### Phase 1: Setup & Initial Sync (Week 1)

**Step 1.1: Provision PostgreSQL**
```bash
# AWS RDS PostgreSQL 15
aws rds create-db-instance \
 --db-instance-identifier prod-postgres \
 --db-instance-class db.r5.xlarge \
 --engine postgres \
 --engine-version 15
```

**Step 1.2: Schema migration**
```bash
# Generate PostgreSQL schema from Django models
python manage.py sqlmigrate --database=postgres > schema.sql

# Or use pgloader for direct conversion
pgloader mysql://user:pass@mysql-host/db \
 postgresql://user:pass@pg-host/db \
 --with "only schemas"
```

**Step 1.3: Initial data load**
```bash
# pgloader handles type conversion
pgloader mysql://user:pass@mysql-host/ecommerce \
 postgresql://user:pass@pg-host/ecommerce
```

**Duration:** ~2 hours for 500GB
**Checkpoint:** Row counts match between databases

#### Phase 2: Dual-Write Period (Week 2)

**Step 2.1: Enable dual-write in application**
```python
# settings.py
DATABASES = {
 'default': { # MySQL - still primary
 'ENGINE': 'django.db.backends.mysql'...
 },
 'postgres': { # PostgreSQL - shadow
 'ENGINE': 'django.db.backends.postgresql'...
 }
}

# Middleware or signal to dual-write
class DualWriteMiddleware:
 def process_response(self, request, response):
 if request.method in ('POST', 'PUT', 'DELETE'):
 sync_to_postgres() # Async task
 return response
```

**Step 2.2: Set up continuous replication**
```bash
# Use Debezium for CDC (Change Data Capture)
# Captures MySQL binlog, writes to PostgreSQL
```

**Step 2.3: Validate data consistency**
```python
# Daily validation job
def validate_sync():
 for model in [User, Order, Product]:
 mysql_count = model.objects.using('default').count()
 pg_count = model.objects.using('postgres').count()
 assert mysql_count == pg_count, f"{model} mismatch"
 
 # Sample record comparison
 sample = model.objects.using('default').order_by('?')[:100]
 for obj in sample:
 pg_obj = model.objects.using('postgres').get(pk=obj.pk)
 assert obj.__dict__ == pg_obj.__dict__
```

**Duration:** 1 week of dual-write to build confidence

#### Phase 3: Cutover (Week 3)

**Pre-cutover checklist:**
- [ ] Row counts match (automated check)
- [ ] Sample data validated
- [ ] PostgreSQL performance tested (run production queries)
- [ ] Application tested against PostgreSQL in staging
- [ ] Rollback procedure documented and tested

**Cutover procedure (1-hour maintenance window):**

```bash
# T-0: Announce maintenance
# T-5min: Enable read-only mode in app
# T-10min: Final sync (should be minimal)

# T-15min: Switch database config
# settings.py
DATABASES = {
 'default': { # NOW PostgreSQL
 'ENGINE': 'django.db.backends.postgresql'...
 },
}

# T-20min: Deploy config change
# T-25min: Validate application works
# T-30min: Disable read-only mode
# T-45min: Monitor for errors
# T-60min: Cutover complete or rollback decision
```

### Rollback Plan

**During dual-write:** Simply deploy config pointing back to MySQL

**After cutover (if within 1 hour):**
1. Enable read-only mode
2. Sync PostgreSQL changes back to MySQL (reverse CDC)
3. Switch config to MySQL
4. Resume

**After cutover (if >1 hour):**
- More complex, would need point-in-time recovery
- This is why we validate thoroughly before cutover

### Data Validation Strategy

| Check | When | Method |
|-------|------|--------|
| Row counts | Daily during dual-write | Automated job |
| Sample records | Daily | Compare 1000 random rows |
| Checksums | Pre-cutover | `pg_dump | md5sum` vs MySQL export |
| Application tests | Pre-cutover | Full test suite against PostgreSQL |
| Critical queries | Pre-cutover | Run top 20 slow queries, compare results |

When changing database systems, major version upgrades, or restructuring data storage

  • Always run dual-write longer than you think necessary, data consistency issues surface over time
  • Have a clear rollback deadline, beyond that point, forward is the only safe direction

3. Dependency Upgrade

Help me safely upgrade this dependency:

**Dependency:** [PACKAGE NAME]
**Current version:** [VERSION]
**Target version:** [VERSION]

**Why upgrading:**
- [REASON - security, features, compatibility]

**Usage in codebase:**
- [HOW IT'S USED - API calls, imports]
- [APPROXIMATE NUMBER OF USAGES]

**Related dependencies:**
- [OTHER PACKAGES THAT DEPEND ON THIS]

**Provide:**
1. Breaking changes between versions
2. Migration steps
3. Code changes needed (with examples)
4. Testing strategy
Dependency: axios
Current: 0.21.1
Target: 1.6.0
Why: Security vulnerability in 0.21.x
Usage: HTTP client for all API calls, ~150 usages across codebase
Related: axios-retry, axios-mock-adapter for tests
## Axios 0.21.1 → 1.6.0 Migration

### Breaking Changes

| Change | Impact | Affected Code |
|--------|--------|---------------|
| CommonJS/ESM changes | Medium | Import statements |
| `axios.defaults.headers.common` | Low | Global header setup |
| Response type narrowing | Medium | TypeScript users |
| Removed deprecated `maxRedirects: false` | Low | If using this option |
| New error structure | High | Error handling code |

### Detailed Changes & Fixes

#### 1. Import Changes (if using ESM)

```javascript
// Before (may still work but check)
import axios from 'axios';

// After (explicit for ESM)
import axios from 'axios';
// Or if issues:
import { default as axios } from 'axios';
```

**Check:** Run `npm ls axios` to see if any dependency needs older version.

#### 2. Error Handling Changes (Most Important)

```javascript
// Before (0.21.x)
try {
 await axios.get('/api/data');
} catch (error) {
 if (error.response) {
 // Server responded with error status
 console.log(error.response.data);
 } else if (error.request) {
 // Request made but no response
 console.log(error.request);
 } else {
 // Error setting up request
 console.log(error.message);
 }
}

// After (1.x) - use axios.isAxiosError for type guard
import axios, { AxiosError, isAxiosError } from 'axios';

try {
 await axios.get('/api/data');
} catch (error) {
 if (isAxiosError(error)) {
 // Properly typed as AxiosError
 if (error.response) {
 console.log(error.response.data);
 console.log(error.response.status);
 } else if (error.request) {
 console.log('No response received');
 } else {
 console.log(error.message);
 }
 // New in 1.x: error.code for specific error types
 if (error.code === 'ECONNABORTED') {
 console.log('Request timed out');
 }
 } else {
 // Non-Axios error
 throw error;
 }
}
```

#### 3. Global Defaults Changes

```javascript
// Before
axios.defaults.headers.common['Authorization'] = token;

// After (same, but be aware of new structure)
axios.defaults.headers.common['Authorization'] = token;
// Or using instance (recommended)
const api = axios.create({
 headers: {
 Authorization: token
 }
});
```

#### 4. TypeScript Response Typing

```typescript
// Before (looser typing)
const response = await axios.get('/users');
const users = response.data; // any

// After (stricter, better)
interface User {
 id: number;
 name: string;
}
const response = await axios.get<User[]>('/users');
const users = response.data; // User[]
```

### Migration Steps

**Step 1: Update related packages first**
```bash
npm install axios-retry@latest axios-mock-adapter@latest
# Check compatibility with axios 1.x
npm ls axios # Ensure no conflicts
```

**Step 2: Upgrade axios**
```bash
npm install axios@1.6.0
```

**Step 3: Fix type errors (if TypeScript)**
- Run `tsc --noEmit` to find type errors
- Add `isAxiosError()` guards to catch blocks

**Step 4: Search and update error handling**
```bash
# Find all axios error handling
grep -r "error.response" src/
grep -r "error.request" src/
```

**Step 5: Run tests**
```bash
npm test
```

### Testing Strategy

1. **Unit tests:** Should mostly pass (axios-mock-adapter compatible)
2. **Integration tests:** Run against real APIs in staging
3. **Manual testing:** Test error scenarios specifically
 - Network timeout
 - 4xx responses
 - 5xx responses
 - CORS errors

**Test error handling explicitly:**
```javascript
describe('API error handling', () => {
 it('handles 404 correctly', async () => {
 mock.onGet('/users/999').reply(404, { message: 'Not found' });
 
 await expect(getUser(999)).rejects.toMatchObject({
 response: { status: 404 }
 });
 });
 
 it('handles network errors', async () => {
 mock.onGet('/users').networkError();
 
 await expect(getUsers()).rejects.toMatchObject({
 code: 'ERR_NETWORK'
 });
 });
});
```

### Rollback

```bash
npm install axios@0.21.1 axios-retry@3.x axios-mock-adapter@1.x
```

Keep the old lock file for quick rollback.

When upgrading packages with breaking changes, security patches, or major version bumps

  • Read the CHANGELOG between your version and target, understand every breaking change
  • Upgrade one major package at a time, multiple simultaneous upgrades make debugging hard

4. API Version Migration

Help me migrate API consumers to a new version:

**API details:**
- Current version: [V1, V2, etc.]
- New version: [TARGET VERSION]
- Breaking changes: [LIST KEY CHANGES]

**Consumers:**
- Number of consumers: [COUNT]
- Consumer types: [INTERNAL / EXTERNAL / BOTH]
- Control over consumers: [FULL / PARTIAL / NONE]

**Changes:**
```
[DESCRIBE OR SHOW API CHANGES]
```

**Constraints:**
- Deprecation timeline: [HOW LONG TO SUPPORT OLD VERSION]
- Communication channels: [HOW YOU REACH CONSUMERS]

**Provide:**
1. Migration communication plan
2. Backward compatibility strategy
3. Consumer migration guide
4. Deprecation timeline
5. Monitoring strategy
API: Payment processing API
Current: v1 (REST)
New: v2 (added idempotency, changed response format)
Consumers: 50 external partners, varying technical capabilities
Changes: New required header, nested response objects, different error format
Constraints: 12-month support for v1, email + developer portal for communication
## API v1 → v2 Migration Plan

### Breaking Changes Summary

| Change | v1 | v2 | Migration Effort |
|--------|----|----|------------------|
| Idempotency header | Optional | Required | Low |
| Response format | Flat | Nested | Medium |
| Error format | `{error: string}` | `{error: {code, message}}` | Medium |
| Authentication | API key in query | API key in header | Low |

### Communication Plan

**Timeline:**
```
Month 0 : Announce v2, publish migration guide
Month 1-3 : Early adopter period (support priority for v2 migrants)
Month 6 : Deprecation warning headers on v1
Month 9 : v1 rate limits reduced, final migration push
Month 12 : v1 sunset
```

**Communication touchpoints:**

| When | Channel | Message |
|------|---------|--------|
| M0 | Email + Portal | v2 launch announcement, migration guide |
| M0 | Portal | Detailed changelog, code examples |
| M3 | Email | Migration progress check-in, offer support |
| M6 | Email + v1 response header | Deprecation warning, timeline reminder |
| M9 | Email | Final warning, list of non-migrated endpoints used |
| M11 | Direct outreach | Personal contact to stragglers |
| M12 | Email | v1 sunset confirmation |

### Backward Compatibility Strategy

**Option A: Adapter Pattern (Recommended)**

Run both versions, translate v1 requests to v2 internally:

```
Client → v1 endpoint → Adapter → v2 logic → Adapter → v1 response
```

```python
# v1 adapter
@app.post("/v1/payments")
async def create_payment_v1(request: V1PaymentRequest):
 # Transform to v2 format
 v2_request = V2PaymentRequest(
 amount=request.amount,
 currency=request.currency,
 idempotency_key=request.headers.get('Idempotency-Key') or str(uuid4()), # Generate if missing
 )
 
 # Call v2 logic
 v2_response = await create_payment_v2_internal(v2_request)
 
 # Transform response back to v1 format
 return V1PaymentResponse(
 id=v2_response.payment.id,
 status=v2_response.payment.status,
 # Flatten nested structure
 )
```

**Benefits:**
- Single source of truth (v2 logic)
- v1 bugs fixed automatically when v2 is fixed
- Easy to monitor v1 usage

### Consumer Migration Guide

**For developer portal:**

```markdown
# Migrating from v1 to v2

## Step 1: Update base URL
```diff
- https://api.example.com/v1/
+ https://api.example.com/v2/
```

## Step 2: Move API key to header
```diff
- GET /v1/payments?api_key=sk_xxx
+ GET /v2/payments
+ Authorization: Bearer sk_xxx
```

## Step 3: Add idempotency key (required for POST/PUT)
```diff
 POST /v2/payments
 Authorization: Bearer sk_xxx
+ Idempotency-Key: unique-request-id-123
```

## Step 4: Update response parsing
```javascript
// v1
const paymentId = response.id;
const status = response.status;

// v2
const paymentId = response.payment.id;
const status = response.payment.status;
```

## Step 5: Update error handling
```javascript
// v1
if (error.response.data.error) {
 console.log(error.response.data.error); // string
}

// v2
if (error.response.data.error) {
 console.log(error.response.data.error.code); // "CARD_DECLINED"
 console.log(error.response.data.error.message); // "The card was declined"
}
```
```

### Monitoring Strategy

**Track per consumer:**
- v1 vs v2 request ratio
- Last v1 request timestamp
- v1 endpoints still in use

**Dashboard metrics:**
```
┌─────────────────────────────────────────┐
│ v1 → v2 Migration Progress │
├─────────────────────────────────────────┤
│ Partners fully migrated: 23/50 (46%) │
│ v1 traffic (7-day): 125K requests │
│ v2 traffic (7-day): 890K requests │
│ Partners with v1 traffic: 27 │
└─────────────────────────────────────────┘
```

**Deprecation headers on v1:**
```http
HTTP/1.1 200 OK
Deprecation: true
Sunset: Sat, 01 Jan 2025 00:00:00 GMT
Link: <https://api.example.com/v2/payments>; rel="successor-version"
```

**Alerts:**
- Alert if partner increases v1 usage (regression)
- Weekly report of non-migrated partners to account managers
- Alert at M11 if any partner still using v1 exclusively

When releasing breaking API changes, deprecating old API versions, or managing external API consumers

  • 12 months is a reasonable deprecation timeline for external APIs, internal can be faster
  • Personal outreach to stragglers is more effective than more emails

Common Mistakes to Avoid

Big-bang migrations instead of incremental, any failure affects everything

Not running old and new in parallel, no safety net when issues arise

Underestimating the long tail of migration, the last 10% of consumers take 50% of the effort

Frequently Asked Questions

Migrations are some of the riskiest work in software, you're changing foundations while the building stands. These prompts help you plan and execute migrations safely, whether upgrading frameworks, switching databases, or rewriting services. The key is incremental change with rollback plans at every step.

Related Templates

Have your own prompt to optimize?