Почему никогда не стоит пересобирать для продакшена

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

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

Это история, которая повторяется в командах каждый день. Коренная причина почти всегда одна и та же: где-то между стейджингом и продакшеном кто-то решил пересобрать приложение вместо того, чтобы продвинуть существующий артефакт.

Два пути: пересборка vs. продвижение

У каждого пайплайна доставки ПО есть несколько сред. Разработка — для экспериментов с новыми функциями. Стейдж — для верификации QA или владельцем продукта. Продакшен — где реальные пользователи взаимодействуют с вашим приложением.

Когда вам нужно переместить артефакт из одной среды в следующую, у вас есть два варианта.

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

Следующая блок-схема иллюстрирует два пути и их результаты:

flowchart TD A[Старт: нужно развернуть в production] --> B{Выбрать путь} B --> C[Пересборка] B --> D[Продвижение] C --> E[Чекаут исходного кода] E --> F[Скачивание зависимостей] F --> G[Компиляция приложения] G --> H[Новый артефакт с новой контрольной суммой и временной меткой] H --> I[Развернуть в production] I --> J[Риск: отличается от протестированного артефакта] D --> K[Существующий артефакт в реестре] K --> L[Пометить как одобренный для production] L --> M[Развернуть те же байты в production] M --> N[Уверенность: совпадает с протестированным артефактом]

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

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

Скрытый риск пересборки

Когда вы пересобираете для продакшена, вы создаёте новый артефакт. У него другой ID сборки. Другая временная метка. И, что критично, зависимости, скачанные во время этой новой сборки, могут отличаться от тех, что использовались при сборке для стейджинга.

Подумайте, что происходит, когда ваш процесс сборки выполняет npm install, pip install или go mod download. Эти команды обращаются к удалённым репозиториям, чтобы получить пакеты. Если мейнтейнер пакета выложил минорное обновление между вашей сборкой для стейджинга и сборкой для продакшена, ваш продакшен-артефакт будет включать это обновление. Даже если изменение технически обратно совместимо, оно вносит разницу между тем, что вы тестировали, и тем, что работает в продакшене.

Тот же риск относится к вашему инструментарию. Если ваша CI-система обновляет версию компилятора, базовый образ или инструменты сборки между сборками, результат может измениться незаметным образом. Код, который нормально компилировался во время сборки для стейджинга, может дать другие машинные инструкции во время сборки для продакшена.

Вы теряете уверенность в том, что протестировано именно то, что работает в продакшене. Вы переходите от уверенности к надежде.

Почему продвижение работает лучше

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

На практике продвижение обычно работает через тегирование. В реестре контейнеров у вас может быть образ с тегом staging. После верификации вы добавляете тег production к тому же образу. Сам образ не меняется. Меняются только метки. Ваша система развёртывания отслеживает появление тега production и забирает образ, когда он появляется.

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

Верификация всё равно происходит перед продвижением

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

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

Когда пересборка может быть необходима

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

Но эти случаи — исключения, а не правило. Большинство команд могут отделить конфигурацию от кода. Большинство требований комплаенса можно удовлетворить, подписывая артефакты после продвижения, а не пересобирая их. Если вы регулярно пересобираете для продакшена, спросите себя: это техническая необходимость или просто привычка?

Практический чек-лист для продвижения артефактов

Прежде чем настраивать workflow продвижения, проверьте следующие пункты:

  • Ваш процесс сборки создаёт единый артефакт, который работает во всех средах
  • Ваш реестр поддерживает тегирование или обновление метаданных без повторной загрузки
  • Ваша система развёртывания может отслеживать изменения тегов и запускать развёртывание
  • Ваш процесс отката ссылается на ранее продвинутые артефакты, а не на старые коммиты
  • Ваша команда понимает, что «продвинуть» означает изменить метаданные, а не пересобрать

Конкретный вывод

Решение между пересборкой и продвижением сводится к одному вопросу: вы хотите быть уверены, что протестировали именно то, что развернули, или вы хотите надеяться, что новая сборка даст тот же результат?

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