Мнение: пушить сразу в мастер — хорошо. Обсуждаем Trunk Based Development
Что вы чувствуете, когда слышите, что кто-то сделал пуш в мастер? А что, если всю разработку построить именно на этом? Встречайте Trunk Based Development.
23К открытий23К показов
Мэттия Бэттистон опубликовал на площадке Medium статью, где рассказал, почему ему нравится пушить сразу в мастер и какие выгоды для команды он видит в таком подходе. Публикуем его рассказ и два противоположных мнения о Trunk Based Development.
Прим. ред. Мнение редакции может не совпадать с мнением автора оригинала.
Разработка с помощью пулл-реквестов и feature-веток (даже краткосрочных) проще для индивидуальной разработки, но в целом не является оптимальной стратегией для команды.
Обычно командам, использующим краткосрочные ветки, было бы лучше работать прямо в мастере, так как практики, которые им нужно соблюдать, чтобы безопасно следовать trunk based development, приносят множество преимуществ: улучшение командной работы, улучшение качества работы, коллективное владение кодом и не только.
«Нет, мы не пушим в мастер, ты совсем что ли?»
Позвольте рассказать вам историю.
Недавно я устроился в консалтинговую компанию. Самая интересная часть работы консультантом заключается в том, что вам приходится работать с множеством клиентов. Так, с начала работы несколько месяцев назад я поработал с тремя разными командами. Они состояли примерно из 6–8 человек, работающих в одном офисе. В основном это были программисты плюс product owner, иногда ещё и scrum master. Почти все в команде сидят рядом друг с другом, не считая странного человека, который изо дня в день работает из дома.
Когда я присоединяюсь к команде, я всегда задаю много вопросов, в основном чтобы понять, как она работает и как я могу помочь. С каждой из трёх команд неизбежно заходил разговор о контроле версий, который проходил примерно так:
Я: «Так, что происходит, когда я делаю коммит?».Они: «Тебе нужно создать пулл-реквест, скинуть ссылку в Slack и подождать, пока кто-нибудь его проверит».Я: «А, то есть вы не пушите сразу в мастер?».Они: *уставились на меня широко открытыми глазами, словно увидели пришельца... несколько секунд тишины…*Они *неодобрительно качая головами*: «Нет, мы не пушим в мастер. Код сначала нужно проверить».И тут я прямо вижу, как они думают: «Он серьёзно? И вот этот человек должен нам "помочь"?».
Само собой, я не против, когда люди ставят под сомнение мои идеи и противятся им, это часть моей работы. Но что меня удивило, так это сколько сопротивления встретила идея, что кто-то мог даже подумать о пуше в мастер. Я много гуглил и поспрашивал людей из других компаний, и оказалось, что feature-ветки — нормальное явление. Тут даже и говорить не о чем, так работает IT-индустрия.
Будучи выходцем из компании, где пушить в мастер было нормой, я подумал и решил написать эту статью. Я хочу объяснить, почему я уверен, что обычно командам, которые используют краткосрочные feature-ветки, будет выгодней делать всё в мастере, а не в этих ветках.
Как команды используют краткосрочные feature-ветки и пулл-реквесты
Давайте объясню, как из моих наблюдений выглядит типичный рабочий процесс команды, использующей краткосрочные feature-ветки:
- У команды есть бэклог историй для реализации. Обычно есть «бэклог спринта» с историями, которые нужно реализовать в этом спринте.
- Разработчик свободен, поэтому он смотрит в бэклог и выбирает историю.
- Разработчик создаёт новую ветку из мастера и начинает работать над историей.
- По мере работы он коммитит и пушит код в ветку с любой удобной частотой, будь то минуты, часы или дни.
- При каждом пуше инструмент сборки (Jenkins, CircleCI и т. д.) запускает сборку ветки. Обычно сборка компилируется и упаковывает код, после чего запускаются какие-нибудь тесты. Возможно, происходит развёртывание в dev-среде.
- Когда разработка завершена, на что может уйти от нескольких дней до недели, разработчик создаёт пулл-реквест (PR).
- Кто-нибудь из команды проверяет PR. В некоторых командах любой человек может сделать это, а в других — только техлид или senior-разработчик.
- Проверяющий(-ие) может оставить комментарии к PR, запросив изменения. В этом случае разработчик возвращается к работе для реализации этих изменений, а затем снова отправляет PR. Этот шаг может повторяться несколько раз.
- Наконец, PR принимают. Разработчик сливает ветку в мастер, обычно совмещая все коммиты из ветки в один большой коммит в мастер («squash и merge»).
- Инструмент CI запускает сборку мастера. Начинается процесс внесения этих изменений в продакшн — иногда они автоматически развёртываются на продакшне, а иногда на одном или нескольких этапах нужно вмешательство человека.
Ваш рабочий процесс может отличаться, но я думаю, что всё это звучит знакомо многим людям. Этот процесс известен под именем GitHub Flow.
Просто для ясности, я знаю, что команды могут использовать ветки и другими способами, например, Gitflow с долгоживущими ветками. Эти стратегии ветвления открывают целый новый мир проблем. Если вы хотите услышать об этих проблемах, я рекомендую эту презентацию Стива Смита. Однако в этой статье я хочу сосредоточиться на проблемах краткосрочных feature-веток и почему я думаю, что командам будет лучше работать, придерживаясь TBD.
Успешная альтернатива: Trunk Based Development, без веток
Что я предлагаю в качестве альтернативы, так это работать напрямую в мастере, без использования веток. Само собой, нужно делать это безопасным образом. Чтобы это стало возможным, команда должна использовать следующие основные методы:
1. Парное программирование
Вам нужно заниматься парным программированием и делать это часто. При работе в паре с другим человеком код пишется и проверяется в реальном времени, что исключает дальнейшую необходимость в PR. Более того, парное программирование приносит невероятное количество пользы команде:
- улучшается сконцентрированность команды;
- люди меньше отвлекаются;
- работа делается быстрее и качественнее;
- в команде стандартизируется стиль кода;
- люди вместе находят лучшие решения проблем;
- происходит обмен знаниями и так далее.
Порой это можно даже перенести на следующий уровень и устроить совместный сеанс программирования, когда вся команда работает над одной вещью на одном компьютере (например когда нужно принять командное решение вроде выбора архитектуры).
2. Имейте сборку, которой доверяете
Вам нужна сборка, которой вы доверяете. Она должна запускать достаточно автоматизированных тестов, чтобы вы были уверены, что кодовая база находится в состоянии, удовлетворительном для релиза. Также она должна быть достаточно быстрой, чтобы вы могли запустить её локально перед пушем в мастер, так как вы не хотите запушить что-то сломанное, что помешает остальной части команды.
Для достижения этих двух вещей команды обычно следуют практикам вроде TDD, BDD и имеют эффективную стратегию тестирования, например следуют пирамиде тестирования, чтобы свести к минимуму количество медленных тестов.
Если сборка вдруг ломается, её починка немедленно становится приоритетом команды. В маловероятном случае, если не удалось сделать это быстро, нужно сделать откат изменений.
3. Используйте «ветвление через абстракцию» или feature-флаги, чтобы спрятать незаконченный код
В любой момент времени в мастере будет незаконченный код. Это не приносит никакого вреда, так как такой код ещё не используется в продакшне. Зато от его нахождения в мастере есть польза: вы быстрее замечаете проблемы интеграции, вы можете провести более сложный рефакторинг и вы можете доказать, что код будет работать, написав для него тесты.
На деле я обнаружил, что в большинстве случаев можно использовать шаблон «ветвление через абстракцию», чтобы избежать использования кода в продакшне до его готовности. Для более сложных сценариев я использовал feature-флаги.
В моей предыдущей компании работать так было нормой. Я проработал там около 6 лет, но я знаю, что там следовали этим практикам более 10 лет. Так что я точно знаю, что такой способ работы может быть успешным (в конце статьи буду подробности того, как мы работали).
Преимущества Trunk Based Development
Я считаю, что описанные мной практики приносят команде массу преимуществ и хорошее взаимодействие, к чему команды должны стремиться. Применив их, вы сможете забыть о ветках и начать безопасно пушить сразу в мастер. Вот некоторые из преимуществ, которые я заметил:
- Более быстрая обратная связь: при использовании PR фидбек поступает только после того, как разработчик посчитает, что он закончил. На этом этапе уже, как правило, поздно менять что-то основательно. Когда вы работаете в парах, обмен фидбеком происходит во время написания кода — или даже раньше, когда вы обсуждаете подход к решению задачи — поэтому его гораздо проще менять.
- Более качественная обратная связь: при использовании PR фидбек обычно представляет собой простой текстовый комментарий. За редкими исключениями такой способ не может донести все нюансы, которые часто требуются при обсуждении программы. Автор PR вполне ожидаемо воспринимает всё в штыки, кода речь заходит о том, что он считает «своим кодом», а комментарии, которые легко могут показаться грубыми, зачастую вызывают раздражение или конфликты. С другой стороны, когда вы работаете в парах, вы обсуждаете ваши идеи лицом к лицу, где на порядок проще объяснить, что вы пытаетесь сказать, и провести здоровую дискуссию.
- Коллективное владение кодом: когда код написан одним человеком, обычно он воспринимает его как «мой код». В итоге ты начинаешь слышать вещи вроде «о, это Алекс написал» или «мы не можем работать над этим, пока Сэм не вернётся». Если работать в парах, то гораздо более вероятно, что команда начнёт смотреть на код как на «наш код».
- Командный стиль кода: хорошие команды пишут код, который выглядит так, словно его писал один человек. Это признак того, что каждый участник ценит командную работу больше, чем собственные предпочтения. При работе в парах становится гораздо проще выработать командный стиль и всегда о нём помнить.
- Более частая интеграция («настоящая» Непрерывная Интеграция): очень часто PR создаётся, когда история/фича закончена. При пуше сразу в мастер мы интегрируем код незамедлительно, приближаясь к реальному значению «непрерывности» в Непрерывной Интеграции.
- Мы привыкаем не ломать вещи: при работе непосредственно в мастере мы не хотим запушить что-нибудь сломанное, поэтому у нас появляется привычка запускать локальную сборку перед пушем и реализовывать код через последовательность изменений, которые ничего не ломают. С другой стороны, при работе с ветками проще простого проигнорировать неудавшуюся сборку и исправить всё только в конце.
- Легче справляться с большими рефакторингами: при работе с ветками мы стараемся избегать делать что-то, что может привести к конфликтам слияния, например, переименовывание пакетов, перемещение файлов, архитектурные изменения. При работе в мастере такие вещи делать даже если не легко, то по крайней мере легче, так как мы можем коммитить небольшие изменения, чтобы у всей команды был актуальный код. Для особо сложных изменений мы собираемся возле одной машины всей командой у устраиваем быстрый сеанс совместного программирования, когда мы сидим вместе и обсуждаем, как должно выглядеть решение.
- Лучше видно, кто над чем работает: когда изменения находятся в ветке, их гораздо сложнее заметить, чем когда все пушат их в мастер. Таким образом становится на порядок лучше видно, кто чем занимается и кому нужна помощь.
- Более удобный просмотр изменений: вместо того, чтобы смотреть на красные/зелёные строки на странице, как это обычно происходит при просмотре PR, можно использовать гораздо более удобные инструменты для просмотра изменений перед их коммитом.
- Сохранение оригинальной истории коммитов: при слиянии изменений из ветки в мастер люди зачастую объединяют все коммиты в один, теряя историю того, как и почему автор сделал те или иные изменения. С другой стороны, при работе в мастере мы сохраняем полную историю каждого коммита. Я работал с кодовыми базами, которым было лет по 10 и чьи авторы уже давно были недоступны, и могу сказать, что возможность посмотреть в истории Git конкретно на коммит, где что-то изменилось, была бесценной, так как это позволяло нам посмотреть на описание этого и любого другого изменения, сделанного в то же время.
Trunk Based Development — показатель здоровья команды
Позвольте быть предельно ясным: TBD сам по себе не является чем-то особенным и не даёт каких-то особых преимуществ. Что их даёт, так это практики, которым нужно следовать для применения TBD. Чтобы команда могла работать прямо в мастере, её члены должны знать, как работать, не ломая код, как писать хорошие тесты, как работать вместе эффективно и так далее.
Можно сказать, что TBD является показателем здоровья команды. И этому действительно есть подтверждение в книге «Accelerate»: после изучения более 10 000 сотрудников и 2 000 организаций исследование показало, что существует тесная взаимосвязь между командой, следующей TBD, и высокими показателями эффективности этой команды. В частности, было обнаружено, что в высокоэффективных командах срок жизни веток составляет менее суток.
Оптимизация эффективности команды
Когда я спрашиваю у команд, почему они используют ветки или почему они думают, что не стоит пушить сразу в мастер, мне обычно отвечают что-то вроде: «Мы должны поддерживать мастер в хорошем состоянии, готовом к релизу» или «Мы хотим проверять код друг друга, чтобы убедиться, что он хорошего качества и соответствует нашим стандартам». Я полностью согласен с обоими утверждениями, но в этой статье я показал вам, как добиться тех же результатов без использования веток.
Однако есть и другая причина, о которой часто умалчивают, почему так много команд используют feature-ветки: «Гораздо проще и эффективнее, когда все работают со своими ветками и не наступают друг другу на пятки».
И хотя это утверждение справедливо, я с ним категорически не согласен. Именно отсюда растут многие проблемы команд. Большинство команд оптимизируют работу отдельного человека, а не всей команды. Они смотрят на продуктивность отдельного человека, а не команды. В терминологии Lean, это является прекрасным примером оптимизации ресурсоэффективности вместо эффективности рабочего процесса.
Feature-ветки оптимизируют индивидуальную производительность, но TBD оптимизирует производительность команды. Когда вы проводите оптимизацию команды, начинает казаться, словно каждый её член начинает работать медленнее. Это смена подхода, которая выглядит нелогично. Но именно так группа отдельных разработчиков превращается в команду.
Хорошие примеры использования feature-веток
После долгих разговоров о том, почему не следует использовать feature-ветки, думаю, стоит прояснить следующее: да, в определённых случаях от них есть польза. Как правило, я бы сказал, что feature-ветки хорошо использовать, когда у кода есть явный владелец, но кто-то другой делает всю работу.
Прекрасный тому пример — open-source модель: в типичном open-source проекте есть владелец, будь то один человек или команда, а ещё есть множество разработчиков со всего света, вносящих вклад в проект без общения друг с другом. В этом случае имеет смысл просить разработчиков отправлять PR, так как владельцам проекта нужно его проверить. Собственно, это то, для чего в GitHub придумали PR! Также в таком случае владельцам проще отклонить любой PR, с которым они не согласны.
Аналогичная ситуация иногда возникает в компаниях, использующих внутреннюю open-source модель: код принадлежит одной команде, но поскольку разработчики слишком заняты, чтобы работать над ним, другая команда вносит в него свой вклад, отправляя PR. Это не единственное решение этой проблемы, но иногда это хороший компромисс.
«Окей, убедили. Как мне начать использовать этот подход?»
Если сейчас вы работаете с feature-ветками и хотите перейти к работе с мастером напрямую, вот несколько предложений:
- Пересмотрите вашу стратегию тестирования и поработайте над стабильной сборкой, которой доверяете. Это может означать, что вам надо начать больше применять TDD, добавлять больше тестов, сохраняя при этом быструю сборку.
- Больше работайте в парах. Если вы обычно этого не делаете, то начните со сложных задач, так как в таком случае будет проще убедить кого-то работать с вами в паре. Затем вы можете продолжать делать это во всё большем количестве случаев, и в итоге образуется группа людей, которым нравится парное программирование. Остальные члены команды со временем подтянутся, особенно если вы введёте правило, что код, написанный в паре с другим человеком, не нуждается в проверке кода.
Ресурсы по теме
Вот хорошие ресурсы, если вы хотите больше узнать о TBD:
- Trunk Based Development;
- Continuous Integration and Feature Branching — Dave Farley;
- Organisation Pattern: Trunk Based Development — Steve Smith;
- The Death of Continuous Integration — Steve Smith;
- Enabling Trunk Based Development with Deployment Pipelines — Vishal Naik;
- Committing straight to the trunk — Paul Hammant;
- What is Trunk-Based Development? — Paul Hammant;
- Accelerate — Nicole Forsgren PhD, Jez Humble, Gene Kim;
- Make Large Scale Changes Incrementally with Branch By Abstraction — Jez Humble;
- BranchByAbstraction — Martin Fowler;
- Branch By Abstraction — Paul Hammant.
Бонус: реальный пример Trunk Based Development
Вот подробная информация о том, как моя предыдущая команда следовала TBD и пушила сразу в мастер. Команда была похожа на описанные выше: 6–10 человек, из них 4–6 разработчиков, 1–2 тестера, 1 бизнес-аналитик и 1 тимлид.
- Разработчики работают в паре весь рабочий день.
- Одна пара разработчиков выбирает новую историю. В рамках процесса подготовки к разработке они пишут скелет одного или нескольких приёмочных тестов и составляют список заданий для истории.
- Когда пара думает, что готова, она собирает команду и показывает всем приёмочные тесты с целью убедиться, что у всех одинаковое понимание того, о чём история и что команда собирается реализовывать. Эта методология называется BDD, где используются примеры для формирования общего представления.
- Каждая пара разработчиков выбирает задание из истории. Как правило, над одной историей у нас работало по 2–3 пары, которые выбирали независимые задачи.
- Каждая пара коммитит и пушит сразу в мастер с частотой, варьирующейся от нескольких минут до нескольких часов. Они отправляют коммит, как только проходит тест, для которого они писали код. Все мы любили небольшие, частые коммиты, с понятными сообщениями, поясняющими почему было сделано то или иное изменение. В идеале сообщение использует бизнес-терминологию и фокусируется на бизнес-задаче, которую мы пытаемся решить этим коммитом. Хороший пример такого сообщения: «Реализовал приёмочный тест, чтобы доказать, что на один порт может быть выделен только один клиент».
- Каждый раз, когда разработчики вытягивают изменения из Git, они делают rebase, чтобы сохранить исходный порядок коммитов.
- Перед коммитом пара всегда запускает локальную сборку, чтобы убедиться, что всё хорошо. Сборка обычно занимает от 30 секунд до 2 минут.
- В качестве стратегии тестирования мы придерживались пирамиды тестирования, всегда предпочитая быстрые тесты медленным. Наши усилия заметно облегчило применение чистой архитектуры, которая позволяла нам держать бизнес-логику изолированной от технических деталей. Это не единственный способ, но нам он отлично подошёл.
- В любой момент времени кодовая база находится в состоянии, готовом к релизу, благодаря шаблону «ветвление через абстракцию». Иными словами, в нашем Java-проекте мы не подключали бины Spring до последнего момента. Весь новый код был доступен для тестов, но в продакшне он появлялся только когда мы были готовы его подключить.
- При пуше кода CI-инструмент запускает сборку и прогоняет все тесты. Мы нигде не делали автоматическое развёртывание, но могли бы, если бы захотели.
- Разработка истории обычно завершалась за несколько дней. После этого вся команда опять собиралась, чтобы посмотреть, что было реализовано.
- Затем последняя версия приложения развёртывается в stage-среде, где наш QA проводит исследовательское тестирование, на которое уходит от нескольких часов до нескольких дней.
- Если не всплывают никакие сюрпризы, то приложение развёртывается в продакшне (обычно на следующий день).
- Тем временем, ближе к концу разработки предыдущей истории, одна пара начинает рассматривать следующую историю, чтобы подготовить её и поддерживать рабочий поток. Мы использовали ограничения на work-in-progress процессы, чтобы держать этот поток под контролем.
- Если в какой-то момент пара чувствовала, что им нужно что-то обсудить с командой (например архитектурное решение), они просто звонили в колокол, чтобы привлечь всеобщее внимание. Все пары собирались вместе для импровизированной командной работы, чтобы вместе выработать решение. Когда все были довольны, пары возвращались к своим задачам.%save-sc3%
Мнения
Само собой, нельзя ожидать, что все посчитают TBD решением всех проблем. Мнения насчёт этой методологии разделились, и мы решили привести примеры как противников TBD, так и сторонников. Мнение противника:
И мнение сторонника:
А что вы думаете о TBD? Делитесь в комментариях.
23К открытий23К показов