Skip to content
Back to skills

Database Migrations

BSecurity

Guides safe, reversible PostgreSQL schema and data migrations using golang-migrate, Prisma, Drizzle, or Django migrations. Use when creating or altering tables, adding or removing columns or indexes, planning a zero-downtime schema change, or reviewing a migration before it runs against production. For tables partitioned by hotel_id, use the postgres-hotel-partitioning skill alongside this one.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 16, 2026
databasesgobashsqldjangogitdatabase

Security analysis

B85/100
  • highPerforms destructive filesystem operations

Pro scans all 2 files and shows the line behind each finding

Scanned October 16, 2026

npx -y skills add shennawardana23/skillme --skill database-migrations --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Database Migrations?

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

Security grade badge for Database Migrations
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/shennawardana23-database-migrations/badge)](https://www.skillsdirectory.com/skills/shennawardana23-database-migrations)

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: database-migrations
description: Guides safe, reversible PostgreSQL schema and data migrations using golang-migrate, Prisma, Drizzle, or Django migrations. Use when creating or altering tables, adding or removing columns or indexes, planning a zero-downtime schema change, or reviewing a migration before it runs against production. For tables partitioned by hotel_id, use the postgres-hotel-partitioning skill alongside this one.
license: Apache-2.0
metadata:
  version: "0.1.0"
---

# Database Migrations

Every schema change is a migration file, never a manual `psql` session
against production. Migrations are forward-only once deployed — a mistake
is fixed with a new migration, not by editing or deleting the one that ran.

## Core rules

1. Schema changes (DDL) and data backfills (DML) are **separate**
   migrations — mixing them makes a migration slow, lock-prone, and hard to
   roll back independently.
2. `NOT NULL` on a new column requires a default, or a nullable column
   followed by a backfill and a later constraint migration — adding
   `NOT NULL` with no default to an existing table locks it for a full
   rewrite.
3. Indexes on existing tables use `CREATE INDEX CONCURRENTLY` — a plain
   `CREATE INDEX` blocks writes for the duration of the build.
4. Test migrations against a production-sized copy, not a 100-row dev
   database — lock duration and rewrite cost scale with table size, and a
   migration that's instant at 100 rows can lock for minutes at 10M.

## Zero-downtime pattern (expand → migrate → contract)

```
EXPAND:   add new column/table (nullable or defaulted); app writes to BOTH old and new
MIGRATE:  backfill; app reads from NEW, still writes to BOTH; verify consistency
CONTRACT: app uses NEW only; drop the OLD column/table in a separate, later migration
```

Renaming a column in production always goes through this pattern — never a
direct `ALTER TABLE ... RENAME COLUMN` on a table any running application
code still references by the old name.

## Go (golang-migrate) — this org's default

```bash
migrate create -ext sql -dir migrations -seq add_reservation_notes
migrate -path migrations -database "$DATABASE_URL" up
migrate -path migrations -database "$DATABASE_URL" down 1
```

```sql
-- 000004_add_reservation_notes.up.sql
ALTER TABLE reservations ADD COLUMN notes TEXT;
CREATE INDEX CONCURRENTLY idx_reservations_notes ON reservations (notes) WHERE notes IS NOT NULL;

-- 000004_add_reservation_notes.down.sql
DROP INDEX IF EXISTS idx_reservations_notes;
ALTER TABLE reservations DROP COLUMN IF EXISTS notes;
```

## Gotchas

- `CREATE INDEX CONCURRENTLY` cannot run inside a transaction block — most
  migration tools wrap each migration in a transaction by default, so this
  needs explicit non-transactional handling (golang-migrate's `-x` flag
  behavior varies by driver; verify your tool's specific mechanism before
  relying on it).
- For any table partitioned by `hotel_id`, a migration that adds an index
  or constraint must apply it per-partition awareness — see
  `postgres-hotel-partitioning` for the specific patterns; a migration
  written as if the table were unpartitioned can silently miss partitions
  created after the migration ran.
- Large batch backfills should use `LIMIT` + `FOR UPDATE SKIP LOCKED` in a
  loop with periodic `COMMIT`, not a single unbounded `UPDATE` — the latter
  holds one long transaction and locks the table for its full duration.

## Real-world grounding

GitLab's 2017 production incident: an engineer, attempting to fix
replication lag, ran `rm -rf` against what they believed was the secondary
database directory but was actually production — and every one of the five
backup mechanisms in place (regular backups, disk snapshots, LVM snapshots,
S3 backups, and a separate replica) had been silently failing or
misconfigured for weeks, discovered only when the team tried to actually
restore from one. The lesson isn't "have backups" — it's that a backup
strategy that has never been tested with an actual restore is not a backup
strategy, it's an assumption.

## Verification

- [ ] Migration has both an up and a down (or is explicitly irreversible, documented as such)
- [ ] No `NOT NULL` added to an existing table without a default
- [ ] Indexes on existing tables use `CONCURRENTLY`
- [ ] Schema change and data backfill are separate migrations
- [ ] Tested against production-sized data, not a small dev sample
- [ ] hotel_id-partitioned tables checked against `postgres-hotel-partitioning`

Files in this skill

  • SKILL.md4.4 KB
  • evals/evals.json2.6 KB

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.