Noveo

Наш блог Подходы к управлению ветками в системах контроля версий (часть 4)

Подходы к управлению ветками в системах контроля версий (часть 4)

Четвертая часть статьи Мартина Фаулера посвящена паттернам для совместной работы команды.

Другие подходы к ветвлению

Основное внимание в этой статье уделено обсуждению паттернов для совместной работы команды и выхода в прод. Но есть и другие паттерны, о которых я хотел бы упомянуть.

Экспериментальная ветка

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

 

Экспериментальные ветки — это то место, где разработчики хотят опробовать какие-то идеи, но не ожидают, что их изменения будут просто интегрированы обратно в mainline. Например, я узнал о новой библиотеке, которая, как мне кажется, станет хорошей заменой той, которую мы используем сейчас. Чтобы понять, нужно ли осуществлять замену, я открываю ветку и просто пытаюсь написать или переписать соответствующую часть системы, используя новую библиотеку. Цель упражнения заключается не в том, чтобы внести код в кодовую базу, а в том, чтобы узнать, применим ли новый инструмент в моём конкретном контексте. Я могу сделать это самостоятельно или поработать над этим вместе с коллегами.

 

Или же у меня есть новая фича, которую нужно внедрить, и я вижу несколько способов это сделать. Я трачу пару дней на каждую альтернативу, чтобы решить, какой из них выбрать.

 

Ключевым моментом здесь является готовность отказаться от кода на экспериментальной ветке и не мержить его с mainline. Не всегда происходит именно так: если мне понравится результат, а код будет легко интегрировать, я не буду игнорировать эту возможность — но и не ожидаю, что так и будет. Я могу не делать привычных процедур, меньше тестировать, допустить немного повторений в коде, вместо того, чтобы пытаться рефакторить его до блеска. Я ожидаю, что если мне понравится эксперимент, я буду работать с нуля, применяя идею к коду на проде, используя экспериментальную ветку в качестве напоминания и руководства, но не используя ни один из коммитов.

 

Когда я закончиваю работу над экспериментальной веткой, я обычно добавляю тег в git’е и удаляю ее. Тег хранит эту строку кода на случай, если я захочу перепроверить её позже — я пользуюсь общепринятым сокращением «exp» в начале тега, чтобы ясно обозначить его суть.

 

Когда это использовать

 

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

 

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

Ветка на будущее

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

 

Это редкий паттерн, но время от времени он встречается при использовании Continuous Integration. Иногда команде приходится вносить изменения, затрагивающие весь написанный до этого код, и обычные приемы интеграции незавершенной работы работают не очень хорошо. В этом случае команда делает что-то, похожее на ветку для фичи: создают ветку на будущее и только выгружают код из mainline, откладывая интеграцию с ней до самого конца.

 

Основная разница между Future Branch и Feature Branch заключается в том, что Future Branch существует только одна. А значит, те, кто на ней работает, никогда не отходят слишком далеко от mainline, и им не нужно иметь дело с другими, отличающимися ветками.

 

Иногда над Future Branch могут работать несколько разработчиков, и в этом случае они осуществляют с ней непрерывную интеграцию. При каждой интеграции, прежде чем залить свои изменения, им сначала нужно загрузить mainline в Future Branch. Это замедляет процесс интеграции, но такова цена использования Future Branch.

 

Когда это использовать

 

Я должен подчеркнуть, что это редкий паттерн. Я подозреваю, что большинству команд, которые выполняют непрерывную интеграцию, никогда приходится прибегать к ней. Я видел этот паттерн при особенно инвазивных изменениях в архитектуре системы. В общем, это последнее средство, которое можно использовать только в том случае, если мы не можем понять, как вместо этого использовать что-то вроде Branch By Abstraction.

 

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

Ветка для совместной работы (Collaboration Branch)

Ветка, созданная для того, чтобы разработчик мог делиться работой с другими членами команды без формальной интеграции.

 

Когда команда использует mainline, то большая часть совместной работы происходит через нее. Другие члены команды видят, что делает разработчик, только после интеграции с mainline

 

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

 

Ветка для совместной работы, как правило, является временной и закрывается, как только работа интегрирована в mainline.

 

Когда это использовать

 

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

Ветка интеграции команды

Позволяет группам внутри команды интегрироваться друг с другом, прежде чем интегрироваться с mainline.

 

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

 

По сути, команда рассматривает ветку интеграции команды как mainline внутри команды, интегрируясь с ней так же, как и с mainline проекта в целом. В дополнение к этим интеграциям команда выполняет отдельную работу по интеграции с mainline проекта.

 

Когда это использовать

 

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

 

Ситуация, в которой использование ветки интеграции команды выглядит еще более очевидным, — это разница в желаемой частоте интеграции. Если на проекте в целом предполагается работа в ветках для фич объемом примерно в две недели работы, а часть команды предпочитает непрерывную интеграцию, то команда может создать ветку для интеграции команды, выполнять непрерывную интеграцию именно с ней и интегрировать фичу, над которой они работают, в mainline сразу по завершении работы.

 

Это актуально и для случаев, когда существует разница между стандартами, используемыми на проекте в целом для стабильной («здоровой») ветки, и стандартами, принятыми у части команды. Подкоманда может работать с более высоким уровнем стабильности, даже если весь проект в целом не может поддерживать mainline достаточно здоровой. Аналогичным образом, если подкоманда пытается сделать свои коммиты достаточно стабильными для хорошо контролируемой mainline, она может выбрать работу с веткой интеграции команды и пользоваться собственными релизными ветками для стабилизации кода перед переходом на mainline. Обычно я одобрил бы такую ситуацию, но это может быть необходимым в особо сложных случаях.

 

Мы также можем думать о ветке командной интеграции как о более структурированной форме ветки для совместной работы, которая основана на формальной организации проекта, а не на специфической совместной работе.

Глядя на некоторые стратегии создания веток

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

 

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

Git-flow

Git-flow стал одной из самых распространенных стратегий использования веток, с которыми я сталкивался. Он был написан Винсентом Дриссеном (Vincent Driessen) в 2010 году, когда git стал популярным. Во времена, предшествовавшие git’у, ветвление часто рассматривалось как сложная тема. Git сделал ветки более привлекательными, отчасти благодаря улучшенным инструментам (например, лучшей обработке перемещения файлов), но также и потому, что клонирование репозитория по сути является веткой, что требует аналогичного подхода к вопросам слияния при пушах обратно в центральный репозиторий.

 

Git-Flow использует mainline (называя ее «разработкой», «develop») в одном «исходном» репозитории. Он использует ветвление по фичам для координации множества разработчиков. Разработчикам рекомендуется использовать их личные репозитории в качестве веток для совместной работы для координации с другими разработчиками, работающими над похожими вещами.

 

Основная ветвь гита исторически носит имя «мастер», в git-flow мастер используется как ветка готовности к проде. Git-Flow использует релизную ветку, так что работа проходит от «разработки» через релизную ветку до «мастера». Хотфиксы организованы ветку для хотфиксов.

 

Git-Flow ничего не говорит ни об объеме веток для фич, ни об ожидаемой частоте интеграции. В нем также ничего не говорится о том, должна ли mainline быть “здоровой” веткой, и если да, то какой уровень здоровья необходим. Наличие релизной ветки подразумевает, что это готовая к релизу mainline.

 

Как отметил Дриссен в дополнении к описанию в этом году, git-flow был разработан для тех проектов, в которых на прод было выпущено несколько версий, например, программное обеспечение, установленное на сайтах заказчиков. Наличие нескольких живых версий, конечно же, является одним из основных триггеров для использования релизных веток. Однако многие пользователи подхватили git-flow в контексте веб-приложения с единственной версией на проде — и здесь такая структура ветвления становится куда более сложной, чем это необходимо.

 

Хотя git-flow очень популярен, в том смысле, что многие люди говорят, что используют его, на самом деле обычно они делают что-то совершенно другое. Часто их реальный подход ближе к GitHub Flow.

GitHub Flow

Git-flow действительно получил широкое распространение, но излишняя сложность его структуры ветвления для веб-приложений стимулировала множество альтернатив. С ростом популярности GitHub было не удивительно, что политика ветвления, используемая его разработчиками, стала хорошо известной, получив соответствующее название — GitHub Flow. Лучшее его описание принадлежит Скотту Чакону.

 

Название GitHub Flow явно говорит о том, что оно было сознательно основано на Git-Flow, будучи своеобразной реакцией протеста. Существенное различие между ними заключается в разных видах продукта, что означает разный контекст, а значит, разные подходы. Git-Flow предполагал продукт, имеющий несколько версий на проде. GitHub Flow предполагает одну версию в проде с частой интеграцией в mainline, готовую к релизу. В этом контексте в релизной ветке нет необходимости. Проблемы на проде исправляются так же, как и обычные фичи, поэтому ветка для хотфиксов становится ненужной, в том смысле, что она обычно означает отклонение от нормального процесса. Удаление этих веток значительно упрощает структуру ветвления до основной ветки и веток под фичи.

 

GitHub Flow называет свою mainline «мастером». Разработчики работают с ветками под фичи. Они регулярно пушат свои ветки фич в центральный репозиторий для лучшей видимости по проекту, но до завершения работы над фичей интеграция с mainline невозможна. Чакон уточняет, что ветки для фич могут быть одной строкой кода или же выполняться в течение пары недель. В любом случае, процесс должен работать одинаково. Поскольку это GitHub, механизм пулл-реквестов является частью интеграции с mainline и использует проверенные коммиты.

 

Git-flow и GitHub Flow часто путают, поэтому, как и всегда, копайте глубже, чем название, чтобы по-настоящему понять, что происходит. Общая тема обоих — использование mainline и веток под фичи.

Trunk-Based Development

Как я писал ранее, я в основном слышу «trunk-driven development» как синоним непрерывной интеграции. Но также разумно рассматривать Trunk-Driven Development как альтернативу политике ветвления для git-flow и GitHub Flow. Пол Хаммант (Paul Hammant) написал подробный сайт, чтобы объяснить этот подход. Пол был моим давним коллегой в ThoughtWorks, и у него был солидный опыт работы с окостенелыми структурами ветвления на стороне клиента с его верным +4 мачете.

 

Trunk-Based Development сосредоточена на выполнении всей работы на mainline (называемой «trunk», что является общим синонимом термина «mainline») и таким образом избегает любых видов долгоживущих веток. Небольшие команды коммитают непосредственно на mainline, используя Mainline Integration, более крупные команды могут использовать недолго живущие ветки для фич, где «недолго» означает не более пары дней — что на практике, вероятно, приравнивается к непрерывной интеграции. Команды могут использовать релизные ветки («branch for release») или готовую к выпуску mainline («release from trunk»).

Заключительные мысли и рекомендации

Со времен самых первых программ люди обнаружили, что если они хотят программу, которая делает что-то немного отличное от существующей программы, то легко взять копию исходного кода и откорректировать ее, как им заблагорассудится. Имея исходный код, я могу вносить любые изменения, какие захочу. Но эти действия затрудняют внесение в мою копию новых фич и исправлений ошибок в исходнике. Со временем это может стать невозможным, как на своем опыте с ранними COBOL-программами убедились множество предприятий, страдающих сегодня от всесторонне настраиваемых ERP-пакетов. Даже если мы не называем это так, каждый раз, копируя исходный код и изменяя его, мы выполняем разветвление исходного кода, даже если не используем систему контроля версий.

 

Как я уже говорил в начале этого длинного поста: ветвление — это легко, мерж сложнее. Ветвление — это мощная техника, но она заставляет меня думать об операторах goto, глобальных переменных и блокировках для параллелизма. Они мощные и простые в использовании, но еще проще начать ими злоупотреблять, и они слишком часто становятся ловушками для беспечных и неопытных. Системы управления исходным кодом могут помочь контролировать ветвления, тщательно отслеживая изменения, но в конце концов они могут выступать только свидетелями проблем.

 

Я не тот, кто считает, что ветвление — это зло. Существуют повседневные проблемы, такие как работа множества разработчиков над единой кодовой базой, где разумное использование ветвления крайне важно. Но мы всегда должны быть с этим осторожными и помнить замечание Парацельса о том, что разница между полезным препаратом и ядом заключается в дозировке.

 

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

 

Отсюда следующий совет: убедитесь, что вы понимаете альтернативы ветвлению, они, как правило, лучше. Помните закон Бодарта, есть ли способ решить вашу проблему путем улучшения модульности? Можете ли вы улучшить организацию вашего процесса разработки? Не будет ли достаточно тега? Какие изменения в вашем процессе сделают эту ветку ненужной? Вполне вероятно, что ветка, на самом деле, лучший путь сейчас — но это знак, предупреждающий вас о более глубокой проблеме, которая должна быть решена в течение следующих пары месяцев. Избавляться от потребности в ветке — это, как правило, хорошая вещь.

 

Помните иллюстрацию LeRoy: ветви расходятся экспоненциально, если не интегрируются. Так что подумайте, как часто вы интегрируете свои ветки. Стремитесь удвоить частоту интеграции (очевидно, что здесь есть ограничение, но вы не приблизитесь к нему, если не выполняете непрерывную интеграцию). На пути к более частой интеграции будут преграды, но часто именно они, как избыточная доза динамита, необходимы, чтобы улучшить процесс разработки.

 

Поскольку мерж — это сложная часть ветвления, обратите внимание на то, что усложняет мерж. Иногда это проблема процесса, иногда — это провал архитектуры. Что бы это ни было, не поддавайтесь Стокгольмскому синдрому. Любая проблема мержа, особенно та, которая вызывает кризис, является признаком повышения эффективности команды. Помните, что ошибки ценны только тогда, когда вы учитесь на них.

 

Подходы, которые я описал здесь, описывают общие конфигурации ветвления, с которыми я сталкивался на своем опыте и опыте моих коллег. Назвав их, объяснив и, прежде всего, описав, когда они полезны, я надеюсь, что помог вам оценить, когда их использование оправдано. Помните, что, как и любой подход, они редко бывают только хорошими или только плохими — их ценность для вас зависит от контекста, в котором вы находитесь. Когда вы сталкиваетесь с политикой ветвления (будь то хорошо известные, такие как git-flow или trunk-based development, или что-то доморощенное в организации разработки), я надеюсь, что понимание сути подходов поможет вам решить, соответствуют ли они вашим обстоятельствам и какие другие подходы было бы полезно добавить.

 

Оригинал: https://martinfowler.com/articles/branching-patterns.html#BasePatterns

 

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

НазадПредыдущий пост ВпередСледующий пост

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: