Миграции БД с Claude Code — Alembic, Prisma, TypeORM
Миграция — самое опасное изменение в проекте. Неправильный ALTER TABLE блокирует таблицу на часы. Пропущенный downgrade превращает откат в рулетку. Claude Code умеет генерировать миграции, но требует ревью с упором на безопасность.
Канал с гайдами и контентом по claude code, выкладываем новости (когда режут лимиты в 10 раз) и какие инструменты через claude реализуем для проектов, канал: https://t.me/claudedevolper
## Правила миграций ### Нельзя в одном релизе - Добавить NOT NULL без default - Переименовать колонку без deprecation-фазы - Удалить колонку, которую ещё читает прод-код - CREATE INDEX без CONCURRENTLY на таблице > 1M строк ### Обязательно - Каждая миграция — атомарна (одно изменение) - downgrade() всегда описан - Для Postgres: CONCURRENTLY для индексов, CHECK NOT VALID для ограничений - Проверка на staging с копией прод-данных ### Workflow 1. Изменил модель → сгенерировал миграцию 2. Ревью диффа — особенно op.drop_column 3. Прогон на dev/staging 4. Deploy: миграция СНАЧАЛА, потом новый код (или наоборот — зависит от изменения)
Alembic (SQLAlchemy)
alembic revision --autogenerate -m "add phone to users"
# migrations/versions/2026_04_17_add_phone.py
from alembic import op
import sqlalchemy as sa
revision = "e7a2b8c1f9d0"
down_revision = "a3b2c1d4e5f6"
def upgrade():
op.add_column(
"users",
sa.Column("phone", sa.String(20), nullable=True),
)
op.create_index(
"idx_users_phone",
"users",
["phone"],
postgresql_concurrently=True,
)
def downgrade():
op.drop_index("idx_users_phone", table_name="users")
op.drop_column("users", "phone")alembic upgrade head alembic downgrade -1 # откат на одну alembic history --verbose
Prisma (Node.js)
npx prisma migrate dev --name add_phone_to_users
Prisma сравнит schema.prisma с БД и сгенерит SQL. Пример:
-- migration.sql
ALTER TABLE "users" ADD COLUMN "phone" TEXT;
CREATE INDEX "users_phone_idx" ON "users"("phone");Для прода — prisma migrate deploy (без генерации, только apply).
Откат Prisma не поддерживает «из коробки» — пишешь новую миграцию, которая восстанавливает состояние.
Zero-downtime добавление NOT NULL
Шаг 1 — колонка nullable + default в приложении:
def upgrade():
op.add_column("users", sa.Column("phone", sa.String(20), nullable=True))UPDATE users SET phone = '' WHERE phone IS NULL; -- лучше батчами по 10K строк
def upgrade():
op.alter_column("users", "phone", nullable=False, server_default="")Безопасное переименование колонки
Нельзя просто op.alter_column("users", "email", new_column_name="email_address") — старый код сломается.
1. Добавить email_address + триггер, копирующий из email 2. Задеплоить код, который пишет в обе, читает из email_address 3. Backfill существующих записей 4. Задеплоить код, который пишет только в email_address 5. Удалить email
Проверка миграции перед применением
# Показать SQL без выполнения alembic upgrade head --sql > up.sql # Prisma npx prisma migrate diff \ --from-schema-datamodel schema.prisma \ --to-schema-datasource schema.prisma \ --script > up.sql
Чтение up.sql глазами — обязательный этап для прод-миграции.
CONCURRENTLY в PostgreSQL
-- НЕ CREATE INDEX idx_users_email ON users(email); CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
Без CONCURRENTLY — эксклюзивный лок на всю таблицу. На миллионной таблице — минуты простоя.
op.create_index(
"idx_users_email",
"users",
["email"],
postgresql_concurrently=True,
)def upgrade():
with op.get_context().autocommit_block():
op.create_index(
"idx_users_email", "users", ["email"],
postgresql_concurrently=True,
)Подводные камни
- autogenerate не видит ENUM-значения. Добавление варианта в ENUM — руками.
- Foreign key без индекса. DELETE по parent таблице превращается в seq scan по child.
- Prisma шизу делает при ручных правках SQL. Не трогай миграционный файл после создания.
- alembic downgrade не всегда обратим. Если в upgrade был DROP COLUMN — данные уже не вернуть.
Как попробовать
1. Добавь в CLAUDE.md секцию миграций 2. Сгенерируй тестовую миграцию 3. Прогони --sql чтобы увидеть реальный SQL 4. Примени на staging → убедись, что downgrade работает
Канал с гайдами и контентом по claude code, выкладываем новости (когда режут лимиты в 10 раз) и какие инструменты через claude реализуем для проектов, канал: https://t.me/claudedevolper