Git стал таким же привычным явлением, как электричество в розетке. Его можно использовать совершенно по-разному — он либо сделает вашу жизнь удобнее, либо причинит боль и доставит кучу проблем.

Привет, меня зовут Макс Мартынов и я ведущий разработчик в Атвинте. В этой статье расскажу про наш подход к Git workflow, в котором баги одной фичи не блокируют деплой другой. Существует множество подходов и наш, конечно, не единственно верный.

Содержание

  1. Выбрать workflow — значит регламентировать путь фичи

  2. Путь к workflow: почему изначальная модель нам не подошла

  3. Наш подход: каждая фича идёт в продакшен по готовности

  4. Вместо послесловия

Выбрать workflow — значит регламентировать путь фичи

То, как вы работаете с Git, касается не только кода, но и коммуникаций, и процессов. Workflow — это договорённость между разработчиками, тестировщиками, девопсами и менеджментом о том, как именно вы будете обращаться с кодом. По сути, ваш внутренний регламент. И если его нет, рано или поздно это обернётся головной болью.

Но если Git — это стандарт, с которым работают все, то значит и этот регламент уже давно придуман? Отчасти так и есть. Ещё в 2010 году Vincent Driessen описал в своём блоге, каким, по его мнению, должен быть Git Flow. Со временем появились и другие подходы — например, GitHub Flow, GitLab Flow и Trunk-Based Development.

Универсального Git workflow, который подходит всем, не существует. То, что отлично работает в стартапе с двумя разработчиками, умрёт под грузом бюрократии в enterprise-продукте с распределённой командой. А процесс, идеальный для мобильного приложения, где релизы проходят модерацию в сторах, может оказаться избыточным и медленным для веб-сервиса, который регулярно обновляется незаметно для пользователя.

Когда мы говорим «выбрать workflow», мы на самом деле подразумеваем «регламентировать наш путь фичи». И здесь внутри команды стоит обсудить несколько вопросов:

  • Как часто мы хотим/можем/должны выкатывать изменения — раз в месяц, раз в день или сразу по готовности?

  • Сколько человек работает над одной кодовой базой?

  • Кто отвечает за релиз?

  • Как мы поступаем со срочными фиксами — они идут в обход workflow или подчиняются тем же правилам?

Ваш workflow — это всегда компромисс между скоростью релиза, удобством разработчиков и контролем качества. Готовые фичи не должны ждать релиза по три недели, но и пользователи не обязаны страдать от багов на продакшене.

Путь к workflow: почему изначальная модель нам не подошла

Наша небольшая команда (от 2 до 4 разработчиков, 1 QA и 1 менеджер) занимается доработкой активно развивающегося веб-сервиса. У нас есть несколько требований к процессам:

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

  2. если фичи и/или багфиксы не связаны логически, то они не должны зависеть друг от друга;

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

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

Что мы тут видим:

  • функциональные ветки (фича-ветки) начинаются от develop;

  • в master фичи попадают через релизные ветки, основанные на develop;

  • срочные багфиксы идут в develop и в master, минуя релизные ветки.

К чему это может привести, покажу на примерах, но перед этим обозначу вводные:

  • сервис уже работает в продакшене;

  • есть два окружения — продакшен (ветка master) и дев (ветка develop);

  • от бизнеса есть запрос на три доработки (далее: фича1, фича2 и фича3) в бизнес-логике сервиса;

  • в команде два разработчика (далее: разраб1 и разраб2) и один QA;

  • разраб1 работает над фичей1, разраб2 — над фичей2, первый из них, кто освободится, работает над фичей3 (так решил бизнес и/или менеджмент).

Оба разработчика создали свои ветки от develop и ведут в них разработку:

Разраб1 закончил фичу1 и залил в develop:

QA её проверил, нашёл кучу багов и вернул на доработку разрабу1. В это время разраб2 закончил фичу2 и готов залить её в develop. А как дальше — ему можно это сделать или же нет? Оба варианта нам не подходят — и вот почему.

Например, мы разрешили ему это сделать:

QA проверяет и не находит багов. Казалось бы, фича готова, надо бы её в master. Однако по workflow Винсента мы должны создать релизную ветку от develop, а там у нас фича1 и её баги. Не можем же мы их отправить на продакшен. Получается требование №1 не соблюдается — фича готова, но заливать её нельзя.

Аналогичную проблему получим, если всё же не разрешим ему заливать:

В этом случае ещё добавится простой в работе QA. Ведь он не может проверять фичу, которая не была залита. Так как разраб2 первым освободился, он берёт в работу фичу3 и согласно workflow Винсента создаёт ветку от develop:

Теперь ветка с фичей3 содержит в себе баги фичи1, что нарушает наше требование №2. Если допустить, что разраб2 закончит ещё одну фичу раньше, чем разраб1 разберётся с багами, то у нас будет уже две готовых к релизу фичи, которые ждут третью:

Если в этот самый момент появится срочный баг с продакшена, то согласно workflow Винсента мы должны будем залить его в develop, а затем в master.

Даже если QA подтвердит, что фикс рабочий — это будет справедливо только для develop, так как на данном этапе код в этом окружении сильно отличается от кода в продакшене. Получается, мы не можем гарантировать, что этот фикс сработает в master точно так же, как develop, и/или не породит новых багов. Это, в свою очередь, нарушает наше требование №3.

Наш подход: каждая фича идёт в продакшен по готовности

Наше решение на данный момент устраивает и бизнес, и менеджмент, и команду разработки.

Наши правила:

  1. три окружения: release, stage и develop;

  2. функциональные ветки (с фичами/фиксами) начинаются только от release, так как должны быть основаны на надёжном и проверенном коде (см. требование №2);

  3. по готовности функциональные ветки сначала заливаются в develop;

  4. если QA находит баги, то фиксы по этим багам производятся в функциональной ветке и по готовности снова заливаются в develop;

  5. как только QA подтверждает, что на develop багов нет — функциональная ветка заливается в stage;

  6. если QA подтверждает, что багов в stage нет, а они могут появиться, только если мерж-конфликт был решён иначе, чем мерж в develop — то stage заливается в release (см. требование №1);

  7. stage заливается в release обязательно с флагом --ff-only, что позволяет иметь тестовое окружение, которое по коду полностью идентично продакшену (см. требование №3);

  8. функциональные ветки заливаются в develop и stage обязательно с флагом --no-ff, что позволяет в случае нештатных ситуаций убрать фичу из нужного окружения, отменяя только коммиты слияния;

  9. очень срочные хотфиксы багов с продакшена допускается залить одновременно в stage и develop (QA сразу проверяет на stage) только по согласованию с ведущим разработчиком, QA и менеджментом, так как если QA найдёт баги на stage, то это заблокирует заливку и тестирование функциональных веток в этом окружении.

Рассмотрим ту же самую ситуацию с двумя разработчиками, тремя фичами и одним хотфиксом, но уже по нашим правилам.

Оба разработчика начали ветки от release:

Разраб1 закончил фичу1 и залил в develop:

Аналогичный сценарий — фича1 возвращена на доработку из-за багов, а разраб2 закончил фичу2 и залил в develop:

Фича2 проверена, багов нет, а значит, разраб2 заливает её в stage и приступает к фиче3:

Далее фича3 готова и заливается в develop, а фича2 проверена на stage, значит ветку release можно «перемотать» (fast-forward) до этого же состояния:

Фича3 также проверена в develop и заливается в stage, а разраб1, наконец, разобрался с багами и отдал фичу1 в тестирование на develop:

Далее по сценарию у нас был хотфикс:

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

Единственное «узкое место», когда релиз в продакшен невозможен — это наличие багов на stage. Именно по этой причине в наших правилах есть пункт про заливку функциональных веток только с флагом --no-ff, чтобы в ветки окружений попадали только мерж-коммиты, которые легко отменить (revert или reset, как удобно команде).

Вместо послесловия

Идеального Git workflow нет, но существует удобный workflow для вашей команды.

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

Если вы только начинаете выстраивать свои процессы — обсуждайте, пробуйте и ищите своё. Берите за основу чужие подходы (наш, например), но всегда примеряйте их на свою команду и на ваши реальные, уже случившиеся кейсы. В конечном счёте, Git workflow — это просто инструмент. Помните: молоток не виноват в том, что вы ударили им по пальцу. Каждому инструменту — своё применение.

Всем лёгких и быстрых релизов!