원클릭으로
migration-safety
Safe database migrations in production — expand-and-contract, lock-safe DDL, timing estimation, rollback SQL.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Safe database migrations in production — expand-and-contract, lock-safe DDL, timing estimation, rollback SQL.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Production-grade GitHub Actions workflows — reusable workflows, OIDC cloud auth, caching, matrix builds, and environment protection rules. Use when the user creates, reviews, or debugs CI/CD pipelines in .github/workflows, or asks about GitHub Actions deployment, OIDC authentication, or workflow optimization.
Systematic diagnosis of Kubernetes pod failures — CrashLoopBackOff, OOMKilled, Pending, ImagePullBackOff, and service connectivity issues. Use when the user encounters pods not starting, container restart loops, scheduling failures, or service unreachability in a K8s cluster.
Implement distributed tracing with OpenTelemetry, Tempo/Jaeger — instrumentation, sampling, and trace-to-log correlation. Use when the user asks about distributed tracing, OpenTelemetry setup, span instrumentation, trace propagation, or connecting traces to logs and metrics.
Design reusable React components with compound patterns, controlled/uncontrolled hybrids, typed prop APIs, async state handling, and ARIA accessibility. Use when the user creates, refactors, or reviews React components, or mentions props, hooks, .tsx files, component APIs, or accessible UI patterns.
Apply STRIDE threat modeling to system designs, identify IDOR and authorization vulnerabilities, and build threat matrices for security reviews. Use when the user designs a new system, reviews an architecture, prepares for a security audit, or asks about common API vulnerabilities like IDOR or broken access control.
Secure CI/CD pipelines with keyless signing, OIDC federation, provenance attestations, policy enforcement, and hardened runners.
| name | migration-safety |
| type | skill |
| description | Safe database migrations in production — expand-and-contract, lock-safe DDL, timing estimation, rollback SQL. |
| related-rules | ["migration-runbook.md","backup-policy.md"] |
| allowed-tools | Read, Write, Edit, Bash |
Expertise: Expand-and-contract,
CREATE INDEX CONCURRENTLY, migration timing estimation, rollback planning.
When planning or executing a production database migration, estimating migration duration, or writing rollback SQL.
-- ❌ DANGEROUS: direct rename locks table and breaks old app version
ALTER TABLE orders RENAME COLUMN user_id TO customer_id;
-- ✅ SAFE: expand-and-contract over multiple deploys
-- Phase 1: EXPAND (add new column, keep old)
ALTER TABLE orders ADD COLUMN customer_id BIGINT;
-- Phase 2: DUAL-WRITE (app v2 writes to both; reads from customer_id)
-- (code change, no migration needed)
-- Phase 3: BACKFILL (run in small batches to avoid lock)
UPDATE orders SET customer_id = user_id
WHERE customer_id IS NULL
AND id BETWEEN <batch_start> AND <batch_end>;
-- Phase 4: CONTRACT (app v3 no longer uses user_id)
ALTER TABLE orders DROP COLUMN user_id;
-- ✅ Safe: CREATE INDEX CONCURRENTLY (no table lock)
CREATE INDEX CONCURRENTLY idx_orders_customer_id ON orders(customer_id);
-- If concurrent creation fails:
DROP INDEX CONCURRENTLY idx_orders_customer_id_invalid;
-- Then retry
-- ❌ Dangerous on large tables: full table lock
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
-- ✅ Safe: ADD COLUMN with no default (instant in PostgreSQL 11+)
ALTER TABLE orders ADD COLUMN processed_at TIMESTAMPTZ;
-- ❌ Dangerous: ADD COLUMN with DEFAULT rewrites all rows (pre-PG11) / blocks (PG11+)
ALTER TABLE orders ADD COLUMN processed_at TIMESTAMPTZ NOT NULL DEFAULT now();
-- ✅ Safe alternative: add nullable, backfill, add constraint
ALTER TABLE orders ADD COLUMN processed_at TIMESTAMPTZ;
UPDATE orders SET processed_at = created_at WHERE processed_at IS NULL;
ALTER TABLE orders ALTER COLUMN processed_at SET NOT NULL;
-- Estimate rows to process
SELECT reltuples::BIGINT AS estimated_rows
FROM pg_class
WHERE relname = 'orders';
-- Rough timing: ~100k rows/sec for simple UPDATE on indexed column
-- ~10k rows/sec for complex JOIN-based UPDATE
-- Always test on production-size staging first!
-- Watch migration progress (PostgreSQL 9.6+)
SELECT
phase,
blocks_done,
blocks_total,
round(100.0 * blocks_done / NULLIF(blocks_total, 0), 1) AS pct_done
FROM pg_stat_progress_create_index
WHERE relid = 'orders'::regclass;
SELECT
phase,
tuples_done,
tuples_total,
round(100.0 * tuples_done / NULLIF(tuples_total, 0), 1) AS pct_done
FROM pg_stat_progress_vacuum
WHERE relid = 'orders'::regclass;
# Never: UPDATE orders SET customer_id = user_id (locks all rows, huge transaction)
# Always: batch by primary key range
import psycopg2
BATCH_SIZE = 10_000
with psycopg2.connect(DSN) as conn:
with conn.cursor() as cur:
cur.execute("SELECT MIN(id), MAX(id) FROM orders WHERE customer_id IS NULL")
min_id, max_id = cur.fetchone()
batch_start = min_id
while batch_start <= max_id:
batch_end = batch_start + BATCH_SIZE
with conn.cursor() as cur:
cur.execute("""
UPDATE orders
SET customer_id = user_id
WHERE id >= %s AND id < %s AND customer_id IS NULL
""", (batch_start, batch_end))
conn.commit() # commit each batch — releases row locks
print(f"Backfilled {batch_start}–{batch_end}")
batch_start = batch_end
-- Every migration should have a paired rollback script
-- Migration (forward):
-- ALTER TABLE orders ADD COLUMN customer_id BIGINT;
-- CREATE INDEX CONCURRENTLY idx_orders_customer_id ON orders(customer_id);
-- Rollback:
DROP INDEX CONCURRENTLY IF EXISTS idx_orders_customer_id;
ALTER TABLE orders DROP COLUMN IF EXISTS customer_id;
# 1. Take snapshot backup
pgbackrest --stanza=main --type=full backup
pgbackrest --stanza=main info # verify backup completed
# 2. Check current lock activity (no long-running transactions)
psql -c "SELECT pid, now() - query_start AS duration, state, query
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC LIMIT 10;"
# 3. Set statement_timeout for migration session (prevent runaway)
psql -c "SET statement_timeout = '10min'; <migration_sql>"
# 4. Test rollback on staging first