Перейти к основному содержимому

ADR: Модель ветвления

  • Status: Accepted by Andrei Ganiushkin andrey@ganyushkin.ru
  • Date: 2026-07-26
  • Authors: Andrei Ganiushkin andrey@ganyushkin.ru
  • Related issues: -

Context

Этот ADR фиксирует набор веток, их назначение и почему выбрана именно такая модель, а не одна из более простых альтернатив (trunk-based, GitHub Flow, полный GitFlow).

Границы решения:

  • в рамках ADR: состав типов веток, назначение каждой ветки, источник ответвления и направления слияния, права push.
  • вне рамок ADR: способ выполнения слияния — см. ADR 0007; подпись коммитов — ADR 0004. Подпись артефактов релиза — ADR 0005.

Требования, которые модель ветвления обязана поддерживать:

  1. main содержит только выпущенные, подписанные состояния — разработка в ней не ведётся (ADR 0005).
  2. Нужна отдельная точка заморозки кода на время ручной подписи артефактов по всем целевым платформам, не блокирующая текущую разработку (ADR 0005).
  3. Выпущенные версии должны поддерживаться патчами после того, как разработка ушла вперёд, — без размораживания уже закрытого релиза. Линия поддержки открывается тогда, когда для конкретной версии действительно понадобилось изменение.

Decision

Принимается модель из шести типов веток: main, develop, feature/*, fix/*, release/*, support/*.

ВеткаНазначениеИсточникКуда сливаетсяВремя жизниКто может push
mainИстория релизов, релизные тегипостоянноникто
developИнтеграция разработкиmainrelease/*постояннотолько maintainer
feature/*Новая функциональностьdevelop или support/*ветка-источникдо приёмкиавтор ветки
fix/*Исправления, security-fixdevelop, release/* или support/*ветка-источникдо приёмкиавтор ветки
release/*Заморозка состава релиза и подпись артефактовdevelopmainдо выпуска релизатолько maintainer
support/*Долгосрочная поддержка выпущенной минорной версиитег версии в main, создаётся по необходимости— (сама выпускает патчи)месяцы/годытолько maintainer

Назначение каждой ветки:

  • main — не место разработки. Хранит последовательность выпущенных состояний и релизные теги. Принимает изменения только из release/*.
  • develop — интеграция новой функциональности и обычных исправлений. Единственный источник для release/*.
  • feature/* — реализация одной функциональной задачи. Сливается в develop (или в support/* для заказных доработок).
  • fix/* — исправление дефекта. Ответвляется от той линии, в которой дефект должен быть исправлен (develop, незакрытая release/* или support/*), и возвращается ровно в неё.
  • release/* — короткоживущий шлюз в main: обновление версии и CHANGELOG, суффикс -rc.X, прогон проверок, подпись артефактов по платформам. После выпуска релиза удаляется.
  • support/* — долгоживущая линия поддержки одной минорной версии. Создаётся не при выпуске релиза, а когда в эту версию потребовалось внести изменение. Из неё выпускаются патч-релизы (vX.Y.1, vX.Y.2, …) для пользователей, которые не переходят на новую версию, или заказные доработки (LTS-features).

Различие release/* и support/*

Обе ветки работают с релизами, но решают разные задачи:

release/*support/*
Рольшлюз к выпуску новой версиилиния жизни уже выпущенной версии
Создаётсяиз develop перед выпускомиз тега версии в main, когда понадобился патч
Живётдо выпуска, затем удаляетсямесяцы/годы
Принимаетподготовку релиза и fix/* до заморозкиfix/* и feature/* (заказные доработки)
РезультатvX.Y.0 в mainvX.Y.1, vX.Y.2, …

Жизненный цикл:

Правила support/*:

  1. Ветка support/vX.Y.x создаётся по необходимости — когда в выпущенную версию требуется внести изменение, — от тега соответствующей версии в main (vX.Y.0 или последнего патча этой линии, если она уже существовала). Заранее, в момент выпуска релиза, ветка не создаётся: пока патчи не нужны, линия поддержки представлена самим тегом.
  2. Исправление для поддерживаемой версии делается в fix/* от support/vX.Y.x и возвращается в неё же. Новая функциональность (например, уникальная доработка по запросу клиента) делается в feature/* от support/vX.Y.x и также возвращается в неё.
  3. Патч-релиз (или релиз с доработкой) собирается, подписывается и тегируется (vX.Y.1) непосредственно из support/vX.Y.x. support/* в main не сливается — main остаётся линией мажорной линии разработки, а патчи поддерживаемых версий адресуются своими тегами.
  4. Исправление, актуальное и для развиваемой версии, переносится в develop (и в другие живые support/*) отдельным fix/* — автором исправления, а не автоматикой.
  5. Срок поддержки каждой support/* определяется организационно. По его истечении ветка переводится в архивное состояние (запрет push) и не удаляется.

Considered Alternatives

Alternative 1: Trunk-based development (одна ветка main + короткоживущие feature-ветки)

  • Pros: минимум веток и правил, быстрая интеграция, нет долгоживущего develop.
  • Cons: нет точки заморозки для подготовки и ручной подписи релиза по всем платформам — релиз пришлось бы готовить прямо в main, что нарушает роль main как истории выпущенных состояний. Нет линии для патчей старых версий.
  • Reason for rejection: не даёт окна заморозки, обязательного для многоплатформенной ручной подписи релиза.

Alternative 2: GitHub Flow (только main + feature/*, релиз — тег в main)

  • Pros: проще GitFlow, привычен небольшим командам.
  • Cons: функциональность и исправления попадают в main напрямую, поэтому main перестаёт быть историей выпущенных состояний. Нет ветки заморозки и нет поддержки старых версий.
  • Reason for rejection: отсутствует шаг подготовки релиза и линия поддержки выпущенных версий.

Alternative 3: Модель без support/* (патчи старых версий через release/* от тега)

  • Pros: на один тип ветки меньше. release/* уже умеет собирать и подписывать релиз.
  • Cons: release/* по назначению короткоживущая и удаляется после выпуска — при каждом патче старой версии её пришлось бы воссоздавать от тега, теряя историю исправлений этой линии; непонятно, где живёт актуальное состояние поддерживаемой версии между патчами. Смешиваются две разные роли — подготовка нового выпуска и сопровождение старого.
  • Reason for rejection: долгосрочная поддержка требует именно долгоживущей ветки с собственной историей исправлений.

Alternative 4: Полный GitFlow (с отдельным типом hotfix/*)

  • Pros: индустриальный стандарт, покрывает больше частных случаев.
  • Cons: hotfix/* не отличается от fix/* ни правами, ни правилами приёмки — различается только ветка-источник, которая и так фиксирована в таблице. Лишний тип ветки удваивает количество правил автоматизации без новой семантики.
  • Reason for rejection: hotfix полностью покрывается fix/* от release/*/support/*.

Consequences

Positive

  • Роли разделены: разработка (develop, feature/*, fix/*), выпуск (release/*), история выпусков (main), сопровождение (support/*).
  • release/* даёт явное окно заморозки для ручной подписи артефактов, не блокируя develop.
  • Выпущенные версии можно патчить годами, не влияя на развиваемую линию.
  • Набор веток минимален: нет типов веток, дублирующих чужое назначение.

Negative

  • Больше веток и правил, чем в trunk-based/GitHub Flow — требуется документация в CONTRIBUTING.ru.md и RELEASING.ru.md.
  • Одновременно живые support/* требуют переноса исправлений в несколько линий вручную — трудозатраты растут линейно с числом поддерживаемых версий.
  • Каждая живая support/* расширяет матрицу сборки и проверок CI.

Risks and Mitigations

  • Risk: исправление внесено в support/* и не перенесено в develop — дефект возвращается в следующей версии.
    • Mitigation: обязательная связанная задача на перенос в develop при приёмке fix/* в support/*. Проверка закрытия связанных задач перед созданием release/*.
  • Risk: ветки создаются не по конвенции (feature/*, fix/*, release/*, support/*), что ломает автоматизацию.
    • Mitigation: проверка имени ветки и ветки-источника в CI.
  • Risk: число живых support/* растёт неограниченно.
    • Mitigation: явный срок поддержки для каждой минорной версии. Перевод просроченных support/* в архивное состояние.

References