Skip to content
Back to skills

Data Migration

ASecurity

Hard gate before any database schema change or data transformation in production. Requires a written migration plan with rollback strategy, dry run, and verification steps before execution. Prevents data loss, downtime, and irreversible schema changes.

  • 24 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added October 16, 2026
ai-agentsgobashsqldatabase

Security analysis

A100/100

Scanned October 16, 2026

npx -y skills add RBraga01/a-team --skill data-migration --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Data Migration?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Data Migration
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/rbraga01-data-migration/badge)](https://www.skillsdirectory.com/skills/rbraga01-data-migration)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: data-migration
description: Hard gate before any database schema change or data transformation in production. Requires a written migration plan with rollback strategy, dry run, and verification steps before execution. Prevents data loss, downtime, and irreversible schema changes.
---

# Data Migration

## The Law

```
EVERY MIGRATION NEEDS A ROLLBACK.
A migration without a rollback plan is a one-way door.
Before you open it, know how to close it.
```

## When to Use

Use this skill before:
- Any `ALTER TABLE` in production
- Any column rename, type change, or removal
- Any large `UPDATE` or `DELETE` (more than 1000 rows)
- Any index creation on a large table
- Any data backfill or transformation script

## The Migration Process

### Step 1: Classify the Migration

| Type | Risk | Strategy |
|------|------|---------|
| Additive (new table, new nullable column) | Low | Single deploy |
| Non-breaking change (add index, add constraint) | Low–Medium | Verify impact first |
| Column rename or type change | High | Multi-phase deploy |
| Column or table removal | High | Multi-phase deploy |
| Large data backfill (>100k rows) | Medium | Batched, off-peak |
| Destructive (DROP TABLE, DELETE) | Critical | Backup + explicit approval |

### Step 2: Write the Migration Plan

Save to `docs/migrations/YYYY-MM-DD-description.md`:

```markdown
# Migration: [Title]

## Summary
[What changes and why]

## Risk Level
[Low / Medium / High / Critical]

## Migration Steps

### UP (apply)
\```sql
-- Step 1: Add new column (nullable — safe, no lock)
ALTER TABLE orders ADD COLUMN discount_amount NUMERIC(10,2);

-- Step 2: Backfill (batched — see script)
-- Run: scripts/migrations/backfill-discount.sql

-- Step 3: Add NOT NULL constraint after backfill
ALTER TABLE orders ALTER COLUMN discount_amount SET NOT NULL;
\```

### DOWN (rollback)
\```sql
ALTER TABLE orders DROP COLUMN IF EXISTS discount_amount;
\```

## Rollback Plan
[How to revert if something goes wrong — step by step]

## Estimated Duration
[How long will the migration take? Any locking risk?]

## Verification
- [ ] Row count before: SELECT COUNT(*) FROM orders;
- [ ] Row count after matches
- [ ] Sample rows spot-checked: SELECT * FROM orders LIMIT 10;
- [ ] Application queries tested against migrated schema
- [ ] No errors in application logs after deploy
```

### Step 3: Zero-Downtime Patterns

**Column rename** — never rename directly; multi-phase:
```
Phase 1: Add new column (old and new both exist)
Phase 2: Deploy app that writes to BOTH columns, reads from old
Phase 3: Backfill new column from old
Phase 4: Deploy app that writes to BOTH, reads from new
Phase 5: Drop old column
```

**Type change** — same multi-phase approach; never `ALTER COLUMN TYPE` on large tables in production directly.

**Index creation on large table** — always `CREATE INDEX CONCURRENTLY`:
```sql
-- [INVALID] — takes full table lock
CREATE INDEX idx_orders_user ON orders(user_id);

-- [VALID] — non-blocking on PostgreSQL
CREATE INDEX CONCURRENTLY idx_orders_user ON orders(user_id);
```

**Large backfill** — never in one transaction; batch it:
```sql
-- Process in batches of 1000 rows until done
DO $$
DECLARE
  batch_size INT := 1000;
  rows_updated INT;
BEGIN
  LOOP
    UPDATE orders
    SET discount_amount = 0
    WHERE discount_amount IS NULL
    LIMIT batch_size;

    GET DIAGNOSTICS rows_updated = ROW_COUNT;
    EXIT WHEN rows_updated = 0;
    PERFORM pg_sleep(0.1);  -- yield between batches
  END LOOP;
END $$;
```

### Step 4: Pre-Migration Checklist

Before executing in production:

- [ ] Migration tested in staging with production-sized dataset
- [ ] DOWN migration tested — rollback actually works
- [ ] Row counts recorded (before state documented)
- [ ] Backup taken or verified recent backup exists
- [ ] Maintenance window announced if locking is expected
- [ ] Application can tolerate both old and new schema simultaneously (for multi-phase migrations)
- [ ] Monitoring alerts active during migration

### Step 5: Execute

```bash
# Dry run first (PostgreSQL)
psql $DATABASE_URL --dry-run -f migration.sql

# Real run with timing
psql $DATABASE_URL -f migration.sql --echo-all

# Check for locks during execution
SELECT pid, query, state, wait_event_type, wait_event
FROM pg_stat_activity
WHERE state != 'idle';
```

### Step 6: Verify

```sql
-- Row count matches expectations
SELECT COUNT(*) FROM affected_table;

-- Sample spot-check
SELECT * FROM affected_table LIMIT 20;

-- No NULL where NOT NULL expected
SELECT COUNT(*) FROM affected_table WHERE new_column IS NULL;

-- No application errors in logs for 10 minutes post-migration
```

## Rollback Decision Tree

```
Migration complete?
  └── No errors in monitoring for 10 minutes? → DONE
  └── Errors found?
      ├── Can app tolerate the error state? → Fix forward (hotfix)
      └── Cannot tolerate? → ROLLBACK IMMEDIATELY
          ├── Run DOWN migration
          ├── Verify application recovers
          └── Document in post-mortem
```

## Hard Rules

- **Never DROP a column in the same deploy as the last code that reads it** — always separate by at least one deploy
- **Never run destructive migrations (DROP TABLE, DELETE) without an explicit human approval step** — not just a code review
- **Never skip the DOWN migration** — "we can recreate it" is not a rollback strategy
- **Never migrate in peak traffic hours** — schedule for the lowest-traffic window

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.