with one click
write-safe-migrations
// Plans and executes safe database migrations with low-downtime patterns, verification, and rollback. Use when changing schema, backfilling data, adding constraints, or creating indexes in production.
// Plans and executes safe database migrations with low-downtime patterns, verification, and rollback. Use when changing schema, backfilling data, adding constraints, or creating indexes in production.
Audits database data quality (nulls, duplicates, orphans, invalid ranges) and produces a short findings report with remediation queries. Use when debugging data issues, validating migrations, or verifying analytics correctness.
Diagnoses and fixes slow database queries using explain plans, statistics, and targeted indexes or rewrites. Use when an endpoint is slow, a query regresses, cpu spikes, or timeouts appear.
Inspects database schema and metadata (tables, columns, indexes, constraints, relationships). Use when exploring an unfamiliar database, writing joins, debugging query behavior, or documenting schema.
| name | write-safe-migrations |
| description | Plans and executes safe database migrations with low-downtime patterns, verification, and rollback. Use when changing schema, backfilling data, adding constraints, or creating indexes in production. |
Default approach: expand → migrate → contract.
Goal: reduce lock time, keep rollback easy, and prove correctness with checks.
create index concurrentlyadd column:
ALTER TABLE public.your_table
ADD COLUMN new_col text;
backfill in batches (example using id ranges):
UPDATE public.your_table
SET new_col = <expression>
WHERE id >= :min_id AND id < :max_id
AND new_col IS NULL;
add index safely:
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_your_table_new_col
ON public.your_table (new_col);
set not null (do this only after backfill + verification):
ALTER TABLE public.your_table
ALTER COLUMN new_col SET NOT NULL;
add column:
ALTER TABLE your_table
ADD COLUMN new_col VARCHAR(255) NULL;
backfill in batches (example):
UPDATE your_table
SET new_col = <expression>
WHERE id BETWEEN ? AND ?
AND new_col IS NULL;
add index:
CREATE INDEX idx_your_table_new_col ON your_table (new_col);
null rate after backfill:
SELECT COUNT(*) AS null_count
FROM your_table
WHERE new_col IS NULL;
row count consistency (when moving data):
SELECT COUNT(*) FROM old_table;
SELECT COUNT(*) FROM new_table;
fk integrity (orphan check):
SELECT COUNT(*) AS orphan_count
FROM child c
LEFT JOIN parent p ON p.id = c.parent_id
WHERE c.parent_id IS NOT NULL AND p.id IS NULL;
## migration: [name]
### objective
- what changes:
- why:
### risks
- locks:
- long running work:
- replication lag:
### plan
- [ ] step 1:
- [ ] step 2:
- [ ] step 3:
### verification
- query checks:
- app checks:
### rollback
- exact rollback steps: