Бесплатная консультация
← Все статьи

Правила релизной ветки, которые работают на практике

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

Специалист за монитором с открытым редактором

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

В результате команда формально находится на этапе стабилизации, но фактически продолжает разработку.

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

Но сама по себе ветка release/* ничего не решает. Работают не названия веток, а правила: когда релизная ветка создаётся, какие изменения в неё допускаются, кто принимает решение, как работают хотфиксы и каким образом команда понимает, какую именно сборку сейчас проверяет QA.

Заморозка — это не запрет, а процедура

Фраза «с сегодняшнего дня замораживаем код» звучит понятно только до первого серьёзного бага.

После этого появляются вопросы:

  • можно ли вносить исправление;
  • насколько оно критично;
  • кто оценивает риск;
  • нужно ли повторно тестировать всю сборку;
  • что делать с исправлением в основной ветке;
  • нужно ли формировать новый release candidate.

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

Рабочий подход проще: заранее определить категории изменений, которые вообще могут попасть в релизную ветку.

Например:

Release blocker — ошибка, из-за которой игру нельзя выпускать.

Certification blocker — нарушение требования платформы, способное привести к отказу при сертификации.

Critical regression — функция, которая раньше работала, но сломалась в текущем кандидате.

High-risk issue — проблема, которая может серьёзно повлиять на пользователей или стабильность продукта.

Остальные исправления остаются в основной ветке и ждут следующей версии.

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

Полезное правило: разработчик, предлагающий изменение, должен объяснить не только что исправляет commit, но и что потенциально может сломаться после его добавления.

Так решение принимается не на основании того, насколько неприятно выглядит баг, а с учётом реального риска для release candidate.

Назначьте владельца релизной ветки

Если решение о каждом изменении принимает вся команда, процесс быстро замедляется.

Трёхмерная модель здания: работа над окружением

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

Поэтому у релизного процесса должен быть конкретный владелец.

Это может быть:

  • tech lead;
  • release manager;
  • lead developer;
  • технический продюсер;
  • другой человек, который понимает текущее состояние проекта и имеет право принимать финальное решение.

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

Для каждого потенциального изменения достаточно иметь короткий набор данных:

Что сломано.

Насколько проблема критична.

Как воспроизвести её.

Что изменяет исправление.

Какие системы оно может затронуть.

Как будет проверено после merge.

Главный принцип: сначала сделать риск видимым, затем принимать решение о сроке, scope и составе релиза.

Релизная ветка создаётся раньше, чем кажется нужным

Одна из типичных ошибок — создавать release branch за несколько дней до выхода.

Ноутбук с открытым графическим редактором

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

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

Формальной изоляции релиза ещё нет, но разработка следующей версии уже замедлилась.

Гораздо удобнее отделять релизную ветку тогда, когда зафиксирован контент и начинается период стабилизации.

Например:

  • main или develop продолжает двигаться дальше;
  • release/1.4 содержит будущую версию 1.4;
  • QA работает с конкретными сборками из release/1.4;
  • новые механики и изменения для 1.5 продолжают попадать в основную линию разработки.

Так команда не должна полностью останавливаться на несколько недель ради одного релиза.

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

QA должен тестировать конкретную сборку

Одна из самых неприятных ситуаций перед релизом выглядит так:

QA сообщает об ошибке.

Разработчик запускает игру — ошибки нет.

Начинается выяснение: какая версия тестировалась, когда она была собрана, успел ли туда попасть последний commit и не изменилось ли что-то после создания билда.

Этого можно избежать почти полностью, если версия сборки видна везде.

Минимальный набор:

  • номер версии;
  • номер сборки;
  • commit hash;
  • название branch;
  • дата сборки.

Эти данные желательно выводить в debug overlay внутри игры, записывать в лог и добавлять к имени артефакта.

Например:

Game_1.4.0_build_582_a73fd21.zip

Тогда баг-репорт «не работает сохранение» превращается в гораздо более полезный:

«В build 582, commit a73fd21 после смены пользователя не загружается сохранение».

Для системного QA и тестирования игры это особенно важно: тестировщик должен всегда понимать, какой именно артефакт он проверял.

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

Release candidate не должен быть движущейся целью

Когда QA начинает полноценную проверку release candidate, этот билд необходимо считать зафиксированной точкой.

Рабочее место художника с графикой на экране

Это не означает, что после него нельзя делать исправления.

Это означает, что любое изменение создаёт новый кандидат.

Например:

RC1 → QA находит блокирующую проблему.

Разработчик делает исправление.

Создаётся RC2.

QA проверяет само исправление и необходимый regression scope уже на RC2.

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

В результате невозможно точно сказать, какая версия действительно прошла полный цикл тестирования.

Именно поэтому QA лучше подключать ещё до content lock, чтобы большая часть системных проблем была обнаружена до того, как каждое исправление начинает увеличивать риск релиза.

Обратный перенос исправлений обязателен

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

Команда исправляет критический баг непосредственно в release branch, выпускает версию и закрывает задачу.

Через несколько недель тот же баг снова появляется в следующем релизе.

Причина простая: исправление осталось только в старой релизной ветке и никогда не вернулось в основную линию разработки.

Поэтому каждое исправление должно существовать во всех ветках, из которых будет создаваться будущая версия.

Есть два основных подхода.

Сначала основная ветка, потом release

Исправление сначала добавляется в main / develop, после чего конкретный commit переносится в release branch через cherry-pick.

Художник за работой над игровым визуалом

Плюс подхода — исправление невозможно случайно потерять в будущем.

Сначала release, потом обратный merge

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

Главное — не полагаться на память разработчика.

Лучше сделать обратный перенос частью definition of done для release fix.

Задача не считается закрытой, пока:

  • исправление не находится в release branch;
  • его не проверил QA;
  • оно не перенесено в основную ветку;
  • CI основной ветки снова не стал зелёным.

Контролируйте расхождение веток

Чем дольше существует release branch, тем больше она расходится с основной.

Мобильная игра на экране смартфона в руках

Само по себе это нормально. Проблемы начинаются, когда никто не понимает масштаб расхождения.

Если команда за время стабилизации внесла в release branch 20 уникальных исправлений, а обратно попало только 16, четыре потенциальных бага уже ждут следующую версию.

Поэтому полезно автоматически отслеживать commits, существующие только в релизной ветке.

Даже простой отчёт перед закрытием релиза способен показать потерянные изменения.

Для крупных проектов такую проверку лучше включить в CI/CD. Вообще многие проблемы релиза возникают не из-за ошибок разработчиков, а из-за ручных операций. Чем больше подобных действий можно сделать воспроизводимыми, тем меньше вероятность случайной ошибки. Это относится и к работе с ветками, и к оптимизации build pipeline.

Не делайте release branch местом для «маленьких улучшений»

Одна из самых опасных формулировок перед релизом:

«Там буквально две строки».

Размер diff практически ничего не говорит о риске.

Две строки могут изменить:

  • порядок инициализации;
  • работу сохранений;
  • расчёт экономики;
  • сетевой запрос;
  • обработку input;
  • платформенный API.

При этом исправление на сотню строк в изолированном инструменте может вообще не влиять на пользовательскую сборку.

Поэтому решение о merge нельзя принимать на основании размера commit.

Нужно смотреть на область воздействия.

Перед добавлением изменения полезно ответить на вопросы:

  • затрагивает ли оно критический пользовательский сценарий;
  • требуется ли миграция данных;
  • меняет ли формат сохранения;
  • влияет ли на network protocol;
  • затрагивает ли платформенные интеграции;
  • требуется ли полный regression pass;
  • можно ли проверить изменение изолированно.

И если изменение не связано с блокирующей проблемой, чаще всего безопаснее отправить его в следующий релиз.

Хотфикс должен иметь отдельный процесс

После релиза правила не становятся менее важными.

Игрок запускает мобильную игру на телефоне

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

Именно поэтому процесс hotfix необходимо продумать заранее.

Команда должна знать:

  • от какой версии создаётся hotfix branch;
  • кто может инициировать срочное исправление;
  • какие проверки обязательны;
  • нужен ли полный regression;
  • кто подтверждает выпуск;
  • куда после релиза должен быть перенесён fix.

Например:

  • release/1.4.0 → релиз;
  • hotfix/1.4.1 → критическое исправление;
  • после выпуска commit обязательно возвращается в main.

Чем меньше решений команда вынуждена придумывать в момент production-инцидента, тем ниже риск сделать дополнительную ошибку.

Релизная ветка и технический долг

Release freeze часто внезапно показывает проблемы, которые команда не замечала во время активной разработки.

Например:

  • нестабильные сборки;
  • слишком долгий CI;
  • слабое покрытие ключевых систем тестами;
  • ручное создание билдов;
  • отсутствие понятной версии артефакта;
  • сложные зависимости между системами;
  • баги, которые невозможно воспроизвести стабильно.

Это не проблемы релизной ветки как таковой.

Она просто делает накопленный технический долг видимым.

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

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

Закрытие релизной ветки — отдельный этап

После успешного выпуска release branch не должна просто оставаться в репозитории навсегда.

Мобильная игра на смартфоне крупным планом

Нужно заранее определить, когда ветка считается закрытой.

Обычно перед закрытием стоит проверить:

Все необходимые commits попали в production.

Все уникальные fixes перенесены в основную ветку.

Финальный release помечен tag.

Артефакты сохранены.

QA знает финальный номер build.

Документация обновлена.

Незакрытые задачи перенесены в следующий milestone.

После этого ветку можно архивировать или удалить в соответствии с правилами команды.

Если этого не делать, через несколько релизов в репозитории остаются release/1.2, release/1.3, release/1.4, несколько hotfix branches и никто уже точно не знает, какие из них являются актуальными.

После релиза сделайте короткий разбор

Для улучшения процесса не нужен многочасовой retrospective.

Достаточно ответить на несколько вопросов:

  • какие исправления пришлось добавлять после code freeze;
  • почему они не были обнаружены раньше;
  • сколько release candidate пришлось собрать;
  • какие проверки заняли больше всего времени;
  • были ли потерянные commits;
  • возникала ли путаница с версиями сборок;
  • что можно автоматизировать до следующего релиза.

Через несколько циклов эти ответы покажут реальные слабые места процесса.

Если каждый релиз команда делает пять поздних фиксов сохранений, проблема, вероятно, находится не в release branch, а в тестировании save system.

Если каждый раз долго собирается новый RC, стоит отдельно разбирать build pipeline.

Если QA регулярно находит критические проблемы после content lock, необходимо раньше включать тестирование.

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

Минимальный набор правил релизной ветки

Если команде нужен максимально короткий вариант, можно начать с семи правил:

Игра на телефоне в руках пользователя

Release branch создаётся после фиксации контента, а не за день до релиза.

В неё попадают только подтверждённые blockers и одобренные исправления.

У каждого изменения есть владелец и описание риска.

QA всегда тестирует конкретный build.

Любое изменение создаёт новый release candidate.

Каждый fix обязательно возвращается в основную ветку.

После релиза ветка закрывается только после проверки расхождений.

Этого уже достаточно, чтобы убрать значительную часть хаоса вокруг code freeze.

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

Главное — чтобы команда в любой момент могла ответить на три вопроса:

Какой build сейчас тестируется?

Какие изменения ещё могут попасть в релиз?

Где находится каждый fix после выпуска?

Если ответы известны, релизная ветка выполняет свою задачу.

NoPanic Studio подключается к существующим игровым проектам на этапах разработки, стабилизации, QA и тестирования, подготовки релизных сборок и дальнейшей поддержки. Если релизный процесс регулярно превращается в ручное управление ветками, нестабильные RC и срочные исправления перед публикацией, проблему стоит искать не только в отдельных багах, но и в организации самого pipeline.

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

Следующая статьяБюджет памяти на мобильных: как не собирать вылеты по ночам
→