В этой статье я расскажу про то как я пытался создать бета-стенд и встроить его в обычный gitflow. Совместно с читателями мы пройдем путь от проблем связанных с этим до новой схемы работы с гитом.
Наш Gitflow
В нашей компании мы использовали всем известный gitflow. Те, кто знает, что это такое может сразу перейти к следующему разделу. Для тех, кто не знает, расскажу.
Основная работа ведется в development ветке. Под каждую новую фичу создается отдельная feature-ветка. При слиянии feature-ветки в development осуществляется сборка и выкладка приложения на тестовый стенд, где QA специалисты проверяют её работу.
Под каждый найденный баг от development создаётся hotfix ветка, в которой он устраняется. Далее hotfix ветка сливается обратно в development — и все по новой: тестовый стенд обновляется и QA снова проверяет.
Когда development ветка отдебажена и в ней накопилось достаточное количество фич для релиза создается release-ветка. В ней всегда находится код, который в любой момент можно смержить в мастер и тем самым обновить продакшн стенд.
Предпосылки создания бета-стенда
За счет наличия петли в описанной схеме: выкатили на тестовый стенд, проверили, исправили, снова выкатили, исчезает огромное количество ошибок. Но, увы, не все.

Конечно наш QA хорошо делает свою работу, количество багов по мере приближения к мастеру уменьшается, но устранить их все не получается по следующим причинам:
Реальное поведение пользователей гораздо сложнее и непредсказуемей синтезированных тест-кейсов.
Не учитываются особенности реальных пользователей (устройство, ОС, браузер, персональные настройки и т.п.).
- Тестирование осуществляется на тестовом стенде с тестовой базой данных, а она отличается от реальной базы данных (имеет артефакты).
Цели и задачи бета-стенда
Устранить указанные проблемы с тестированием мы решили с помощью beta стенда — т.е. дать нашим внутренним пользователям системы, заказчикам, доверенным клиентам и прочим лицам ранний доступ.
Теперь после development стенда, где их проверяет QA специалист, новые фичи попадают на beta стенд, где с ними работают реальные пользователи. Они оповещаются о начале бета-тестирования сразу после обновления бета-стенда. Ошибки в бета-версии приложения отображаются в системе логирования. Периодически они фиксятся и бета-стенд обновляется. Когда ошибок больше не возникает создается релиз. Таким образом широкая аудитория пользователей получает стабильную версию приложе��ия.
На этапе беты тестирования есть возможность получить обратную связь от пользователей, узнать будет ли введеный функционал удобен широкой аудитории и что нужно изменить. Beta является своего рода пилотной версией приложения.
Такая схема соответствует основным стадиям разработки: альфа, бета и релиз.
Мы обсудили аспект Continuous Delivery, т.е. в какой момент создавать предрелизы и релизы. Самое время перейти к Continuous Integration, т.е. разработать саму схему работы с git с учетом beta.
Попытка внедрить бета в Gitflow
Первое, что приходит на ум — использовать release ветку для деплоя на beta. Release ветка в таком случае рассматривается как намерение релиза, т.е. говоря иначе — это предрелиз (почти что бета). А что гармонично получается, и ничего менять в gitflow не надо. Нужно только накрутить новое правило в CD для выкладки билда на создание/изменение release ветки и все.
Такая схема приблизительно будет выглядеть так:

Замечание: На графе пунктирными линями обозначены BASE коммиты.
Что происходит на графе?
- Под задачу создается feature ветка от development
- Ветка feature сливается обратно в development ветку. При изменении development ветки осуществляется выкладка на development стенд. QA Специалист начинает тестирование.
- Под найденные ошибки от development создается hotfix ветка, в которой ошибки устраняются и она вмерживается обратно. Если ошибок не найдено, от development ветки создается release ветка, чтобы зафиксировать стабильное состояние development ветки.
- При создании release ветки создаётся сборка приложения (release candidate), которая выкатывается на beta стенд. Пользователям рассылается оповещение (например в таск менеджере создается коммент у соответствующих задач или в мессенджере создается сообщение).
- Когда release ветка отдебажена она сливается в master ветку, происходит продакшн сборка приложения и выкатывание на продакшн.
- Если на бою находится баг, от мастера создается hotfix ветка, в которой он исправляется. Далее она протягивается по всем веткам путем последовательного слияния.
На первый взгляд рабочая схема. Теперь давайте рассмотрим её плюсы и минусы.
Плюсы:
Небольшое количество основных веток, которые нужно поддерживать в актуальном состоянии. Большую часть времени их будет всего 2-е: dev и master.
- Есть возможность обновлять основные ветки прямо из github при помощи создания PR — отпадает необходимость стягивать ветку к себе, чтобы произвести ребейз. Тем не менее потребуется стягивать ветку к себе чтобы проставить новую версию, а также в случае конфликта.
Минусы:
Первая проблема возникает когда требуется внести hotfix в master, не дожидаясь подхода нового релиза. Ладно, можно вмержить hotfix в master и затем в release и dev в общем-то это не проблема (разве что с мержами запутаться можно). Проблема появляется когда релизной ветки нет. А как обновить бета-окружение без релизной ветки, а никак, разве что вручную. Не правильно как-то, у нас же настроена CD схема, скажете вы, и я соглашусь.
Есть временной лаг при внесении hotfix в основные ��етки. На представленной схеме флоу внесения hotfix в мастер выглядит так: hotfix → master → dev → release. А должно быть так: hotfix → master → release → dev, поскольку release важнее dev и в ней изменения должны появиться раньше. Смержить dev, при внесении в него hotfix, в ветку release сразу может не получиться — dev может содержать изменения, которые не должны попасть в текущую (открытую) release ветку. Таким образом нужно ждать следующего релиза прежде чем hotfix появится на бета-стенде. Или например если в beta потребуется внести hotfix будет такой флоу: hotfix → beta → master → dev, а должен быть такой: hotfix → beta → dev → master. В этой схеме нарушается принцип причинности.
Регрессионное обновление основных веток (release и dev) происходит через merge commit, что усложняет накладывание CD схемы на CI. Также в этой схеме легко запутаться — большое количество степеней свободы. Например можно hotfix слить в master, потом слить в release ветку, но забыть о dev.
Автоматическое назначение версии невозможно из-за обновляющих ветки "слева" мерж-коммитов. Придется вручную проставлять номера версии, в которых можно запутаться. Потребуется делать версионный коммит вручную, в нужной ветке. Также существует вероятность того, что мейнтейнер забудет установить тег и тогда приложение будет выкачено со старой версией, что приведет к некорректному логгированию ошибок.
- Неочевидный способ обновления бета-сцены. Релизная ветка после слияния удаляется — в этом особенность схемы.
Новая CI схема с бетой
Давайте попробуем избавиться от этих проблем.
Поскольку beta стенд существует всегда — нужно сделать так, чтобы release ветка существовала всегда. Давайте назовем её beta. Тогда станет возможным вносить hotfix на бета-стенд в отсутствие release ветки. Но в таком случае проблема с избыточным количеством merge commit усилится, поскольку количество веток увеличилось. Для решения этой проблемы достаточно отказаться от обновления основных веток стратегией merge.
Графически отобразить получившийся флоу можно следующим образом.

Замечание: На графе пунктирными линями обозначены BASE коммиты.
Что происходит на графе?
- Под задачу создается feature ветка, также как и в случае gitlow.
- Ветка feature сливается обратно в development ветку.
- От development ветки создается pre-release ветка, чтобы зафиксировать стабильное состояние development ветки. Осуществляется сборка и выкладка на development стенд. QA специалист начинает тестирование.
- Если были найдены баги — от pre-release ветки создается hotfix ветка, в ней баги устраняются и ветка сливается обратно. После этого все ветки левее pre-release ребезятся от неё. Если фичи прошли проверку QA и готовы к публичному тестированию мы вмерживаем pre-release ветку в beta ветку
- При изменении beta ветки создается бета-сборка приложения, которая выкатывается на бета-стенд. Пользователи получают ранний доступ к фичам. В процессе пользования бетой в системе логирования аккумулируются возникающие ошибки. Которые периодически исправляются с помощью hotfix ветки. Далее hotfix ветка вмерживается обратно в beta ветку — стенд обновляется. На каждое изменение beta ветки основные ветки слева нужно ребейзить от неё. Если уже существуют pre-release ветки — их нужно отребейзить от dev.
- Когда в бета-версии приложения устранены все ошибки, создается release ветка.
- Ветка release вмерживается в мастер.
- Если в продакшне находится баг, от master создается hotfix, в котором он исправляется. Далее hotfix протягивается по всем веткам путем переноса BASE основных веток на HEAD веток, от которых те были созданы.
Замечание: обновлением основных веток на всех стадиях (pre-alpha, alpha, beta...) занимается ответственный за релиз (мейнтейнер проекта). Члены команды работают только c dev веткой.
Схема выглядит довольно круто, не правда ли? Давайте рассмотрим её плюсы и минусы и сравним её с предыдущей.
Плюсы:
Более четкая роль беты в CI/CD. Не будет проблем с хотфиксами и протягиванием изменений через нее. Т.е. принцип причинности не нарушается, флоу будет таким: hotfix → master → beta → dev.
Возможно автоматизированное проставление версий и сбор changelog, что особенно важно для библиотек. Нет вероятности ошибиться.
Тесты лишний раз не запускаются, что ускоряет процесс принятия изменений.
Нет лишних мерж коммитов.
- CI / CD полностью соответствует стадиям разработки (wikipedia) (pre-alpha, alpha, beta, release-candidate, release, post-release).
Минусы:
Большое количество веток. Но это не страшно поскольку ими занимается ответственный за релиз. Также у каждой ветки своя роль в автоматизированном тестировании, об этом пару слов напишу ниже.
- Нет возможности управлять актуальностью веток через github. Тем не менее в случае merge стратегии (которая используется в предыдущей схеме) могут возникать конфликты, из-за которых все равно придется стягивать ветку на локальную машину.
Связь со стадиями разработки
Предложенная схема полностью отвечает всем стадиям разработки программного обеспечения.
пре-альфа — фичи сделаны, но еще не протестированы QA специалистом. В них может содержаться большое количество багов.
альфа — на этом этапе приложение собирается и выкатывается на development стенд, где QA мануальщик проверяет его работу.
бета — условно стабильная сборка приложения, протестированная QA специалистом. Сборка на этом этапы выкладывается на бета стенд для тестирования реальными пользователями.
релиз кандидат — сборка прошедшая все этапы тестирования и теперь ожидающая своего релиза.
- релиз — стабильная версия приложения.
У наших проектов большое количество автоматизированных тестов. Выполнение всех тестов занимает примерно 1 час. Чтобы ускорить принятие PR в ветках на каждом этапе внедрения фичи мы выполняем только важные для этого этапа тесты. Например для принятия кода в pre-alpha мы запускаем самые простые тесты: lint и unit. На этапе принятия beta выполняются также интеграционные тесты. На этапе release-candidate помимо озвученных тестов запускаются также acceptance тесты. Причем на этом этапе тесты запускаются на раннерах с разными ОС и под разными браузерами. После создания релиза (этап post-release не обозначенный в схеме) запускаются smoke тесты.
Задавайте вопросы в комментариях, если что-то интересное осталось за кадром.
Ссылки:
