Резюме

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

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

«Нет, мы не стремимся к освоению, ты в своем уме?»

Позвольте мне рассказать вам историю.

Недавно я присоединился к консалтинговой компании. Самое интересное в работе консультанта - это то, что вы можете работать с множеством клиентов, и, действительно, с тех пор, как я начал работать пару месяцев назад, я работал с 3 разными командами. Для некоторого контекста все эти команды были размещены в одном месте. Их размер составлял около 6–8 человек, в основном инженеры и владельцы продукта, иногда с мастером схватки. Почти все в команде сидят рядом друг с другом, а изо дня в день работают странные люди.

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

Я: «Хорошо, а что происходит, когда я возвращаю код?»

Они: «Вы создаете запрос на перенос, помещаете ссылку в резерв, и кто-то ее проверяет»

Я: «О, так ты не сразу станешь мастером?»

Они: * смотрят на меня широко открытыми глазами, как будто они только что увидели инопланетянина ... несколько секунд тишины ... *

Они: * неодобрительно качают головами * «Нет, мы не стремимся к совершенству. Сначала необходимо проверить код »

В этот момент я вижу, как они думают: «Этот парень серьезно? Это тот человек, который должен нам «помогать»?

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

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

Как команды используют краткосрочные функциональные ветки и запросы на вытягивание

Позвольте мне объяснить, что я наблюдал за типичным процессом команды, использующей краткосрочные функциональные ветки:

  • У команды есть еще много историй, которые нужно сделать. Обычно у них будет «бэклог спринта» с кучей историй, которые нужно сделать в этом спринте. (Между прочим, у меня есть некоторые идеи о том, как это можно было бы сделать лучше, подробнее об этом в следующем посте.)
  • Разработчик свободен, поэтому они смотрят в бэклог и выбирают историю. (Процесс принятия решения о том, что выбрать, варьируется и может быть ярким признаком наличия ряда проблем в команде. Но, опять же, подробнее об этом в следующей публикации.)
  • Разработчик создает новую ветку из master и начинает работу над историей.
  • В процессе работы они фиксируют и отправляют код в ветку с любой удобной для них частотой. Это могут быть минуты, часы или дни.
  • Всякий раз, когда код загружается, инструмент сборки (Jenkins, circleci и т. Д.) Запускает сборку в ветке. Обычно сборка компилирует и упаковывает код с последующим запуском некоторых тестов. Возможно, он может быть развернут в среде разработки.
  • Когда разработка завершена, что может занять от нескольких дней до недели, разработчик создает запрос на вытягивание.
  • Кто-то из команды просматривает пулреквест. В некоторых командах это может проверить кто угодно, но в других командах это должен делать технический руководитель или старший разработчик.
  • Рецензенты могут оставлять комментарии к PR с просьбой внести изменения. В этом случае разработчик возвращается к реализации этих изменений, а затем снова отправляет PR. Этот шаг можно повторить несколько раз.
  • В конце концов PR утверждается. Разработчик объединяет ветку в мастер, обычно путем объединения всех коммитов из ветки в одну большую фиксацию в мастере («сжатие и слияние»).
  • Инструмент CI запускает сборку на master. Запускается конвейер для внедрения этих изменений в производственную среду - иногда они развертываются автоматически на всем пути к производству, иногда они требуют утверждения вручную на одном или нескольких этапах.

Ваш точный рабочий процесс может отличаться, но я ожидаю, что многим это покажется знакомым. Этот рабочий процесс известен под названием GitHub Flow.

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

Удачная альтернатива: разработка на основе магистрали, без ветвей

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

1. Парное программирование

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

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

2. Получите сборку, которой вы доверяете

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

Для достижения этих двух целей командам обычно необходимо следовать таким практикам, как TDD, BDD, и иметь эффективную стратегию тестирования - например, следование пирамиде тестирования, чтобы свести к минимуму количество медленных тестов.

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

3. Используйте «переход по абстракции» или флаги функций, чтобы скрыть незавершенную работу.

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

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

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

Многочисленные преимущества разработки на основе магистрали

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

  • Ранняя обратная связь: обратная связь с PR приходит только тогда, когда разработчик думает, что они готовы. На этом этапе обычно уже слишком поздно что-либо менять. Когда вместо этого происходит объединение в пары, обратная связь происходит во время написания кода - или даже до этого, когда вы обсуждаете, как подойти к проблеме, - так что это намного проще изменить.
  • Более качественная обратная связь: при использовании PR отзывы обычно помещаются в текстовое поле для комментариев. За редким исключением, это вряд ли передаст нюанс, который часто требуется при обсуждении программного обеспечения. Автор оригинала, естественно, защищает то, что он воспринимает как «свой код», а комментарии, которые легко кажутся грубыми, имеют сильную тенденцию вызывать раздражение или конфликты. Вместо этого, объединяясь в пары, вы обсуждаете свои идеи лицом к лицу, где на порядок легче уточнить, что вы пытаетесь сказать, и провести здоровую дискуссию.
  • Коллективное владение кодом: когда код написан только одним человеком, этот человек склонен рассматривать это как «мой код». Вы начинаете слышать такие вещи, как «ох, это написал Алекс, вам нужно спросить их» или «мы не сможем работать над этим, пока Сэм не вернется». При объединении вместо этого гораздо более вероятно, что команда создаст коллективное владение кодом и будет рассматривать его как «наш код».
  • Стиль программирования в команде: хорошие команды пишут программное обеспечение, которое выглядит так, как будто его написал один и тот же человек. Это признак того, что люди ценят командную работу больше, чем свои личные предпочтения. В паре гораздо проще установить командный стиль, и всегда об этом напоминают.
  • Более частая интеграция (фактическая непрерывная интеграция): довольно часто PR поднимается только тогда, когда история / функция завершена. Вместо этого, продвигаясь напрямую к мастеру, мы интегрируем наш код немедленно и возвращаемся к реальному значению слова непрерывный в Непрерывной интеграции.
  • Мы привыкаем ничего не ломать: когда мы работаем непосредственно с мастером, мы действительно не хотим выдвигать что-либо сломанное, поэтому мы привыкаем запускать сборку локально, прежде чем нажимать, и реализовывать код в серии неразрывных изменений. С другой стороны, с ветвями слишком легко игнорировать неудачную сборку и исправлять ее только в конце.
  • Проще выполнять крупные рефакторинги: работая в ветке, мы боимся делать вещи, которые могут вызвать конфликты слияния, например переименование пакетов, перемещение вещей, архитектурные изменения. Хотя это все еще непросто, при непосредственной работе над мастером это, по крайней мере, легче делать, поскольку мы можем фиксировать небольшие изменения и оставлять остальную команду в курсе последних событий. Что касается действительно сложных изменений, то в моей предыдущей команде мы собирались вокруг одной машины для быстрого импровизированного сеанса моб-программирования, где мы все сидели вместе и обсуждали, как будет выглядеть решение.
  • Больше видимости того, над чем все работают: когда изменения находятся в ветке, они намного менее заметны, чем если бы мы все нажимали на мастер. Становится намного легче увидеть, над чем все работают, и определить, нужна ли помощь одному из наших товарищей по команде.
  • Лучшие инструменты для проверки изменений: вместо того, чтобы смотреть на красные / зеленые линии на веб-странице, как это обычно делают при просмотре PR, есть гораздо лучшие инструменты для проверки того, что изменилось, прежде чем мы его зафиксируем. Мы можем использовать нашу IDE или любой другой инструмент, который нам нравится.
  • Сохранение исходной истории коммитов: при слиянии изменений из ветки в мастер часто люди сжимают все коммиты в одну, теряя историю того, как и почему первоначальный автор внес эти изменения. Вместо этого, работая над мастером, мы сохраняем полную историю каждого коммита по мере его совершения. Я работал с кодовыми базами, которым было 10 лет, авторы которых давно ушли, и возможность использовать историю git для точного определения коммита, в котором что-то изменилось, было бесценным, поскольку это дало нам возможность прочитать сообщение, которое было отправлено с это и любые другие изменения, которые были сделаны одновременно.

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

Магистральная разработка - признак здоровья команды

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

Можно сказать, что разработка на основе ствола является показателем здоровья команды. И действительно, это то, что они обнаружили в книге Accelerate: после изучения более 10 000 сотрудников и 2 000 организаций, исследование показало, что существует сильная корреляция между командой, занимающейся разработкой на основе магистрали, и этой командой, которая является высокоэффективной. В частности, они обнаружили, что в высокопроизводительных командах филиалы живут меньше суток.

Оптимизация для командной работы

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

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

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

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

Хорошие варианты использования функциональных веток

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

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

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

«Хорошо, я убежден. Как туда попасть?"

Если вы в настоящее время используете ветки функций и хотите перейти к этапу, на котором вы можете работать непосредственно с мастером, вот несколько советов:

  • Пересмотрите свою стратегию тестирования и работайте над стабильной сборкой, которой вы доверяете. Это может означать, что вам нужно начать делать больше TDD, чтобы добавить больше тестов, сохраняя при этом быструю сборку.
  • Начать сопряжение еще. Если вы обычно не занимаетесь спариванием, я предлагаю вам сначала начать делать это на сложных задачах, потому что будет легче убедить кого-то из вашей команды присоединиться к вам. Затем вы можете начать делать это по все большему и большему количеству дел и создать в команде основную группу людей, которым нравится работать в паре. Остальная часть команды в конечном итоге последует этому, особенно если вы установите правило, согласно которому код, написанный при объединении в пары, не нуждается в проверке кода.

Дополнительные ресурсы

Помимо прямых ссылок в статье, это хорошие ресурсы, если вы хотите узнать больше по теме:

Особая благодарность Уиллу, Араму и Притешу за их ранние отзывы и за помощь в разработке и совершенствовании этих идей на протяжении многих лет. Также благодарим Jürgen Gmach за просмотр черновика и отзыв.

Бонус: разработка на основе магистральных линий в реальной жизни

Вот подробное описание того, как моя предыдущая команда занималась разработкой на основе магистрали и сразу же стремилась к освоению. Контекст очень похож на контекст описанных выше команд: совместная команда из 6–10 человек; 4–6 разработчиков, 1–2 тестировщика, 1 бизнес-аналитик, 1 руководитель группы.

  • Разработчики работают в паре на постоянной основе.
  • Одна пара разработчиков подхватывает новую историю. В рамках определения подготовки к разработке они пишут скелет одного или нескольких приемочных тестов и список задач для истории.
  • Когда пара думает, что они готовы, они собирают команду и показывают всем приемочные испытания с намерением подтвердить, что все одинаково понимают, о чем идет речь и что команда собирается реализовать. Это называется BDD, где мы используем примеры для достижения общего понимания.
  • Каждая пара разработчиков выбирает задачу из истории. В большинстве случаев над одной историей работали 2 или 3 пары, просто выбирая независимые задания.
  • Каждая пара совершает и подталкивает прямо к мастеру с частотой от нескольких минут до нескольких часов. Они совершают фиксацию каждый раз, когда переходят в зеленое состояние: рефакторинг красный-зеленый, фиксация и нажатие. Мы все любили небольшие, частые коммиты с четкими сообщениями, указывающими, * почему * мы только что сделали то или иное изменение. Сообщение о фиксации идеально использует бизнес-термины и фокусируется на бизнес-результате, которого мы пытаемся достичь с помощью этой фиксации. Хороший пример: «Реализовал соотв. тест, чтобы доказать, что только один клиент может быть выделен для порта » или « Переименован класс, поскольку мы обнаружили, что остальная часть бизнеса называет «комнату» «колокольным пространством» » .
  • Всякий раз, когда разработчики извлекают данные из git, они перемещают их, чтобы коммиты оставались в том же порядке, в каком они происходили в реальной жизни.
  • Перед фиксацией пара всегда запускает локальную сборку, чтобы гарантировать ее успех. Сборка обычно занимает от 30 секунд до 2 минут.
  • В нашей стратегии тестирования мы следовали пирамиде тестирования, всегда предпочитая более быстрые тесты медленным. Нашим усилиям во многом способствовало использование чистой архитектуры, которая позволяла изолировать бизнес-логику от технических деталей. Это не единственный способ, но он отлично сработал для нас.
  • В любой момент времени кодовая база всегда доступна для выпуска с помощью шаблона «переход по абстракции». На практике, будучи Java-проектом, мы просто не подключались к компонентам Spring до самой последней минуты. Весь новый код был доступен для тестирования, но не использовался в производстве, пока мы не были готовы подключить его.
  • Всякий раз, когда код загружается, инструмент CI строит его и запускает все тесты. Мы нигде не развертывались автоматически, но могли бы сделать это, если бы захотели.
  • Разработка рассказа обычно завершалась через несколько дней. На этом этапе мы снова собрались вокруг команды, чтобы продемонстрировать то, что только что было реализовано.
  • Последняя версия приложения теперь будет выпущена в промежуточную среду, где наш эксперт по обеспечению качества проведет некоторое исследовательское тестирование (обычно продолжающееся от нескольких часов до нескольких дней).
  • Если не было никаких сюрпризов, приложение продвигалось в рабочую среду (обычно на следующий день).
  • Тем временем, ближе к концу развития предыдущей истории, одна пара начала бы смотреть на следующую историю, чтобы подготовить ее и сохранить хорошее течение. Мы использовали ограничения незавершенного производства, чтобы держать этот поток под контролем.
  • Если в какой-то момент одна пара чувствовала необходимость обсудить что-то в команде (например, архитектурные решения; дизайн; компромиссы), они просто звонили в колокольчик, чтобы привлечь всеобщее внимание. Все пары собирались для импровизированного сеанса моб-программирования, чтобы вместе выработать решение. Когда все были счастливы, пары снова разделились.