Mid Авторский

Trunk-Based Development

Почему короткоживущие ветки и частая интеграция ускоряют доставку

Первоисточник

Trunk Based Development

Paul Hammant, 2017

Проблема длинных веток

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

GitFlow, популяризированный Vincent Driessen в 2010 году, предлагал целую иерархию веток: feature branches, develop, release, hotfix — и в своё время это выглядело элегантно. Но на практике GitFlow хорошо работал только в мире, где релизы происходили раз в месяц или реже. Когда индустрия сдвинулась к continuous delivery и деплоям несколько раз в день, GitFlow стал тормозом, а не помощником.

Trunk-Based Development: идея

Trunk-Based Development (TBD) — это подход, при котором все разработчики интегрируют свои изменения в одну основную ветку (trunk, main, master) часто и маленькими порциями. Paul Hammant, один из главных евангелистов TBD, описывает его правила так: ветки живут не дольше одного-двух дней, каждый коммит в trunk проходит автоматические проверки, и trunk всегда находится в deployable состоянии.

В самой чистой форме TBD разработчики коммитят напрямую в trunk — так работают в Google, где более 25 000 инженеров пишут код в один монорепозиторий. В более распространённом варианте — scaled trunk-based development — разработчики создают короткоживущие ветки (на день-два), которые проходят CI и code review, и затем мержатся в main. Ключевое отличие от GitFlow — не в наличии или отсутствии веток, а в их продолжительности жизни.

Что говорят данные DORA

DORA выделяет trunk-based development как одну из ключевых технических практик, которые предсказывают производительность команды. Анализ данных из State of DevOps Report за 2016–2017 годы показал чёткую закономерность: команды достигают более высоких показателей скорости доставки и стабильности, если в их репозитории не более трёх активных веток одновременно и если ветки мержатся в trunk минимум раз в день.

Report 2021 года добавил к этому количественную оценку: элитные команды, которые выполняли свои SLO по надёжности, в 2.3 раза чаще использовали trunk-based development по сравнению с отстающими. А отстающие команды, в свою очередь, с большей вероятностью использовали долгоживущие ветки и откладывали мерж.

Здесь важно понять причинно-следственную связь: TBD работает не потому, что «мерж в main» обладает магическими свойствами, а потому, что короткоживущие ветки вынуждают разработчиков делать маленькие, атомарные изменения. Маленькие изменения проще ревьюить (PR на 50 строк вместо 2000), проще тестировать (меньше поверхность потенциальных поломок), проще откатывать (если что-то пошло не так — revert одного коммита, а не археологические раскопки в мега-мерже).

Как работают в Google и Meta

Google — канонический пример trunk-based development в масштабе. Более 25 000 инженеров работают в одном монорепозитории, и почти весь код живёт в trunk. Разработчики отправляют свои изменения напрямую в main (после code review и автоматических проверок), а feature flags управляют тем, какая функциональность доступна пользователям. Книга «Software Engineering at Google» описывает, что такой подход требует серьёзной инфраструктуры — мощного CI, хороших инструментов для code review (Critique), системы feature flags — но окупается за счёт минимальных конфликтов и постоянной интеграции.

Meta использует похожую модель: разработчики пушат изменения в main, гейтинг-система решает, какие изменения попадут в следующий релиз, а каждое изменение проходит автоматические проверки и canary deployment.

Предварительные условия

TBD — не тот подход, который можно внедрить в понедельник утром без подготовки. Он требует нескольких предварительных условий, и если их не выполнить, вы получите не continuous delivery, а continuous chaos.

Быстрый CI — если сборка и тесты занимают 40 минут, разработчики не смогут мержить в main несколько раз в день. CI для trunk-based development должен укладываться в 10 минут, а лучше в 5, иначе очередь мержей превращается в пробку.

Автоматические тесты с хорошим покрытием — когда вы мержите в main несколько раз в день, у вас нет времени на ручной QA каждого изменения. Автоматические тесты — ваша сеть безопасности, и она должна быть надёжной. О проблеме flaky-тестов — в следующей главе.

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

Культура маленьких изменений — это, пожалуй, самое сложное условие, потому что оно требует изменения привычек. Разработчики, привыкшие к большим feature-веткам, должны научиться декомпозировать задачи на маленькие, самодостаточные куски, каждый из которых можно мержить в main, не ломая его.

Частые возражения

«А как мы будем деплоить недоделанную фичу?» — Feature flags. Код попадает в main и в продакшен, но скрыт за флагом. Когда фича готова, вы включаете флаг — и это уже релиз, а не деплой. О разнице между деплоем и релизом — в главе про feature flags.

«У нас недостаточно тестов» — это проблема, которую TBD обнажает, но не создаёт. Если у вас нет тестов, вам и с GitFlow плохо — вы узнаёте о проблемах позже и с большим трудом. TBD мотивирует инвестировать в тесты, потому что без них он действительно опасен.

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

С чего начать

Если вы сейчас работаете с долгоживущими ветками и хотите двигаться к TBD, начните с одного правила: ветка не живёт дольше двух рабочих дней. Если за два дня изменение не готово к мержу — оно слишком большое, его нужно разбить на части. Через месяц такой практики вы обнаружите, что конфликтов стало меньше, ревью стали быстрее (потому что PR стали меньше), и у вас появилось гораздо более точное представление о том, в каком состоянии находится кодовая база — потому что main всегда отражает реальность, а не фантазию трёхмесячной давности.