Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Feature flags: постепенное включение фич

Feature flags: раскатка, таргетинг и уборка флагов

Как управлять включением фич: виды флагов (release/ops/experiment), gradual rollout по процентам, kill switch для инцидентов и борьба с долгоживущим легаси флагов.

Прямой ответ

Feature flag — ветвление поведения по переменной конфигурации вместо ветвления кода: деплой отделён от релиза (код на проде выключен — включение отдельным решением, мгновенно и без деплоя). Четыре вида флагов с разным временем жизни и требованиями: release (незавершённая фича прячется до готовности — дни/недели), ops/kill-switch (аварийное отключение деградировавшей фичи — живёт годами, обязан быть быстрым и надёжным), experiment (A/B-тесты с распределением по пользователям — недели, с аналитикой), permission (доступ по тарифам/ролям — постоянные, это уже продукт). Ключевая практика — gradual rollout: включение по процентам (1% → 10% → 50% → 100%) с консистентным распределением (hash user_id, не random — пользователь не должен мигать между версиями) и наблюдением метрик между ступенями (SLO-логика: ошибка на 1% не катастрофа, на 100% — инцидент). И дисциплина, без которой флаги превращаются в легаси: срок жизни и уборка — у release-флага есть дата удаления из кода (фича раскатана — ветка else удаляется задачей в трекере; флаги-долгожители множат комбинации состояний, которые никто не тестирует — «flag debt» стоит дороже, чем фича без флага).

Механика: где жить флагу

Минимальная реализация — таблица/конфиг «флаг → правило» + функция проверки в коде (if flags.enabled("new_checkout", user)): правила по убыванию сложности — булево вкл/выкл, белый список (внутренние пользователи видят фичу раньше всех — dogfooding), процент от консистентного хеша (crc32(user_id + flag_name) % 100 < N — детерминированно: тот же пользователь всегда в той же группе), таргетинг по атрибутам (страна, тариф, версия приложения), принудительное значение для конкретного пользователя (поддержка включает клиенту для отладки). Хранение: в БД/сервисе флагов, не в коде и не только в env (смена env = деплой/рестарт — убивает смысл «мгновенного переключения»; env-флаги годятся для статических permission-флагов). Готовые системы (Unleash, GrowthBook, Flagsmith — self-hosted; LaunchDarkly — SaaS) добавляют: UI управления, аудит изменений (кто включил — расследование инцидентов начинается с этого), SDK с локальным кешем (проверка флага не ходит в сеть на каждый запрос — кеш-паттерны), и fallback при недоступности сервиса флагов (дефолтные значения в коде — сервис флагов лежит → продукт работает на безопасных дефолтах, не падает).

Gradual rollout и kill switch в связке

Раскатка — процесс, а не событие: (1) флаг выключен, код задеплоен (мёртвый код под флагом на проде — норма: он не исполняется); (2) включение команде/белому списку — dogfooding ловит стыдное до пользователей; (3) 1% пользователей — сутки наблюдения: ошибки фичи (отдельная метрика с тегом флага — метрики), конверсия/латентность против контрольной группы; (4) 10% → 50% → 100% с паузами (каждая ступень — решение по данным, не по расписанию); (5) раскатано — флаг в очередь на удаление. Kill switch — обратный ход тем же рычагом: новый чекаут на 50% показал рост ошибок оплат — выключение за секунды без деплоя/отката (в сравнении со стратегиями деплоя: canary ловит на этапе деплоя, флаг — в любой момент жизни фичи, включая «через две недели после раскатки проявился краевой случай»). Требование к kill switch — независимость от сломанного: флаг-сервис доступен при лежащем приложении (или локальный кеш с дефолтом «выключено» для опасных фич), переключение проверено тренировкой (кнопка, которую никогда не нажимали, в инцидент не работает).

Долг флагов: почему их надо убирать

Каждый активный флаг удваивает число состояний системы: две ветки кода, которые надо поддерживать; тесты обеих комбинаций (в реальности тестируется включённая — выключенная гниёт молча); чтение кода с if flag трёхлетней давности (почему? для кого? можно ли удалить? — археология). Правила уборки: release-флаги — удаление в том же спринте, что раскатка 100% (задача «убрать флаг X» создаётся в момент создания флага — не «потом»); experiment-флаги — удаляются с подведением итога эксперимента (оба исхода — решение: раскатать или удалить фичу; «эксперимент идёт третий год» — это не эксперимент); ops-флаги — остаются, но ревизуются ежеквартально (флаг деградации старого платёжного шлюза после его отключения — мёртвый код, удалить); permission — не флаги в смысле долга (продуктовая логика). Техническая помощь: линтеры/инструменты инвентаризации (piranha-подобные — находят мёртвые ветки выключенных флагов), CI-чек «флаги старше N дней — список на разбор», и культура: флаг — временная конструкция, «пока не решим», а не способ вечно не решать (нераскатанная полгода фича — вопрос к продукту, не к коду).

Практический сценарий: новая корзина с раскаткой и откатом

Фича: переписанная корзина (риск высокий — деньги). План: release-флаг checkout_v2 в GrowthBook (self-hosted — данные пользователей не уходят в SaaS); код мержится серией PR под флагом (мелкие диффы ревьюабельны — ветка-долгожитель не копится); dogfooding на команде — неделя (нашли баг промокодов); 5% — трое суток: метрика ошибок чекаута с тегом флага (дашборд) — на v2 конверсия равна, ошибок нет; 25% — обнаружен краевой случай: корпоративные аккаунты с отсрочкой платежа теряют условия (тарифный атрибут не учтён в новой логике) — kill switch: 25% → 0% за 20 секунд, фикс день, повторная раскатка с 5%; 100% через две недели; задача удаления флага закрыта через неделю после (else-ветка −400 строк — старый код корзины удалён, археология не нужна). Разбор сценария: флаг окупился дважды — откат без деплоя в момент, когда каждая минута с деньгами стоит, и спокойная пошаговая раскатка вместо «выкатили в пятницу всем».

FAQ

Флаги против canary/blue-green — дублирование? Разные слои: canary/blue-green — про деплой (какая версия кода обслуживает трафик), флаги — про поведение внутри версии (какая логика активна); канареечный деплой + флаги внутри — нормальная комбинация (деплой новой версии тихо, включение фич постепенно); флаги не заменяют стратегии деплоя при breaking-изменениях схемы БД (expand/contract).

Как тестировать код с флагами? Юниты — обе ветки (параметризация значения флага в тестах — дешёво); интеграция — включённая ветка как основная + смоук выключенной (старое поведение не сломано); E2E-матрица всех комбинаций флагов — нереалистично, отсюда и долг: больше 3–5 активных release-флагов одновременно — сигнал притормозить фиче-поток.

Кто должен иметь право переключать флаги? Продукт-команда фичи + дежурный (kill switch в инциденте — без поиска «кто тут владеет флагом»); обязательны аудит-лог изменений и описание у флага (что делает, кто владелец, до какого числа живёт) — флаг без владельца — будущий инцидент или вечное легаси.

Нужны ли флаги маленькому проекту/пет-продукту? Минимально: один-два kill-switch для рискованного (платежи, интеграции) и permission-логика как есть; полноценная флаг-платформа — от команды и продукта, где раскатки регулярны; но даже простейшая таблица флагов в БД окупается первым же инцидентом с мгновенным отключением.

Источники: Martin Fowler (Feature Toggles), документация Unleash/GrowthBook — ссылки в шапке.