Trunk-based development - усі розробники часто (щодня чи частіше) зливають зміни в основну гілку, без довгоживучих гілок фіч. Прапорці функцій (feature flags) роблять це можливим: незавершена функція потрапляє в продакшен вимкненою.
Навіщо так робити:
- без болісних злиттів: гілка, що жила місяць, конфліктує з усім; щоденні маленькі злиття - ні;
- розгортання ≠ реліз: код деплоїться коли завгодно, а функція вмикається окремим рішенням - для внутрішніх користувачів, 5% аудиторії, конкретної команди;
- швидкий відкат: проблема - вимкнути прапорець за секунди, без деплою;
- експерименти: A/B-тести різних варіантів.
Види прапорців (за класифікацією з статті Фаулера):
- релізні - приховують незавершене; живуть дні чи тижні;
- операційні («аварійні вимикачі») - вимкнути важку функцію під навантаженням; живуть довго;
- експериментальні - A/B-тести;
- дозволи - функції для певних тарифів чи ролей; фактично частина продукту.
У Laravel - Laravel Pennant:
Feature::define('new-checkout', fn (User $user) => $user->isInternal() || Lottery::odds(1, 20)->choose());
if (Feature::active('new-checkout')) {
return $this->newCheckout($request);
}
@feature('new-checkout') ... @endfeature
Архітектурні наслідки:
- код має підтримувати обидва шляхи одночасно - а це прямо впливає на дизайн: нову реалізацію зручно додати поруч зі старою за спільним інтерфейсом (стратегія, «branch by abstraction»), а не переписувати старий код на місці;
- міграції бази - сумісні в обидва боки: нова колонка додається до використання, стара видаляється після вимкнення прапорця (expand/contract);
- тестування комбінацій - обидва стани прапорця мають бути протестовані.
Головний ризик - накопичення прапорців. Кожен прапорець подвоює кількість можливих станів системи. Релізний прапорець, що лишився в коді після повного запуску, - технічний борг. Практика: дата видалення при створенні, задача на прибирання, інвентаризація.
Передумови trunk-based development: швидкі автоматичні тести в CI, невеликі pull request-и й швидке рев'ю - інакше «часті злиття» ламатимуть основну гілку.