Trunk-Based Development
Базовые принципы
- Любые коммиты в trunk не должны ломать сборку
- Любые коммиты в trunk должны быть маленькими настолько, чтобы review нового кода не занимало более 10 минут
- Релиз выпускается только на основе trunk
GIT flow VS TBD
Конфликты
Долгоживущие ветки
Проблемы:- Накапливаются конфликты
- Код из ветки недоступен для выполнения других задач
- Параллельная разработка одного и того же механизма
- Рассинхронизация логики кода в разных ветках
- Ломается история GIT при конфликтах
- Блокируется деплой готовой задачи, если вначале нужно задеплоить другую
- Блокировка выполнения второстепенной задачи, если нужен код из другой задачи
При GIT flow долгоживущие ветки - это норма. У нас в проекте были ветки возрастом более года. Актуализация таких веток занимала несколько дней. А ведь эти ресурсы можно было использовать на разработку новых фич!
При TBD если разработка идет непосредственно в master/trunk таких проблем нет - код актуализируется после первого pull запроса. Разница с local state будет минимальна. В большинстве случаев получим fast forward. При работе в feature ветке могут возникать проблемы описанные выше. Чтобы минимизировать проблемы, TBD рекомендует ограничение жизнь ветки в 2 дня. Это условный интервал, за который не должно накопиться много конфликтов. Дополнительным положительным фактором является то, что основная часть команды продолжает писать в master, и этот код можно подливать в feature ветку и тем самым уменьшать количество конфликтов, которые могут появиться в день X. Когда ветка вольется в master. Важно соблюдать правило 2 дней!
Разный state БД
Это порождает ошибки разработки и деплоя.- Ошибка генерации миграции. Например, при добавлении новой колонки мы указываем после какой колонки надо добавить новое поле. Но этой колонки может не оказаться на момент деплоя.
- Мы не можем просто так взять и добавить колонку с constraint not null так как в других ветках код не будет работать. Это порождает тех. долг, мусорный код и ошибки, которые могут быть спровоцированы не законченным кодом.
- Ошибки doctrine. Если добавить новый тип данных doctrine, но код не распространить по всем веткам, то там где его нет, будут ошибки во время калькуляции метаданных.
При GIT flow различное состояние бд - это норма
При следовании TBD эти проблемы исключаются. Изменение структуры бд всегда происходит в мастер. Разработчикам остается актуализировать ветку и проблема решена.
Разработка
GIT flow
При GIT flow нужно работать над задачей в отдельной ветке Задач много, следовательно веток много. Все ветки различаются. Различается набор сервисов. Различается структура бд. Для начала разработки нужно переключиться на ветку Обычно приходится почистить кеш. Применить миграции. Для тестов переинициализировать бд. Без регламента на мелкие commit`ы, провоцируются ситуации когда вся задача складывается в один commit, что делает историю менее читаемой. Так же при таком подходе могут накапливаться изменения файлов при разработке. Когда возникает необходимость переключиться на другую задачу, то нужно спрятать незакомиченные правки (при возвращении к задаче нужно будет снова накатить патч изменений). Этот процесс занимает время и отвлекает от задачи, уменьшает желание уходить с ветки.TBD
При TBD нужно работать мелкими правками. Большую часть времени работаем в мастер. У всех приблизительно одинаковый набор сервисов, состояние БД. Небольшие различия в коде, вероятно, даже не повлияют на работоспособность окружения. Если возникает необходимость переключиться на другую задачу. Нет необходимости уходить в другую ветку и пересобирать окружение. Задачу можно сделать здесь на месте. Закомитить правку и продолжить основную задачу.Release ready
GIT
Состояние Release ready достигается путем слияния feature ветки в staging и проверке на dev стенде. По факту мы тестируем измененный код. Отличающийся от master и production. Что может спровоцировать ряд ошибок.TBD
Состояние Release ready достигается за счет создания release ветки и проверке на dev стенде. Как и в случае с GIT flow. Мы создаем отдельную ветку и тестируем. Различия в том что в случае GIT на дев стенде одновременно находится ряд задач в финальной стадии реализации, нуждающиеся в мелких правках. При TBD на дев стенде будут находиться сразу все задачи принятые в работу в разных состояниях готовности. В данном случае тестируется ровно тот же код что окажется в production, с теми же feature flags. Это позволяет избежать ряда ошибок и гарантировать что эту ошибку возможно воспроизвести локально, а не искать ветку в рамках которой появилась ошибка. Есть ряд правил которые минимизируют количество ошибок на дев стенде- Маленькие атомарные комиты. Позволяют легче воспринимать изменения и снижают риск ошибки.
- Continuous review Теперь код не спрятан в отдельной ветке, а доступен всем. Правки прилетают небольшие и каждый может их изучить. Тем самым будет происходить слежение за развитием продукта и понимание что меняется
- Автотесты
- Парное программирование Позволяет снизить риск, того что будет допущена ошибка и спроектировать качественное архитектурное решение по задаче
- Feature flags Весь не готовый код должен быть скрыт за флагом
- Сложные задачи по прежнему можно делать в отдельных ветках Важно декомпозировать задачу так, чтобы работы по ней длились не более 2 дней.
- Частые деплои. Уменьшает количество правок которое должно уйти в production. Следовательно и риски что-то поломать.
Схема работы по TBD
TBD
Branch by Abstraction
Можно выделить следующие этапы:- Выделить интерфейс для заменяемой функциональности.
- Заменить прямой вызов реализации в клиенте на обращение к интерфейсу.
- Создать новую реализацию, которая реализует интерфейс.
- Подменить старую реализацию на новую.
- Удалить старую реализацию.
Пример, реализации задачи
Имеем механизм отправки писем
Выделяем интерфейс
Интегрируем его в старый механизм
Заменяем прямой вызов на обращение через интерфейс
Делаем новую реализацию
Заменяем вызов старого механизма на новый
либо сразу через DI
и в коде будет так
Feature Flags
Суть подхода в том чтобы обернуть код в if с проверкой пред. установленной опции. Например,
или
Все флаги описываются в enum, что позволяет быстро отследить все места в проекте где флаг используется. Для применения флагов на проекте, было реализовано следующее решение:
- Сначала берутся значения из .env файлов
- Далее проверяются значения в переменных окружения, например: если явно передать значение в docker
- В конце проверяется значение в сессии, что позволяет быстро включить/выключить флаг и проверить функционал.
Для удобного управления флагами и мониторинга состояния активности флага, на коленке была собрана служебная страница
Правил с флагами будет два:
- После того, как функциональность полностью протестирована и стабильно работает, флаг нужно удалить.
- Мест в коде, где идет ветвление по одному и тому же feature флагу, должно быть минимальное количество.
Декомпозиция задач и Short-lived ветки
Все ветки кода, кроме главной, должны иметь короткий срок жизни, максимум – несколько дней. Этого можно добиться за счет мелкой декомпозиции: ветка будет небольшой, если она решает небольшую задачу. Правильная постановка задач на этапе планирования играет очень важную роль. Можно, например, придерживаемся принципа декомпозиции задач INVEST. Декомпозиция по INVEST определяет, каким должен быть пользовательский сценарий (user story):- Independent — независимый
- Negotiable — написанный понятным языком
- Valuable — несущий ценность
- Estimable — поддающийся оценке
- Small — компактный, не более 40 часов разработки
- Testable — тестируемый в широком смысле