В этой статье я расскажу про то как я пытался создать бета-стенд и встроить его в обычный 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 коммиты.


Что происходит на графе?


  1. Под задачу создается feature ветка от development
  2. Ветка feature сливается обратно в development ветку. При изменении development ветки осуществляется выкладка на development стенд. QA Специалист начинает тестирование.
  3. Под найденные ошибки от development создается hotfix ветка, в которой ошибки устраняются и она вмерживается обратно. Если ошибок не найдено, от development ветки создается release ветка, чтобы зафиксировать стабильное состояние development ветки.
  4. При создании release ветки создаётся сборка приложения (release candidate), которая выкатывается на beta стенд. Пользователям рассылается оповещение (например в таск менеджере создается коммент у соответствующих задач или в мессенджере создается сообщение).
  5. Когда release ветка отдебажена она сливается в master ветку, происходит продакшн сборка приложения и выкатывание на продакшн.
  6. Если на бою находится баг, от мастера создается hotfix ветка, в которой он исправляется. Далее она протягивается по всем веткам путем последовательного слияния.

На первый взгляд рабочая схема. Теперь давайте рассмотрим её плюсы и минусы.


Плюсы:


  1. Небольшое количество основных веток, которые нужно поддерживать в актуальном состоянии. Большую часть времени их будет всего 2-е: dev и master.


  2. Есть возможность обновлять основные ветки прямо из github при помощи создания PR — отпадает необходимость стягивать ветку к себе, чтобы произвести ребейз. Тем не менее потребуется стягивать ветку к себе чтобы проставить новую версию, а также в случае конфликта.

Минусы:


  1. Первая проблема возникает когда требуется внести hotfix в master, не дожидаясь подхода нового релиза. Ладно, можно вмержить hotfix в master и затем в release и dev в общем-то это не проблема (разве что с мержами запутаться можно). Проблема появляется когда релизной ветки нет. А как обновить бета-окружение без релизной ветки, а никак, разве что вручную. Не правильно как-то, у нас же настроена CD схема, скажете вы, и я соглашусь.


  2. Есть временной лаг при внесении 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. В этой схеме нарушается принцип причинности.


  3. Регрессионное обновление основных веток (release и dev) происходит через merge commit, что усложняет накладывание CD схемы на CI. Также в этой схеме легко запутаться — большое количество степеней свободы. Например можно hotfix слить в master, потом слить в release ветку, но забыть о dev.


  4. Автоматическое назначение версии невозможно из-за обновляющих ветки "слева" мерж-коммитов. Придется вручную проставлять номера версии, в которых можно запутаться. Потребуется делать версионный коммит вручную, в нужной ветке. Также существует вероятность того, что мейнтейнер забудет установить тег и тогда приложение будет выкачено со старой версией, что приведет к некорректному логгированию ошибок.


  5. Неочевидный способ обновления бета-сцены. Релизная ветка после слияния удаляется — в этом особенность схемы.

Новая CI схема с бетой


Давайте попробуем избавиться от этих проблем.


Поскольку beta стенд существует всегда — нужно сделать так, чтобы release ветка существовала всегда. Давайте назовем её beta. Тогда станет возможным вносить hotfix на бета-стенд в отсутствие release ветки. Но в таком случае проблема с избыточным количеством merge commit усилится, поскольку количество веток увеличилось. Для решения этой проблемы достаточно отказаться от обновления основных веток стратегией merge.


Графически отобразить получившийся флоу можно следующим образом.



Замечание: На графе пунктирными линями обозначены BASE коммиты.


Что происходит на графе?


  1. Под задачу создается feature ветка, также как и в случае gitlow.
  2. Ветка feature сливается обратно в development ветку.
  3. От development ветки создается pre-release ветка, чтобы зафиксировать стабильное состояние development ветки. Осуществляется сборка и выкладка на development стенд. QA специалист начинает тестирование.
  4. Если были найдены баги — от pre-release ветки создается hotfix ветка, в ней баги устраняются и ветка сливается обратно. После этого все ветки левее pre-release ребезятся от неё. Если фичи прошли проверку QA и готовы к публичному тестированию мы вмерживаем pre-release ветку в beta ветку
  5. При изменении beta ветки создается бета-сборка приложения, которая выкатывается на бета-стенд. Пользователи получают ранний доступ к фичам. В процессе пользования бетой в системе логирования аккумулируются возникающие ошибки. Которые периодически исправляются с помощью hotfix ветки. Далее hotfix ветка вмерживается обратно в beta ветку — стенд обновляется. На каждое изменение beta ветки основные ветки слева нужно ребейзить от неё. Если уже существуют pre-release ветки — их нужно отребейзить от dev.
  6. Когда в бета-версии приложения устранены все ошибки, создается release ветка.
  7. Ветка release вмерживается в мастер.
  8. Если в продакшне находится баг, от master создается hotfix, в котором он исправляется. Далее hotfix протягивается по всем веткам путем переноса BASE основных веток на HEAD веток, от которых те были созданы.

Замечание: обновлением основных веток на всех стадиях (pre-alpha, alpha, beta...) занимается ответственный за релиз (мейнтейнер проекта). Члены команды работают только c dev веткой.


Схема выглядит довольно круто, не правда ли? Давайте рассмотрим её плюсы и минусы и сравним её с предыдущей.


Плюсы:


  1. Более четкая роль беты в CI/CD. Не будет проблем с хотфиксами и протягиванием изменений через нее. Т.е. принцип причинности не нарушается, флоу будет таким: hotfix > master > beta > dev.


  2. Возможно автоматизированное проставление версий и сбор changelog, что особенно важно для библиотек. Нет вероятности ошибиться.


  3. Тесты лишний раз не запускаются, что ускоряет процесс принятия изменений.


  4. Нет лишних мерж коммитов.


  5. CI / CD полностью соответствует стадиям разработки (wikipedia) (pre-alpha, alpha, beta, release-candidate, release, post-release).

Минусы:


  1. Большое количество веток. Но это не страшно поскольку ими занимается ответственный за релиз. Также у каждой ветки своя роль в автоматизированном тестировании, об этом пару слов напишу ниже.


  2. Нет возможности управлять актуальностью веток через github. Тем не менее в случае merge стратегии (которая используется в предыдущей схеме) могут возникать конфликты, из-за которых все равно придется стягивать ветку на локальную машину.

Связь со стадиями разработки


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


  • пре-альфа — фичи сделаны, но еще не протестированы QA специалистом. В них может содержаться большое количество багов.


  • альфа — на этом этапе приложение собирается и выкатывается на development стенд, где QA мануальщик проверяет его работу.


  • бета — условно стабильная сборка приложения, протестированная QA специалистом. Сборка на этом этапы выкладывается на бета стенд для тестирования реальными пользователями.


  • релиз кандидат — сборка прошедшая все этапы тестирования и теперь ожидающая своего релиза.


  • релиз — стабильная версия приложения.

У наших проектов большое количество автоматизированных тестов. Выполнение всех тестов занимает примерно 1 час. Чтобы ускорить принятие PR в ветках на каждом этапе внедрения фичи мы выполняем только важные для этого этапа тесты. Например для принятия кода в pre-alpha мы запускаем самые простые тесты: lint и unit. На этапе принятия beta выполняются также интеграционные тесты. На этапе release-candidate помимо озвученных тестов запускаются также acceptance тесты. Причем на этом этапе тесты запускаются на раннерах с разными ОС и под разными браузерами. После создания релиза (этап post-release не обозначенный в схеме) запускаются smoke тесты.


Задавайте вопросы в комментариях, если что-то интересное осталось за кадром.


Ссылки:


Поделиться с друзьями
-->

Комментарии (15)


  1. lumag
    09.12.2016 10:08

    git rebase — это плохо. Теряется контекст разработки, затрудняется работа git bisect и т. п.


    1. MadWombat
      14.12.2016 12:45

      Гм. А без rebase порядок комитов перестает быть хронологическим. И с rebase и без есть проблемы.


    1. ajile
      14.12.2016 14:01

      Конечно ребейз на «публичных» ветках делать плохо и не потому что теряется контект разработки (даже напротив — история становится чище), а просто потому что приходится делать форс пуши. Они в свою очередь плохи тем, что позволяют менять историю коммитов (удалять, расщеплять, объединять их и т.п.), что может приводить к сложным конфликтам при дальнейшем протаскивании изменений «наверх».

      Поэтому в этой схеме есть некоторые ограничения: 1) при ребейзе нельзя входить в интерактивный режим и вручную править коммиты (ребейз используется только для изменения основы ветки); 2) обновлением основных веток занимается один человек — мейнтейнер проекта (члены команды работают только с dev веткой).

      Таким образом использование rebase в этой схеме безопасно.


  1. Ghedeon
    09.12.2016 12:24
    +1

    Вроде есть устоявшаяся терминология, что bugfix это до релиза и hotfix — после. Как-то сбивает с толку, когда hotfix появляется из dev ветки. Взять даже эту картинку из каноничного http://nvie.com/posts/a-successful-git-branching-model/

    Hotfix
    image


    1. ajile
      14.12.2016 14:23

      1. Название bugfix подразумевает наличие какого-то бага, а hotfix — срочную правку, что больше подходит для этой схемы. От противного: для внесения изменения (не исправление бага) на beta стенд, потребуется создать bugfix ветку.

      2. Поскольку отличий с точки зрения процесса принятия изменений, в одну из основных веток, между bugfix и hotfix ветками нет (тесты запускаются одни и те же, правила мержа те же), то также нет смысла именовать ветки по разному (в зависимости от того куда они будут сливаться).

      3. Непонятно как называть фикс-ветки (bugfix или hotfix), если они создаются для beta ветки (она же также релизится).


      1. VolCh
        16.12.2016 12:33

        1. fix подразумевает исправление ошибки, bugfix — плановое, hotfix — срочное, немедленное

        2. Смысл имеется. Как раз для того, чтобы было ясно куда фикс будет вливаться, хотя бы чтобы реджектить мерж/пулл-реквесты, отправленные не по адресу.

        3. hot подразумевает правку по живому, без прохождения полного цикла тестирования и приёмки, так что годится только для прода.



  1. nkochnev
    09.12.2016 18:42

    На бета стенде используется боевая база данных или специальная для бета стенда? Если да, то как решается проблема разных схем данных в БД для бета и прод стендов?


    1. ajile
      14.12.2016 14:33

      Отличный вопрос. У нас на бете сейчас используется продовская БД. Мы (фронтенд) выпускаем бету только после того как бекенд зарелизится. Наличие отдельной БД для бета стенда — задача нетривиальная. Мы её еще не решили.


      1. nkochnev
        14.12.2016 18:53

        А бета стенда бекенда нет?

        Я спрашиваю, потому что у себя вижу необходимость в запуске тестового (бета) окружения. Как его лучше организовать, чтобы с одной стороны протестировать приложение в более или менее реальном окружении, с другой чтобы не отправить реальным пользователям оповещалку с бета стенда, для меня пока загадка…


        1. VolCh
          16.12.2016 12:37

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


          1. ajile
            16.12.2016 14:00

            Если beta БД использовалась бы только для чтения или не синхронизировалась бы с продовской, ваше предложение подошло бы.


            Основная проблема заключается в том, что бета БД и прод БД должны содержать одни и те же данные, в любой момент времени. Но поскольку у них разные схемы возникает проблема синхронизации.


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


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


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


            3. Поскольку обе БД являются master (обеспечивают read-write доступ) в случае выхода из строя какой-то из них возможны потери данных.

            Возможно есть и другие проблемы.


        1. ajile
          16.12.2016 14:21

          Не уверен, что правильно понял проблему. Бета стенд — это полноценный прод только следующей версии для предоставления раннего доступа к новому функционалу "контрольной" группе пользователей. С него должны уходить сообщения пользователям как с прода. Если нужно протестировать приложение в более-менее реальном окружении нужно использовать dev стенд, предварительно среплецировав данные с прода, как VolCh предлагает делать.


  1. amakhrov
    09.12.2016 21:53

    Я правильно понимаю, что при изменениях в ветках справа, у вас ребейзится ветка dev, с которой активно работают разработчики?
    Как вы решаете вопрос конфликтов с локальными копиями на машинах разрабов?


  1. ajile
    14.12.2016 15:21

    Да, при изменении веток справа, мейнтейнер проекта последовательно ребейзит ветки слева до dev. Далее каждый разработчик обновляет (ребейзит) свои feature ветки от dev.


    Графически можно представить это следующим образом.


    Вносятся изменения в master