Когда деплой кода не означает релиз фичи
Ваша команда только что закончила большой рефакторинг алгоритма поиска. Код протестирован, проверен и развёрнут в продакшене. Но вот в чём дело: никто его ещё не использует. Новый алгоритм лежит на сервере, скомпилированный и готовый к работе, но каждый пользователь по-прежнему получает результаты от старого. И это сделано намеренно.
Такой сценарий может показаться странным, если вы привыкли к деплоям, где каждое изменение немедленно влияет на пользователей. Но на самом деле это мощный паттерн, который разделяет две вещи, которые большинство команд считают одним и тем же: деплой кода и релиз фичи.
Проблема, которую не решает один деплой
Канареечные деплои и staged rollout решают одну задачу хорошо: они контролируют, какой объём трафика попадает на новую версию приложения. Вы отправляете 5% пользователей на новую версию, следите за ошибками, затем увеличиваете до 100%. Это работает, когда нужно постепенно раскатить целую версию.
Но что, если вы хотите включить конкретную фичу только для определённых пользователей, не разворачивая разные версии кода? Возможно, вы хотите, чтобы внутренние тестировщики видели новые результаты поиска, а все остальные оставались на старом алгоритме. Или включить фичу сначала для пользователей из одного региона. Или протестировать новый процесс оформления заказа на 10% пользователей, но только если они заходят с мобильного приложения.
Канареечные деплои и staged rollout этого не решают. Они работают на уровне версий, а не на уровне фич. Вам нужно нечто, что живёт внутри вашего кода и принимает решения для каждого запроса, пользователя или сессии.
Фича-флаги: слой управления внутри вашего кода
Фича-флаги — это условные проверки в вашем коде, которые определяют, должна ли фича быть активна для данного пользователя. Ключевая идея в том, что условие не зависит от того, какая версия кода запущена. Оно основано на конфигурации, которая может меняться в любой момент без повторного деплоя.
Вот как это выглядит на практике. Ваша команда заканчивает новый алгоритм поиска. Код развёрнут в продакшене как часть последней версии. Но внутри функции поиска есть проверка:
Вот пример на JavaScript, иллюстрирующий этот паттерн:
const featureFlags = require('./feature-flags');
function search(query, user) {
if (featureFlags.isEnabled('new-search', user)) {
return newSearchAlgorithm(query);
} else {
return oldSearchAlgorithm(query);
}
}
// Флаг по умолчанию выключен, поэтому все пользователи идут по старому пути.
// Когда будете готовы, включите флаг для тестировщиков через панель управления.
if feature_flag.is_active("new_search_algorithm", user):
return new_search_algorithm(query)
else:
return old_search_algorithm(query)
Флаг по умолчанию выключен. Каждый пользователь по-прежнему получает старый алгоритм, хотя новый код лежит на том же сервере. Когда вы будете готовы, вы включите флаг для внутренних тестировщиков. Они начнут видеть новые результаты. Вы следите за их поведением. Если что-то пошло не так, вы выключаете флаг обратно. Никакого отката. Никакого повторного деплоя. Просто изменение конфигурации, которое вступает в силу за секунды.
Зачем разделять деплой и релиз?
Первое преимущество — одновременно психологическое и практическое: деплой перестаёт быть событием с высокими ставками. Когда каждый деплой немедленно активирует все изменения, вы накапливаете работу, тщательно тестируете и задерживаете дыхание во время релизных окон. Когда деплой и релиз разделены, вы можете деплоить ежедневно или даже несколько раз в день, зная, что новый код остаётся неактивным, пока вы его явно не включите.
Второе преимущество — гранулярный контроль. Фича-флаги не просто включены или выключены для всех. Вы можете задавать правила таргетинга:
- Активно для пользователей с определёнными ID
- Активно для пользователей из определённых регионов
- Активно для внутренних тестировщиков или бета-пользователей
- Активно для процента от общего числа пользователей
Эти правила можно комбинировать. Вы можете начать с 5% пользователей, наблюдать день, увеличить до 25%, затем до 50%, затем до 100%. Каждое изменение происходит через панель управления или API-вызов. Никаких изменений кода. Никаких деплоев.
Третье преимущество — аварийный выключатель. Когда фича вызывает проблемы после активации, вы можете отключить её из одного места. Это намного быстрее, чем ждать завершения пайплайна отката. За секунды проблемная фича отключается, и пользователи возвращаются к старому поведению. Для инцидентов в продакшене эти секунды имеют значение.
Скрытая стоимость: флаговый долг
Фича-флаги добавляют сложности в кодовую базу. Каждый флаг вводит условную ветку, которую нужно поддерживать. Если ваша команда создаёт флаги и никогда их не удаляет, код заполняется мёртвыми условиями, которые никто не помнит.
Типичный сценарий: создаётся фича-флаг для нового процесса оформления заказа. Процесс успешно раскатывается на 100% пользователей. Старый код оформления заказа всё ещё там, защищённый флагом, который всегда выключен. Через несколько месяцев новый разработчик видит флаг и задаётся вопросом, актуален ли он. Никто не знает. Флаг остаётся, потому что удалять его кажется рискованным.
Это флаговый долг, и это основная операционная стоимость фича-флагов. У флагов должен быть жизненный цикл: создать, активировать, мониторить, удалить. Когда фича полностью раскатана на всех пользователей, старый код и флаг должны быть удалены. Флаг выполнил свою задачу. Оставлять его — значит только увеличивать когнитивную нагрузку и расширять поверхность для багов.
Выбор подхода к управлению флагами
Для небольших команд или простых сценариев фича-флаги могут начинаться как переменные окружения или конфигурационные файлы, которые приложение читает при запуске. Это работает, но есть ограничение: изменение флага требует перезапуска или перезагрузки конфигурации.
Для более крупных команд или более сложных правил таргетинга существуют специализированные платформы управления флагами, такие как LaunchDarkly, Split или Flagr. Они предоставляют оценку флагов в реальном времени, таргетинг пользователей и журналы аудита. Эти платформы позволяют изменять флаги из панели управления и видеть эффект немедленно на всех запущенных экземплярах.
Правильный выбор зависит от вашего масштаба. Начинайте с простого. Добавляйте сложность только тогда, когда она нужна.
Практический чек-лист по использованию фича-флагов
- У каждого флага есть чёткий владелец и цель, задокументированные в доступном месте
- У флагов есть запланированная дата удаления или условие для удаления (например, «удалить, когда раскатка достигнет 100%»)
- Старые пути кода удаляются, когда флаг больше не нужен
- Изменения флагов логируются с указанием, кто, что и когда изменил
- Для критических флагов есть дашборд мониторинга, показывающий, кто видит какой вариант
- У команды есть регулярный ритм очистки устаревших флагов
Вывод
Фича-флаги дают вам слой управления, который работает независимо от вашего пайплайна деплоя. Канареечные деплои и staged rollout контролируют, какой объём трафика попадает на новую версию. Фича-флаги контролируют, какие пользователи видят какие фичи в рамках одной версии. Используйте их вместе — и вы получите гранулярный контроль над тем, как изменения доходят до ваших пользователей. Просто помните: каждый созданный вами флаг — это часть технического долга, которую нужно убрать, когда её задача выполнена.