В какой ветке вести разработку? Из какой ветки деплоить на PROD? В какой ветке чинить багу, выявленную на IFT? Многие команды закрыли для себя этот вопрос, но для многих он остаётся открытым.
Этот пост не будет содержать каких-то особых ноу-хау и киллер-фич. В нём я расскажу наиболее простую и понятную (лично мне) практику релизных циклов на основе git flow. И постараюсь объяснить каждое своё решение и каждый подход.
Итак, поехали.
Сколько понадобится стендов?
В идеале, в дополнение к проду, хорошо иметь ещё два стенда: интеграционно‑функциональный (далее — IFT) и, конечно, DEV. Опишем каждый стенд, его задачи и назначение более подробно:
DEV. Стенд для разработки. Все новые фичи и починенные баги в рамках спринта в первую очередь выкатываются на этот стенд. Активное тестирование фич и исправленных багов происходит тут. Деплоит на него в основном команда разработки. Содержит синтезированные данные.
IFT. Стенд для интеграционно‑функционального тестирования. На этапе отладки фичи и исправленные баги тестируются на нём. Как правило, размещается в тестовом окружении заказчика и содержит реальные обезличенные данные. Если у Вас проект чуть сложнее CRUD, а стенда IFT у Вас нет, разрабатывать будет больно.
PROD. Продуктивный стенд, с реальными посетителями. Вершина иерархии окружения. Деплоем на такие стенды занимается специальная команда внедрения. Лучше, если у разработчиков вообще не будет к нему доступа.
Как управлять разработкой для этого минимума стендов, мы и поговорим в этом посте.
Как мы поделим спринт?
Спринт мы поделим на две неравные части: разработка и отладка.
Активная разработка — это бо́льшая часть спринта, во время которой реализуются фичи, заявленные в спринте и исправляются баги, найденные на DEV‑стенде.
Отладка — это вторая часть спринта, которая наступает после реализации фич спринта, а тестирование переносится на IFT‑стенд.
Какие основные ветки потребуются для разработки?
Итак, у нас 3 стенда. Для каждого из них лучше создать свою ветку, назвав её соответствующим образом: prod/master
, ift/test
, dev/develop
- как угодно, главное, чтобы по названиям было понятно их назначение или они хотя бы были в понятийной среде разработки. В нашем примере, ветки будут master
, test
и develop
.
Правило первое. Каждый стенд имеет свою одну-единственную мастер-ветку. Разворачивание приложения на стенд происходит только из неё.
Выглядеть это будет следующим образом:
Этот контракт необходимо поддерживать всеми силами.
Этап активной разработки.
Активная разработка ведётся только из ветки develop
. Реализуете фичу - ответвляете ветку feature/XXX
от develop
. Исправляете баг - пожалуйста, bug/XXX
. По окончании разработки, ветка вливается обратно в develop
, а оттуда уже изменения попадают на DEV-стенд.
Правило второе. Вся активная разработка ведётся в ветках, образованных от ветки
develop
, которые по окончании разработки в них вливаются обратно вdevelop
.
На DEV-стенде размещено приложение в состоянии, соответствующем текущему спринту; на IFT и PROD - предыдущему. Выглядит это так:
Как видно из картинки, на IFT и PROD код ещё в версии спринта А, тогда как на DEV-стенде уже в версии спринта Б.
Этап отладки.
По окончании активной разработки, весь код из ветки develop
вливается в ветку test
. Тестирование на DEV‑стенде прекращается и переносится на IFT‑стенд. Этот процесс можно назвать code freeze в рамках спринта.
С этого момента, разработчик уже может брать задачи следующего спринта и продолжать реализовывать обычным способом, в ветках от develop
, по окончании реализации которых он в обычном режиме деплоится на DEV‑стенд. Происходит разделение стендов на уровне спринтов: IFT‑стенд принадлежит текущему спринту, тогда как DEV‑стенд — будущему.
С момента отладки уже можно проводить планирование следующего спринта, чтобы определить scope задач, которые разработчик уже может брать в работу.
Таким образом, на этапе отладки каждый стенд находится в версии своего спринта: тогда как PROD всё ещё в версии предыдущего спринта, то IFT уже в версии нынешнего, а DEV — в версии спринта будущего.
Таким образом, разработчик не простаивает на время отладки и уже может сделать часть работы, намеченной на следующий спринт.
Этап отладки: что, если был найден баг на стенде IFT?
Интеграционно‑функциональный стенд имеет несколько важных отличий от стенда разработки: а) он находится в окружении заказчика, что позволяет отладить взаимодействие с этим окружением, и б) он наполнен не синтезированным мусором, а обезличенными данными с PROD. Это позволяет тестировщику провести тестирование фич на уровне, наиболее близком к продуктивному.
Конечно, целью такого тестирования является обнаружение новых багов.
Итак, тестировщик нашёл новый баг. Как мы помним, IFT находится в состоянии текущего спринта, а DEV — будущего. Если мы для устранения бага создадим ветку от develop
, то к тому моменту в develop
уже могут оказаться фичи следующего релиза, которые не были ещё протестированы и не должны попасть в текущий релиз. Тогда, после устранения бага, нам придётся черри‑пикать изменения из develop
в test
и держать потом эти черри‑пики в уме, но это плохой выход из ситуации (в целом, практика черри‑пиков является костылём и в разработке может применяться только от безысходности).
Отсюда вытекает правило третье:
Правило третье: мы помним правило Первое, согласно которому, изменения на стенд деплоятся только из своей мастер‑ветки. Стало быть, если на каком‑то конкретном стенде были найдены причины для изменения кода, то и разработка в рамках этих изменений ведётся из мастер‑ветки этого стенда.
Иными словами, если на IFT был найден баг, для его устранения мы образуем ветку из test
. После исправления бага, ветка вливается обратно в test
, происходит деплой на IFT, баг тестируется повторно, и если всё ок, ветка test
вливается в develop
.
При таком подходе, сохраняется следующая взаимосвязь: ветка test
содержит в себе версию кода, актуальную по текущий релиз; ветка develop
содержит в себе версию кода, актуальную по текущий релиз + изменения в рамках следующего релиза.
Окончание этапа отладки и заход на новый спринт.
Единственное назначение стенда IFT — это тестирование приложения «как бы на проде». Окружение и наполнение IFT‑стенда не должно кардинально отличаться от прода и как можно чаще обогащаться оттуда обезличенными данными. Если у Вас не так — будете тестировать на проде, что уж.
После окончания этапа отладки изменения из ветки test
переносятся в ветку master
и деплоятся на PROD. Версионность стендов возвращается к состоянию активной разработки (в следующей итерации), начинается новый спринт, с его победами и поражениями.
Как будет выглядеть полный релизный цикл?
В соответствии с вышеописанным, полный релизный цикл будет выглядеть так:
Вместо заключения: что делать, если баг найден на проде?
Мы приложили все усилия для того, чтобы это не произошло. Но мы точно знаем, что это произойдёт. Что делать?
В первую очередь, попробуйте воспроизвести баг на стенде IFT. Если вы держите IFT в состоянии, актуальном PROD, он в очень высокой долей вероятности воспроизведётся. Если воспроизвёлся, переходим к разделу «Этап отладки: что, если был найден баг на стенде IFT?«, правилу 3 и далее вниз по посту: исправили на IFT, влились из IFT в develop
, влились в master
, зарелизились на PROD повторно.
Если баг не воспроизвёлся на IFT, значит, это проблема чисто PROD и применяем правило 3 уже к нему: бранчуемся из master
, исправляем проблему, далее переливаем изменения из master
→ test
→ develop
.
Комментарии (5)
TyVik
04.04.2023 13:15Ох тяжело вам будет с таким подходом в контейнеры деплоиться. В манифесте 12factor app первым же пунктом - обеспечьте деплой любой ветки на любое окружение.
Да и стендов маловато. Весь флоу превратится в хаос, когда из будет хотя бы штук 5.
Vladimir_Nastoyaschiy
04.04.2023 13:15А можете песотовать что-то на тему "как подружить гит с микросервисами"? Вроде подавляющее количество статей про гитфлоу описывают плюс-минус то же самое, что и в текущем топике
TyVik
04.04.2023 13:15Мне кажется, это ортогональные вещи. В микросервисной архитектуре можно работать по любому флоу. Именно у нас используется GitHub flow с одним master и линейной историей; если бы делали коробочное решение (или надо было поддерживать несколько версий), то логично было бы использовать GitLab flow.
koropovskiy
Хорошее, доступное объяснение GitFlow. Спасибо.
Однако ситуация "bug на prod" который требует немедленной реакции не раскрыта совсем.
Ок. ответвились от master, но вливаться из bug в master ? совсем без тестирования? не выглядит безопасным. А поскольку про тесты на feature|bug ветках вы не рассказывали, то я бы рекомендовал все таки путь "master->bug->test->master" чтобы путь релиза "test-master" не нарушался.
И интересны ситуации
"спринт В уже хочет на test, но там еще результаты спринта Б, который проверен на 80% и ожидается, что установка на prod через N дней допустим". Что делаете?
или еще интереснее и по мне так вероятнее
"спринт Б установлен на test, и в тот же день приходит critical-bug с prod от спринта А"
xpendence Автор
Ок. ответвились от master, но вливаться из bug в master ? совсем без тестирования? не выглядит безопасным. А поскольку про тесты на feature|bug ветках вы не рассказывали, то я бы рекомендовал все таки путь "master->bug->test->master" чтобы путь релиза "test-master" не нарушался.
Да, согласен, но есть момент. Тестировать что-то на IFT в таком случае имеет смысл, если баг воспроизвёлся на IFT. Если баг на IFT не воспроизводится, тогда мы в любом случае не сможем протестировать его исправление, и максимум, что мы сможем сделать - это регресс на IFT после исправления. Тогда подойдёт путь master -> bug -> master -> test -> регресс на тесте -> релиз в мастере. На целостность кода это, по сравнению с Вашим способом, не повлияет, но путаницы будет немного меньше, чем возврат ветки bug к родителю через другую ветку.
"спринт В уже хочет на test, но там еще результаты спринта Б, который проверен на 80% и ожидается, что установка на prod через N дней допустим". Что делаете?
Ситуация знакомая :) Но тут всё просто. Спринт В может захотеть на тест только после а) выполнения всех задач спринта б) успешного тестирования на деве в) code freeze. Если команда находится на этапе отладки предыдущего спринта, а Вы уже сделали все задачи следующего, значит, Вы недооценили свои силы на спринт :) Что ж, попросите добавить в свой скоуп на спринт ещё задач :)
"спринт Б установлен на test, и в тот же день приходит critical-bug с prod от спринта А"
Хорошо, если можно вернуть стенд в состояние спринта А и воспроизвести баг на IFT, но, к сожалению, не каждый в каждом случае спринт можно откатить до состояния предыдущего релиза, да и стоимость такого отката может превышать профит от починки такого бага. git flow в любом случае будет master -> bug -> master; далее, в зависимости от состояния IFT, или регресс на IFT, или скользкая дорожка внепланового релиза на прод без регресса (тестируем на проде), после успешного исправления бага master -> test -> develop. К сожалению, такое тоже бывает.