Snapshot Flow: как мы делаем ручные релизы без CI/CD предсказуемыми

Любая команда разработки, как только в ней становится больше одного участника, начинает выстраивать процесс, в котором есть репозиторий кода, несколько разработчиков и, опционально, один или несколько контуров развёртывания. Мы начали командную работу задолго до появления git, и начинали работать «как работается», предоставив этот процесс естественной эволюции. На сегодняшний день мы имеем то, что я назвал Snapshot Flow.

Как мне кажется, он во многом перекликается с Trunk-Based Development, но не в его каноническом понимании. А если присмотреться повнимательнее, то окажется, что это и не классический GitFlow. Это — наш собственный путь, рождённый практикой.

Не на всех проектах есть фиксированный состав разработчиков. Нам часто нужно переключаться между фичами и выполнять частичные деплои: какие-то из них готовы полностью, какие-то — нет, а какие-то нужно выкатывать только на отдельные контуры. Я не стал железной рукой насаждать какую-то методику, которая для всех была бы в новинку и не факт, что подошла бы. Вместо этого я вполне осознанно пустил ситуацию на самотёк, чтобы однажды зафиксировать, что же происходит в боевых условиях на проектах по пятницам (шутка (нет!)).

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

Порядок до того, как код попадает на контуры

Суть нашего подхода проста: весь код рождается и интегрируется в одной главной ветке — master (или main). Это наш единственный источник истины и точка сборки для всех последующих контуров. А ещё есть ветки-контуры, например testing и production, и их, при необходимости, может быть больше. Каждая такая ветка соответствует своему окружению.

Зачем ствол, если мы всё равно разворачиваем с testing и production? Всё просто: ветки контуров (testing, production/v1, production/v2) — это не ветки разработки, а ветки-снимки (snapshot branches). Их единственная задача — зафиксировать состояние кода для развертывания на конкретное окружение. Вся реальная работа — написание кода, его интеграция и тестирование — происходит в master и в короткоживущих ветках, которые вливаются в master.

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

Как мы делаем это без CI/CD: дисциплина вместо магии

Ключевой вопрос: как поддерживать master в стабильном состоянии без автоматического конвейера? Ответ — в жёсткой дисциплине и нескольких ключевых практиках.

Обязательный ревью. У нас железное правило: короткоживущая ветка feature/ живёт не больше одного рабочего дня. Если задача не умещается — мы дробим её. Каждая такая ветка перед слиянием в master проходит код-ревью другим разработчиком. Фокус ревью — не только на качестве кода, но и на вопросе: «Это изменение безопасно для ствола?». Мы спрашиваем себя: «Что сломается, если это попадёт в master прямо сейчас?». Эта культура взаимной ответственности — наш главный «CI».

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

Флаги функций (Feature Flags). Это важный инструмент нашей стратегии. Поскольку у нас нет изолированных сред для каждой фичи, мы включаем новую функциональность прямо в master, но, если это требуется, «оборачиваем» её выключателем. На тестовом контуре (testing) флаг включается для тестировщиков. На боевом (production) он может быть выключен, включен для 1% пользователей или для внутренней аудитории. Это позволяет десяткам маленьких, невидимых изменений безопасно накапливаться в master, не дожидаясь «дня икс» для большого релиза. Когда функциональность окончательно готова и проверена, мы просто включаем флаг для всех. А если что-то пошло не так — выключаем его, не откатывая весь релиз.

Коммиты в ветки-контуры запрещены. Код в testing и production попадает только через merge из master. Контуры развёртывания «смотрят» строго на свои ветки и ни на какие другие.

Жизненный цикл кода: от идеи до продакшена

Вот как выглядит путь строки кода у нас:

  1. Разработка: Создаётся ветка feature/... от master. Через несколько часов работы и код-ревью она сливается обратно в master. Функция, если нужно, скрыта за флагом.

  2. Интеграция: master — это постоянно текущий поток таких микрослияний. Мы уверены в его стабильности, потому что каждое изменение было небольшим и проверенным.

  3. Подготовка к тесту: Когда накоплен нужный объём изменений, ответственный за релиз переливает код из master в ветку testing. Запускается скрипт деплоя этой ветки на тестовый контур.

  4. Релиз: После QA фиксы (тоже через master!) попадают в testing. Для выкатки создаётся ветка production от актуального testing. Запускается скрипт деплоя на прод.

Ветки testing и production у нас всегда линейны и предсказуемы. Они не становятся ареной для слияния конфликтующих фич в последний момент. Они — просто отражение того, что уже было согласовано и интегрировано в master.

Что это даёт

Мы отказались от иллюзии, что долгие изолированные ветки дают контроль. На практике они создают лишь технический долг в виде конфликтов и неожиданных багов. Snapshot Flow, при всей его кажущейся простоте, даёт нам предсказуемость и возможность включиться в работу на любом этапе. Мы знаем, что состояние в master — это то, что будет завтра на тесте, а послезавтра — на проде. Это позволяет планировать релизы не как реалити-шоу «Пятничный деплой», а как нечто обыденное, пусть и не всегда простое.

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

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

Звоните нам! +7 495 369-15-48
Написать в телеграм