Стратегии работы с ветками — одна из тем, по которым то и дело разгораются жаркие споры как в интернете, так и в реальной жизни точно так же, как о предпочтительном использовании пробелов или табуляции.
Многие жестко придерживаются одной из сторон в этом противостоянии, однако, как это часто бывает в разработке ПО, правильный выбор зависит от ситуации. Давайте теперь разберемся, что такое стратегии работы с ветками и какое отношение они имеют к CI/CD.
Попросту говоря, стратегия работы с ветками — это согласованный подход вашей команды к созданию и объединению веток в системе контроля версий. Настройки системы контроля версий и использования веток влияют на настройки CI/CD-пайплайна, поэтому нужно выбрать модель, соответствующую вашим требованиям.
Команды разработчиков стали чаще задумываться о стратегиях работы с ветками, когда набрали популярность распределенные системы контроля версий, в первую очередь Git, и обрабатывать ветки стало проще.
В распределенных системах существует несколько копий репозитория, а следовательно, и несколько первоисточников, хотя многие команды выбирают одну главную копию. Несколько членов команды могут параллельно вносить изменения в свои копии репозитория и работать над кодом совместно, отправляя изменения в другие копии в том же репозитории.
С появлением Git создавать ветки и объединять изменения из разных веток стало проще простого. В Git ветка — это просто коммит (или серия коммитов), которому присвоено отдельное имя. Ветки — идеальный вариант для хранения наборов изменений, например, при работе над новой функцией или рискованном эксперименте.
После этого можно поделиться изменениями, перенеся ветку в другой репозиторий и (или) объединив ее с другой веткой, например с мастером, чтобы внести ее в общую кодовую базу. Если же изменения окажутся неудачными, их легко отменить. При объединении двух веток Git самостоятельно согласует коммиты в обеих и значительно упрощает процедуру их объединения в других системах контроля версий.
Непрерывная интеграция требует, чтобы все члены команды часто делали коммиты изменений. В этом случае вы сможете регулярно проверять, все ли работает как надо, а не тратить недели и месяцы на интеграцию разных рабочих потоков после написания всего кода. Это в свою очередь позволит чаще выпускать обновления ПО, реализовав преимущества непрерывной доставки и развертывания.
Если при CI/CD используются разные ветки, важно определить, куда следует делать коммиты изменений, когда выполняется автоматическая сборка и тестирование, откуда обновления будут развернуты в продакшн. С этой точки зрения, самый простой вариант — по крайней мере, теоретически, — вообще не пользоваться ветками. При магистральной разработке все регулярно делают коммиты изменений в основную ветку, хранящуюся в центральном репозитории. Она постоянно находится в состоянии готовности к релизу, и сборки регулярно развертываются в продакшн.
Магистральная разработка может быть очень эффективной, особенно если у вас хорошо настроен CI/CD-пайплайн, и вы обеспечиваете непрерывное развертывание в серверную систему, однако не исключены и некоторые проблемы. Стратегии работы с ветками предлагают и другие варианты управления изменениями в коде. У каждого из этих вариантов есть свои плюсы и минусы. Ниже несколько самых распространенных сценариев, которыми пользуются команды разработчиков, работающие с CI/CD-пайплайном.
Как следует из названия, функциональные ветки создаются для отделения некоторых функций от основной кодовой базы. Когда работа ведется вне основной ветки (мастера), проще поддерживать мастер в состоянии готовности к развертыванию и — если развертывание выполняется прямо из мастера, а не из ветки релиза (см. ниже) — предотвратить возможное развертывание незаконченных функций в продакшн. При этом разработчики могут свободно делиться сделанной работой с коллегами, размещая свою ветку либо в центральном репозитории (если он выбран), либо в любом другом.
Основной недостаток функциональных веток — за это их часто критикуют сторонники магистральной разработки — заключается в том, что, откладывая интеграцию изменений до тех пор, пока функция не будет «готова», мы теряем преимущества непрерывной интеграции. В результате мы рискуем получить конфликты при объединении веток и более сложные баги, устранить которые будет сложнее, чем при итеративных изменениях.
Решить эту проблему можно, настроив сервер CI на автоматический запуск сборки и тестирования в функциональных ветках и мастере (или любой другой ветке, которая используется для подготовки релизов). В результате вы сразу получите обратную связь по новым функциям, а вероятность проблем при объединении изменений снизится.
Один из путей минимизации конфликтов при объединении — сокращение срока существования функциональных веток: один день, максимум — два. Другой способ — регулярно перебазировать коммиты из мастера в функциональную ветку, то есть выполнить их слияние. Благодаря этому функциональная ветка будет учитывать все коммиты изменений в мастере. Однако если у вас одновременно идет работа во многих функциональных ветках, из которых не делаются коммиты в мастер, опасность конфликтов все равно остается.
Функциональные ветки — удобный способ управления текущей работой, а ветки релизов используются для фиксации изменений перед релизом. Ветки релизов очень удобны для непрерывной доставки, когда обновления доставляются с определенной периодичностью, а не по мере готовности. С их помощью удобно поддерживать в продакшене сразу несколько версий.
Если вы используете ветки релизов, каждая из них включает в себя изменения, запланированные для определенного релиза. Ветки создаются на основе мастера или другой ветки разработки, когда соответствующие изменения готовы к выпуску, и новые функции туда уже не добавляются. Параллельно в мастере или других ветках может идти разработка функций для других релизов.
Сборки, созданные из соответствующих веток, проходят автоматическое тестирование, исправление ошибок делается в ветке релиза, затем тестирование повторяется, пока релиз не будет готов. Ошибки необходимо исправить также в основной ветке, чтобы они не попали в будущие версии продукта.
Сохраняя ветки релизов в течение всего срока поддержки каждой версии ПО, гораздо проще создавать обновления для старых версий.
Если требуется обновление, его можно разработать либо в ветке срочного исправления, либо в мастере, протестировать обычным образом, а затем применить к ветке релиза (например, с помощью cherry-pick).
После этого исправление проходит CI/CD-пайплайн в составе соответствующей ветки, чтобы перед развертыванием изменений убедиться, что в версии нет ошибок. Можно также вести работу прямо в ветке релиза, а затем по мере необходимости применить исправления к мастеру.
Ветки срочных исправлений работают так же, как функциональные, но для полноты картины о них тоже стоит упомянуть. Такие ветки создаются для максимально быстрой разработки и выпуска исправлений в продакшн.
Ветку быстрого исправления можно создать из мастера или из ветки релиза в зависимости от того, откуда вы развертываете продукт. Как и для функциональных веток, иногда перед объединением ветки быстрого исправления с основной удобно провести код через некоторые этапы CI/CD-пайплайна, а уже после объединения выполнить полное предрелизное автоматическое тестирование.
Мы рассмотрели лишь самые распространенные варианты работы с ветками в CI/CD-пайплайне, но на самом деле их гораздо больше.
Самое главное — выбрать оптимальную стратегию именно под ваши требования. Придерживаясь философии DevOps, предполагающей непрерывное совершенствование, и не ограничивая искусственно выбор вариантов, вам будет проще адаптировать стратегию к своим нуждам по мере развития CI/CD-пайплайна.