Миграции БД с Claude Code — Alembic, Prisma, TypeORM (Гайд и обучение Claude Code)

Миграция — самое опасное изменение в проекте. Неправильный ALTER TABLE блокирует таблицу на часы. Пропущенный downgrade превращает откат в рулетку. Claude Code умеет генерировать миграции, но требует ревью с упором на безопасность.

Миграции БД с Claude Code — Alembic, Prisma, TypeORM (Гайд и обучение Claude Code)

Канал с гайдами и контентом по claude code, выкладываем новости (когда режут лимиты в 10 раз) и какие инструменты через claude реализуем для проектов, канал: https://t.me/claudedevolper

CLAUDE.md для миграций

Правила миграций ### Нельзя в одном релизе - Добавить 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))

Шаг 2 — backfill в фоне:

UPDATE users SET phone = '' WHERE phone IS NULL; -- лучше батчами по 10K строк

Шаг 3 — NOT NULL:

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 — эксклюзивный лок на всю таблицу. На миллионной таблице — минуты простоя.

В Alembic:

op.create_index( "idx_users_email", "users", ["email"], postgresql_concurrently=True, )

Нужен autocommit_block:

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

Миграции БД с Claude Code — Alembic, Prisma, TypeORM (Гайд и обучение Claude Code)
Начать дискуссию