Короткий ответ
Нужно заранее спроектировать миграцию так, чтобы она была обратно совместима со старой версией кода хотя бы некоторое время, и подготовить проверенный план отката кода отдельно от отката данных, поскольку откатить миграцию БД обычно сложнее, чем откатить деплой кода.
Как это работает подробнее
Откат кода (деплой предыдущей версии) обычно быстрый и безопасный сам по себе, но если новая версия уже выполнила несовместимую миграцию (например, удалила колонку), старая версия кода может перестать работать после отката. Поэтому миграции проектируют так, чтобы можно было откатить код, оставив схему БД в состоянии, совместимом со старой версией (подход expand/contract из предыдущих вопросов).
Пример кода
example.txt
План релиза с учётом отката:
1. Деплой кода v2, который пишет И в старое поле price, И в новое price_cents
(миграция уже добавила price_cents как nullable).
2. Наблюдение 24 часа. Если проблема — откат на код v1, который использует
только price, данные не теряются, т.к. price продолжал обновляться.
3. Только после стабильной работы v2 — бэкфилл и удаление price (отдельный релиз).Что использовать на практике
- Практика. Перед релизом стоит явно проверить план отката: что произойдёт, если откатить только код (сработает ли он со свежей схемой), и что делать, если проблема обнаружена уже после того, как контрактный шаг миграции (удаление старых полей) выполнен. Для рискованных релизов полезны feature-флаги — они позволяют откатить поведение мгновенно, не трогая деплой или схему данных вообще.
Подводные камни
- Риски. Частая ошибка — тестировать откат только в теории, не проверив его на стейджинге, и обнаружить при инциденте, что откат кода не работает со свежей схемой. Другая ошибка — объединять в одном релизе необратимые изменения данных (например, удаление колонки) с рискованной новой бизнес-логикой, из-за чего откатить проблемную логику становится невозможно без потери данных. Также часто забывают явно прописать, кто и по каким признакам принимает решение об откате во время инцидента.