Наши команды практикуют подход Trunk Based Development – новый код сразу добавляется в мастер-ветку, сторонние ветки живут максимум несколько дней. А чтобы коммиты не мешали друг другу, разработчики используют фича-флаги (Feature Flags) – переключатели в коде, которые запускают и останавливают работу его компонентов.
Рассмотрим обычную итерацию разработки: планирование, уточнение требований, создание задач в трекере, разработка. По мере готовности задачи разворачиваются на тестовом окружении для проверки, релизная ветка стабилизируется. Потом наконец наступает выпуск, и команда может наконец получить реальную обратную связь от пользователей.
Если вы стремитесь сокращать Time-to-Market, это недопустимо долго. Чем раньше вы получите обратную связь от пользователей, тем скорее вы исправите ошибки, тем меньше времени вы тратите на неудачные идеи, тем больше ресурсов можете уделить идеям удачным.
Чтобы обновления быстрее доезжали до прода, одна итерация должна включать одну фичу. Именно поэтому нужно сокращать срок жизни веток.
Проблемы долгоживущих веток
Конфликты между коммитами (Merge hell). Откладывание релиза, интеграция с внешней системой, прочие внешние факторы могут привести код в нерабочее состояние.
Трудности с шарингом кода. Другим членам команды может потребоваться код из новой ветки, если фичи зависят друг от друга. Приходится стартовать ещё одну ветку только ради доступа к этому коду.
Проблемы тестового окружения. Если тестовый сервер один, а фиче-веток много, параллельно тестировать задачи не получится.
Тяжело откатить изменения. Проблемы после релиза – обычное дело, и если из-за новой фиче-ветки начинаются сбои, разработчикам приходится выбирать между хотфиксом и ревертом, лезть в исходный код, заново выкладывать решение.
Как Trunk Based Development решает эти проблемы
Trunk Based Development (от англ. trunk – «ствол дерева») – метод разработки кода на основе одной главной ветки. В отличие от подхода Gitflow, TBD позволяет разработчикам добавлять новые модули сразу в master. Второстепенные feature-ветки также могут создаваться, но они имеют короткий срок жизни.
Trunk Based Development предполагает только одну ветку для разработки, которая называется trunk. В любой момент эту ветку можно развернуть её на проде, а разработка, как и прежде, идёт в отдельных фича-ветках. Только теперь эти ветки живут не более двух дней.
Все изменения в trunk вливаются через пул-реквесты. Изменения небольшие, поэтому процесс ревью не затягивается. Попадание нового кода в trunk запускает процессы автоматического билда, тестов и развёртывания на необходимые окружения.
Но как вести разработку в одной ветке, если какие-то фичи ещё не готовы, а релиз завтра? Тут нам на помощь приходят Feature Flags.
Как работают Feature Flags
По сути своей, это IF-блок, который запускает кусок кода при выполнении некого условия. Самое простое – разработчик сам прописывает, включать или выключать код. Могут быть параметры посложнее: например, по расписанию или только для пользователей с такими-то уровнем доступа. Или наоборот – фича отключается, если нагрузка на систему превышает заданный порог.
В точке переключения мы обращаемся к переключателю (toggle router), который определяет состояние фичи. Чтобы понять, какой нужен флаг, роутер обращается к его конфигурации и контексту. Первая определяет общую стратегию включения флага (основные условия для его работы), второй включает любую дополнительную информацию (например, имя пользователя, который отправил запрос, текущее время и т.д.).
Как использовать Feature Flags
Технически фиче-флаги работают одинаково, а по способу применения их можно разделить на следующие категории:
Релизные (release toggles): скрывают неготовые фичи, уменьшают количество веток, открепляют запуск фичи от даты деплоя. Основной тип флагов.
Экспериментальные (experiment toggles): используются для A/B тестирования, позволяют таргетировать функции на разные группы пользователей. Таким образом вы можете развернуть новый сервис на Х% аудитории, чтобы оценить нагрузку или собрать первые отзывы.
Разрешающие (permission toggles): открывают доступ к платным фичам или закрытым функциям администратора. Такие флаги могут жить очень долго, быть очень динамичными и менять своё состояние при каждом запросе.
Операционные (ops toggles): отключают ресурсоёмкие функции. Например, так можно регулировать работу приложения на слабых смартфонах или застраховаться от падения производительности при запуске новой функциональности – флаг отключит модуль до того, как тот вызовет критический сбой.
### Что дают Feature Flags
Непрерывная доставка фич со стабильным качеством – возможность отключить нерабочий код снижает риски в релизной версии.
Тестирование новых фич в боевых условиях – фиче-флаги позволяют постепенно внедрять сервисы, контролируя риски при релизе на реальную аудиторию.
Возможность развивать несколько версий ПО параллельно – TBD и фичефлаги позволяют предлагать разные функции разным группам пользователей, при этом поддерживать все эти версии ПО может всё та же одна команда.
Как управлять флагами
Проприетарные решения: LaunchDarkly, Bullet-Train, Unleash. Каждый продукт предлагает какие-то свои преимущества, каждый по-своему удобный. Но за лицензию придётся платить, а гибкость настройки зависит от разработчика системы.
Open source решения: Moggles, Esquilo. Это бесплатные решения, но чтобы они заработали у вас, потребуется над ними поколдовать. Кроме того, придётся подбирать продукт с таким набором функций, который вас устроит.
Собственная система управления: вариант, которым пользуемся мы. Это единственное в своём роде решение, которое целиком нас устраивает. В будущих постах расскажем подробнее.
Как флаги работают у нас
Feature Flags Portal (FF-Portal): Web + REST API приложение для манипулирования состоянием флагов. Напрямую работает с хранилищем флагов.
Feature Flags Storage (FF-Storage): персистентное хранилище с настройками флагов и их статусами.
Kubernetes ConfigMap (FF-configmap): k8s ConfigMap ресурс, который собирается на основе данных, которые хранятся в FF-Storage в удобном формате для конечного приложения. Изменение данных флагов через FF-Portal также влечёт к изменению FF-configmap.
Microservice (MS): Микросервис, который использует FF-configmap как источник конфигурации при старте приложения. При изменений FF-configmap, микросервис делает перезагрузку своей конфигурации.
Приложение считывает конфигурацию флагов с FF-ConfigMap, который монтируется к Pod-у как файл. При изменении ConfigMap, k8s обновит и файл, далее приложение среагирует на изменение файла и перегрузит конфигурацию флагов.
Изменение флагов происходит через портал, который отправляет интеграционное сообщение в шину при обновлении статуса. Компонент Config Updater обновляет значения флагов в FF-ConfigMap через K8S API.
Напоследок о тестировании
Возникает вопрос, как тестировать продукт с фиче-флагами? На первый взгляд, флаги усложняют в этот процесс – если переключателей становится много, то и количество всевозможных состояний резко растёт.
Но не всегда флаги зависят друг от друга. Поэтому мы для релиза тестируем два предельных случая: 1) все новые флаги выключены и 2) все флаги включены.
Практика показывает, что обычно этого достаточно.