Почему развертывание баз данных сложнее развертывания приложений

Вы всю неделю выкатываете новый код приложения. Новая функциональность заходит, старый процесс останавливается, новый запускается. Если что-то сломалось — вы переключаетесь обратно на предыдущую версию. Весь цикл занимает минуты. Это кажется безопасным, потому что код заменим.

А затем приходит миграция базы данных. Нужно добавить новую колонку. Переименовать таблицу. Вы запускаете скрипт миграции, и вдруг продакшн-база получает другую структуру. Приложение сначала работает нормально, но через час кто-то сообщает о пропавших данных в отчёте. Вы пытаетесь откатить миграцию, но данные, которые были преобразованы во время миграции, не вернулись в исходное состояние чисто. Некоторые строки всё ещё имеют новую структуру. Некоторые старые значения исчезли.

Это тот момент, когда многие команды осознают: развертывание базы данных — это не просто «развертывание для другого типа сущности». Это принципиально иная задача.

Код одноразов, данные — нет

Когда вы только учились создавать приложения, вы, вероятно, думали о приложении как о наборе кода. Вы пишете код, запускаете его, видите результат и снова меняете код, если что-то не так. Если новая версия не работает, вы возвращаетесь к предыдущей. Этот процесс кажется лёгким, потому что у него нет долгосрочных последствий. Неправильный код можно выбросить, заменить или переписать с нуля.

Но когда приложение начинают использовать другие люди, рядом с ним вырастает нечто: данные. Данные пользователей, транзакций, заказов, история операций. Эти данные — не просто содержимое базы. Данные — это запись того, что уже произошло в бизнесе. Каждая строка в таблице хранит что-то ценное. Если данные потеряны, переписывание кода их не вернёт. Потерянные данные невозможно регенерировать.

В этом фундаментальное различие между кодом приложения и базой данных. Код приложения можно рассматривать как заменяемый артефакт. Вы можете удалить папку проекта, снова склонировать её из репозитория — и приложение будет работать точно так же, как раньше. Но база данных устроена иначе. База данных хранит состояние — текущее состояние бизнес-данных. Это состояние формируется из накопления уже произошедших операций. Если базу данных удалить, состояние исчезнет. Не существует git clone, который восстановит данные клиентов, собранные за год.

Влияние на развертывание

Эффект этого различия становится очевидным сразу во время развертывания. Когда вы развертываете новую версию приложения, вы отправляете последний код на сервер, останавливаете старый процесс и запускаете новый. Если с новой версией проблемы, вы можете откатиться, вернув предыдущую версию. Приложение будет работать так же, как до обновления.

Но когда развертывание базы данных меняет структуру таблиц, это затрагивает существующие данные. Новая колонка может быть заполнена значениями по умолчанию. Типы данных могут измениться. Старые данные могут потребовать преобразования. Если эти изменения вызывают проблемы, откат — это не просто замена версий кода. Данные, которые уже были изменены, не возвращаются автоматически в исходную форму.

Рассмотрим простую миграцию, которая добавляет колонку и заполняет её данными:

-- Прямая миграция
ALTER TABLE users ADD COLUMN age INT;
UPDATE users SET age = 25 WHERE age IS NULL;
-- Скрипт отката
ALTER TABLE users DROP COLUMN age;

Если прямая миграция выполнилась и приложение начало записывать новые значения возраста, то при откате колонка удаляется, и эти новые значения теряются. В отличие от кода приложения, вы не можете просто переразвернуть старую версию, чтобы восстановить потерянные данные.

Именно поэтому многие команды становятся осторожными с развертыванием баз данных. Они могут быстро менять версии приложений несколько раз в день, но колеблются запускать миграцию базы данных при каждом деплое. Не потому, что миграции технически сложны, а потому что последствия другие. Неправильный код можно исправить, переписав его. Неправильные данные могут потребовать дней на исправление, а иногда их невозможно восстановить в точности как было.

Как это меняет ваш процесс развертывания

Разница между кодом и данными влияет на то, как вы проектируете свой процесс развертывания. Для приложений вам в основном нужно убедиться, что новый код может работать. Для баз данных вы должны гарантировать, что структурные изменения не повредят существующие данные, не нарушат работу работающего приложения и их всё ещё можно будет отменить, если что-то пойдёт не так.

Вот практические последствия:

Развертывание приложений — аддитивно. Вы добавляете новый код, удаляете старый, и система продолжает работу. Старая версия всё ещё доступна в вашем репозитории или хранилище артефактов.

Развертывание баз данных — трансформационно. Вы меняете структуру существующих данных. Старая структура исчезает после выполнения миграции. Даже если у вас есть резервная копия, её восстановление означает потерю всех данных, созданных после создания этой копии.

Откат приложений дёшев. Вы направляете балансировщик нагрузки на старую версию или перезапускаете старый процесс. Состояние приложения определяется работающим кодом.

Откат баз данных дорог. Вам нужно написать обратную миграцию, которая преобразует данные обратно к предыдущей структуре. Эту обратную миграцию необходимо тестировать. Она может завершиться ошибкой, если были добавлены новые данные, которые не соответствуют старой структуре. А если прямая миграция преобразовала данные необратимо (например, объединила имя и фамилию в одну колонку), обратная миграция может привести к потере информации.

Реальный риск — не технический

Многие команды относятся к миграциям баз данных как к чисто технической задаче. Они пишут скрипты миграций, тестируют их на стейджинге и запускают в продакшене. Когда что-то идёт не так, они винят скрипт миграции или инструмент для работы с БД.

Но реальный риск часто организационный. Миграция базы данных затрагивает данные, принадлежащие реальным пользователям. Ошибка может повредить записи клиентов, финансовые данные или логи соответствия требованиям. Команда, выполняющая миграцию, может не до конца понимать, как эти данные используются другими системами. Переименование колонки может сломать отчётный запрос, который выполняется раз в месяц. Изменение типа данных может привести к тому, что устаревший сервис начнет молча выдавать ошибки.

Вот почему развертывание баз данных требует другого уровня внимательности. Техническая сложность управляема. Организационная сложность — вот что заставляет команды замедляться.

Практический чек-лист для развертывания баз данных

Перед запуском миграции базы данных в продакшене проверьте следующее:

  • Можно ли откатить миграцию? Напишите и протестируйте скрипт отката до выполнения прямой миграции.
  • Не сломает ли миграция текущее приложение? Если приложение читает колонку, которую вы переименовываете, оно будет падать, пока его тоже не обновят.
  • Можно ли выполнить миграцию на работающем приложении? Некоторые миграции блокируют таблицы, что может вызвать простой.
  • Есть ли резервная копия? Сделайте резервную копию перед любой миграцией, которая преобразует данные.
  • Кого нужно уведомить? Предупредите команды, которые потребляют эти данные, включая отчётность, аналитику и data science.

Вывод

Код приложения одноразов. Вы можете выбросить его, переписать и развернуть новую версию без потери чего-либо важного. Данные базы данных не одноразовы. Каждая миграция меняет то, что нельзя легко воссоздать.

Относитесь к развертыванию баз данных с той же строгостью, что и к инцидентам в продакшене. Пишите скрипты отката. Тестируйте миграции на реалистичных данных. Общайтесь с командами, которые зависят от этих данных. И никогда не предполагайте, что миграция безопасна только потому, что она прошла на стейджинге.

Разница между кодом и данными — не техническая деталь. Это причина, по которой развертывание баз данных заслуживает собственного процесса, собственной стратегии тестирования и собственной оценки рисков.