Аргументы против `git cherry pick`: Рекомендуемая стратегия ветвления для многосредовых проектов dbt
Этот блог был написан до появления сред Staging. Теперь вы можете использовать dbt Cloud для поддержки обсуждаемых здесь шаблонов. Подробнее о средах Staging.
Почему люди используют cherry pick для верхних веток?
Самая простая стратегия ветвления для внесения изменений в код вашего репозитория dbt проекта — это иметь единственную основную ветку с кодом уровня продакшн. Чтобы обновить ветку main
, разработчик должен:
- Создать новую ветку для функции непосредственно от ветки
main
- Внести изменения в эту ветку для функции
- Протестировать локально
- Когда будет готово, открыть pull request для слияния изменений обратно в ветку
main
Если вы только начинаете работать с dbt и решаете, какую стратегию ветвления использовать, этот подход — часто называемый "непрерывным развертыванием" или "прямым продвижением" — является оптимальным. Он предоставляет множество преимуществ, включая:
- Быстрый процесс продвижения для внесе ния новых изменений в продакшн
- Простая стратегия ветвления для управления
Основной риск, однако, заключается в том, что ваша ветка main
может стать уязвимой для багов, которые могут проскользнуть через процесс утверждения pull request. Чтобы провести более интенсивное тестирование и контроль качества перед слиянием изменений в коде в продакшн, некоторые организации могут решить создать одну или несколько веток между ветками для функций и main
.
Прежде чем добавлять дополнительные основные ветки, спросите себя - "действительно ли этот риск стоит добавления сложности в рабочий процесс моих разработчиков"? В большинстве случаев ответ — нет. Организации, использующие простую стратегию с одной основной веткой, (почти всегда) более успешны в долгосрочной перспективе. Эта статья предназначена для тех, кто действительно абсолютно должен использовать многосредовой проект dbt.
Например, один репозиторий dbt проекта может иметь иерархию из 3 основных веток: dev
, staging
и prod
. Чтобы обновить ветку prod, разработчик должен:
- Создать новую ветку для функции непосредственно от ветки
dev
- Внести изменения в эту ветку для функции
- Протестировать локально
- Когда будет готово, открыть pull request для слияния изменений обратно в ветку
dev
В этой иерархической схеме продвижения, после проверки набора веток для функций в dev
:
- Вся ветка
dev
сливается в веткуstaging
После окончательной проверки ветки staging
:
- Вся ветка
staging
сливается в веткуprod
Хотя этот подход — часто называемый "непрерывной доставкой" или "косвенным продвижением" — более сложен, он позволяет обеспечить более высокий уровень защиты вашего продакшн-кода. Вы можете рассматривать эти дополнительные ветки как слои защитной брони. Чем больше у вас слоев, тем сложнее будет двигаться быстро и ловко на поле боя, но вы также будете менее подвержены травмам. Если вы когда-либо играли в D&D, вы поймете этот компромисс.
Поскольку эти дополнительные ветки замедляют ваш рабочий процесс разработки, организации могут быть склонны добавить больше сложности для увеличения скорости — выбирая отдельные изменения для слияния в верхние ветки (в нашем примере, staging
и prod
), вместо того чтобы ждать продвижения всей ветки. Да, я говорю о звере, который называется cherry picking в верхние ветки.
git cherry-pick
— это команда git, которая позволяет применять отдельные коммиты из одной ветки в другую ветку.
Теоретически, cherry picking кажется хорошим решением: он позволяет выбират ь отдельные изменения для продвижения в верхние ветки, чтобы разблокировать разработчиков и увеличить скорость.
На практике, однако, когда cherry picking используется таким образом, он вводит больше риска и сложности и (по моему мнению) не стоит компромисса. Cherry picking в верхние среды может привести к:
- Большему риску нарушения иерархических отношений основных веток
- Ошибочным практикам тестирования, которые не учитывают зависимые изменения кода
- Увеличению вероятности конфликтов слияния, что отнимает время у разработчиков и подвержено человеческим ошибкам
Если вы не тестируете изменения независимо, не стоит продвигать их независимо
Если вы пытались использовать стратегию ветвления, которая включает cherry picking в верхние среды, вы, вероятно, столкнулись с такой ситуацией, когда ветки для функций тестируются только в комбинации с другими:
- Алекс хочет внести изменение в код, поэтому он создает новую ветку от
dev
, названнуюfeature_alex
- Бекка работает над другим изменением кода, поэтому она создает новую ветку от
dev
, названнуюfeature_becca
- Изменения Алекса утверждены, поэтому он сливает
feature_alex
вdev
. - Изменения Бекки утверждены, поэтому она сливает
feature_becca
вdev
. - Кэрол работает над чем-то другим, поэтому она создает новую ветку от
dev
, названнуюfeature_carol
. - Изменения Кэрол утверждены, поэтому она сливает
feature_carol
вdev
. - Команда тестирования замечает проблему с новым дополнением Кэрол в
dev
. - Изменения Алекс а и Бекки срочные и должны быть продвинуты скоро, они не могут ждать, пока Кэрол исправит свою работу. Алекс и Бекка делают cherry-pick своих изменений из
dev
вstaging
. - Во время финальных проверок команда замечает проблему с изменениями Алекса в
staging
. - Бекка настаивает на том, что ее изменения должны быть немедленно продвинуты в продакшн. Она не может ждать, пока Алекс исправит свою работу. Бекка делает cherry-pick своих изменений из
staging
вprod
.
В чем проблема?
В приведенном выше примере команда тестировала feature_becca
в комбинации с feature_alex
— поэтому нет гарантии, что изменения feature_becca
будут успешными сами по себе. Что если feature_becca
полагалась на изменение, включенное в feature_alex
? Поскольку тестирование веток не проводится независимо, рискованно сливать их независимо.
Ветки для функций содержат больше, чем кажется на первый взгляд
Давайте представим другую версию истории, где изменения Кэрол — единственные, которые в конечном итоге сливаются в prod
:
- Алекс хочет внести изменение в код, поэтому он создает новую ветку от
dev
, названнуюfeature_alex
. - Бекка работает над другим изменением кода, поэтому она создает новую ветку от
dev
, названнуюfeature_becca
. - Изменения Алекса утверждены, поэтому он сливает
feature_alex
вdev
. - Изменения Бекки утверждены, поэтому она сливает
feature_becca
вdev
. - Кэрол работает над чем-то другим, поэтому она создает новую ветку от
dev
, названнуюfeature_carol
. - Изменения Кэрол утверждены, поэтому она сливает
feature_carol
вdev
. - Команда тестирования утверждает всю ветку
dev
. dev
слива ется вstaging
.- Во время финальных проверок команда замечает проблему с изменениями Алекса и Бекки в
staging
. - Кэрол настаивает на том, что ее изменения должны быть немедленно продвинуты в продакшн. Она не может ждать, пока Алекс или Бекка исправят свою работу. Кэрол делает cherry-pick своих изменений из
staging
вprod
.
В чем разница?
Поскольку feature_carol
была создана после того, как feature_alex
и feature_becca
уже были слиты обратно в dev
, feature_carol
зависит от изменений, внесенных в других двух ветках. feature_carol
не только содержит свои собственные изменения, она также несет изменения из feature_alex
и feature_becca
. Даже если Кэрол это понимает и делает cherry-pick только отдельных коммитов из feature_carol
, она все равно не в безопасности из-за ранее упомянутой зависимости тестирования. Коммиты feature_carol
тестировались только в комбинации с feature_alex
и feature_becca
.
Повторяющиеся конфликты слияния отнимают время разработки
Чтобы избежать этой проблемы зависимости, ваша команда может подумать о создании веток для функций непосредственно от prod
(вместо dev
). Если мы представим предыдущий сценарий с этой изменением, однако, мы легко увидим, почему это тоже не работает:
- Алекс хочет внести изменение в код, поэтому он создает новую ветку от
prod
, названнуюfeature_alex
. - Бекка работает над другим изменением кода, поэтому она создает новую ветку от
prod
, названнуюfeature_becca
. - Изменения Алекса утверждены, поэтому он сливает
feature_alex
вdev
. - Изменения Бекки утверждены, поэтому она сливает
feature_becca
вdev
. - Кэрол работает над чем-то другим, поэтому она создает новую ветку от
prod
, названнуюfeature_carol
. - Изменения Кэрол утверждены, поэтому она сливает
feature_carol
вdev
. - Команда тестирования утверждает всю ветку
dev
. dev
сливается вstaging
.- Во время финальных проверок команда замечает проблему с изменениями Алекса и Бекки в
staging
. - Кэрол настаивает на том, что ее изменения должны быть немедленно продвинуты в продакшн. Она не может ждать, пока Алекс или Бекка исправят свою работу. Кэрол делает cherry-pick своих изменений из
staging
вprod
.
Теперь feature_carol
содержит только свои индивидуальные изменения — команда может сливать ее ветку независимо в dev
, staging
и, в конечном итоге, в prod
, не беспокоясь о случайном подтягивании изменений из других двух веток.
В чем проблема?
Однако возникает новая проблема, если feature_alex
или feature_becca
изменяют те же строки кода, что и feature_carol
. Когда feature_carol
сливается в каждую из основных веток, Кэрол придется решать конфликты слияния каждый раз одинаковым образом, чтобы сохранить иерархию веток. Это занимает время и подвержено человеческим ошибкам.
Что делать вместо этого: Рекомендуемая стратегия ветвления для многосредовых проектов dbt
В конце концов, cherry picking в верхние ветки — это стратегия ветвления, которая вызывает больше проблем, чем стоит.
Вместо этого, если вы решите использовать стратегию ветвления, которая включает несколько основных веток (таких как dev
, staging
и prod
):
- Защитите свою ветку
dev
с помощью dbt cloud CI job - Обеспечьте тщательные проверки кода (ознакомьтесь с нашим рекомендуемым шаблоном PR)
- Продвигайте каждую основную ветку иерархически друг в друга
Если возникают проблемы во время тестирования на ветке dev
или staging
, разработчики должны создавать дополнительные ветки по мере необходимости для исправления багов, пока вся ветка не будет готова к продвижению.
Как уже упоминалось, у этого подхода есть явный недостаток — может потребоваться больше времени для исправления всех багов, обнаруженных во время тестирования, что может привести к:
- Задержкам в развертывании
- Замораживанию кода на
dev
, создавая очередь устаревших веток для функций, ожидающих слияния
К счастью, мы можем смягчить эти задержки, проводя тщательное тестирование на индивидуальных ветках для функций, обеспечивая, чтобы команда была крайне уверен а в изменении до слияния ветки для функции в dev
.
Кроме того, разработчики могут дополнить вышеуказанный рабочий процесс, создавая хотфиксы для быстрого решения багов в верхних средах.
Хотфикс
— это ветка, которая создается для быстрого исправления бага, обычно в вашем продакшн-коде. Если в prod
был обнаружен критический баг, создается ветка хотфикса от prod
, затем сливается в prod
, а также во все подчиненные ветки (dev
и staging
) после утверждения изменения. Аналогично, если в staging
был обнаружен критический баг, создается ветка хотфикса от staging
, затем сливается в staging
, а также во все подчиненные ветки (dev
) после утверждения изменения. Это позволяет исправить баг в верхней среде, не дожидаясь следующего полного цикла продвижения, но также гарантирует, что иерархия ваших основных веток не будет потеряна.
Даже с учетом своих сложностей, иерархическое продвижение веток является рекомендуемой стратегией ветвления при работе с несколькими основными ветками, потому что оно:
- Упрощает ваш процесс разработки: Ваша команда работает более эффективно с меньшим количеством сложных правил для соблюдения
- Предотвращает конфликты слияния: Вы экономите время разработчиков, избегая необходимости вручную решать сложные конфликты слияния снова и снова
- Гарантирует, что код, который тестируется, — это код, который в конечном итоге сливается в продакшн: Вы избегаете кризисных сценариев, когда неожиданные баги проникают в продакшн
Теперь я признаю это: этот блог-пост был в основном просто сессией для выпуска пара, предоставляющей мне катарсический выход для ярости против cherry picking (мои личные сообщения в Slack открыты, если вы хотите увидеть все мемы, которые не попали в этот пост).
И вы можете подумать... ладно, Грейс, я не буду делать cherry pick в верхние ветки. Но как мне действительно настроить мой dbt проект для правильного использования иерархического продвижения веток?
Не волнуйтесь, руководство и обучающий курс уже в пути ;)
Comments