Feature Flags и выпуск фич
Feature Flag (FF) — это управляемое условие, которое включает/выключает поведение системы без релиза кода. Флаги позволяют: выкатывать фичи безопасно, таргетировать группы пользователей/рынков/тенантов, быстро отключать проблемные компоненты, проводить эксперименты и конфигурировать параметры в рантайме.
Ключевые цели:- Снизить blast radius при релизах.
- Разделить развертывание и активацию.
- Дать прозрачное управление изменениями с аудитом, SLO и откатом «в один клик».
1) Типы флагов и когда их применять
Release flags — поэтапное включение новой фичи (dark → canary → ramp-up → 100%).
Ops/kill-switch — мгновенное отключение зависимостей (провайдер, подсистема, тяжелые вычисления).
Experiment (A/B, multi-variant) — разделение трафика на варианты (weights, sticky bucketing).
Permission/Entitlement — доступ к фичам по ролям/планам/юрисдикциям.
Remote Config — параметры поведения (порог, таймаут, формула) из флага/конфига.
Migration flags — переключения схем/путей данных (переезд на новый индекс/БД/эндпоинт).
Анти-паттерн: один и тот же флаг «про все» — дробите на фичу, комп-свитч и параметры.
2) Модель данных флага (минимум)
yaml flag:
key: "catalog. new_ranker"
type: "release" # release ops kill experiment permission config migration description: "New Directory Ranking"
owner: "search-team@company"
created_at: "2025-10-01T10:00:00Z"
ttl: "2026-01-31" # delete deadline after 100% enable rules:
- when:
tenant_id: ["brand_eu","brand_latam"]
region: ["EE","BR"]
user_pct: 10 # progressive percentage then: "on"
- when:
kyc_tier: ["unverified"]
then: "off"
variants: # for experiments
- name: "control"; weight: 50
- name: "v1"; weight: 30
- name: "v2"; weight: 20 payload:
v1:
boost_freshness: 0. 3 boost_jackpot: 0. 2 v2:
boost_freshness: 0. 2 boost_jackpot: 0. 4 prerequisites: # dependent flags/schema versions
- key: "catalog. index_v2_ready"
must_be: "on"
audit:
require_ticket: true change_window: "09:00-19:00 Europe/Kyiv"
safeguards:
max_rollout_pct: 50 # stop threshold auto_rollback_on:
p95_ms: ">200"
error_rate: ">2%"
3) Оценка и таргетинг (evaluation)
Ключи таргетинга: `tenant_id, region/licence, currency, channel, locale, role, plan, device, user_id, cohort, kyc_tier, experiment_bucket`.
Порядок оценки: prerequisites → deny-правила → allow-правила → дефолт.
Sticky bucketing: для экспериментов хэшируйте стабильный идентификатор (например, `hash(user_id, flag_key)`) — чтобы пользователь всегда получал один вариант.
ts result = evaluate(flag, context) // pure function if (!prereqs_ok(result)) return OFF if (deny_match(result, ctx)) return OFF if (allow_match(result, ctx)) return resolve_variant_or_on(result, ctx)
return flag. default
4) Распределение и архитектура FF
Варианты:- Server-side SDK (рекомендовано): источники истинности и кэш в бэкенде; унификация логики.
- Edge/CDN evaluation: быстрый таргетинг на периметре (где нет PII/секретов).
- Client-side SDK: когда нужна персонализация UI, но — только с минимальным контекстом и без чувствительных правил.
- Config-as-Code: хранение флагов в репозитории, валидация CI, rollout через CD.
- Startup bootstrap + streaming updates (SSE/gRPC) + fallback к последнему снапшоту.
- SLA «freshness» флагов: p95 ≤ 5 с.
5) Стратегии выпуска
5.1 Dark Launch
Фича включена, но невидима пользователю; собираем метрики и ошибки.
5.2 Canary
Включаем 1–5% трафика в одной юрисдикции/тенанте; мониторим p95/p99, ошибки, конверсию.
Stop conditions — пороговые триггеры автокатофа по метрикам.
5.3 Progressive Rollout
10% → 25% → 50% → 100% по расписанию с ручной/авто-верификацией.
5.4 Shadow / Mirroring
Дублируем запросы в новый путь (без видимого эффекта) и сравниваем результаты/латентность.
5.5 Blue/Green + FF
Разворачиваем две версии; флаг рулит трафиком и переключает зависимости по сегментам.
6) Зависимости и кросс-сервисная консистентность
Используйте prerequisites и «health-флаги» готовности: индекс построен, миграция завершена.
Координация через события: `FlagChanged(flag_key, scope, new_state)`.
1. включить read-путь → 2) проверить метрики → 3) включить write/side-effects.
- Сервисные контракты: дефолт должен быть безопасным (fail-safe OFF).
7) Наблюдаемость и SLO
Метрики на флаг/вариант/сегмент:- `flag_eval_p95_ms`, `errors_rate`, `config_freshness_ms`.
- Бизнес-метрики: `ctr`, `conversion`, `ARPU`, `retention`, guardrails (например, RG-инциденты).
- Автоматические SLO-пороги для автокатофа.
Логи/трейсинг: добавляйте `flag_key`, `variant`, `decision_source` (server/edge/client), `context_hash`.
Дашборды: «лестница» rollout с порогами, heatmap ошибок по сегментам.
8) Безопасность и комплаенс
PII-минимизация в контексте.
RLS/ACL: кто может менять какие флаги (по доменам/рынкам).
Часовые окна изменений (change windows) и «двойное подтверждение» для чувствительных флагов.
Неизменяемый аудит: кто/когда/что/почему (ticket/incident link).
Юрисдикции: флаги не должны обходить регуляторные запреты (например, включать игру в запрещенной стране).
9) Управление «долгоживущими» флагами
У каждого флага есть TTL/дата удаления.
После 100% включения — создайте task на удаление веток кода, иначе вырастет «флаг-долг».
Отмечайте флаги как `migration`/`one-time`, отделяйте их от постоянных `permission/config`.
10) Пример API/SDK контракта
Evaluation API (server-side)
http
POST /v1/flags/evaluate
Headers: X-Tenant: brand_eu
Body: { "keys":["catalog. new_ranker","rgs. killswitch"], "context": { "user_id":"u42", "region":"EE" } }
→ 200
{
"catalog. new_ranker": { "on": true, "variant":"v1", "as_of":"2025-10-31T12:10:02Z" },
"rgs. killswitch": { "on": false, "variant":null, "as_of":"2025-10-31T12:10:02Z" }
}
Client SDK (кэш, fallback)
ts const ff = await sdk. getSnapshot() // bootstrap const on = ff. isOn("catalog. new_ranker", ctx)
const payload = ff. payload("catalog. new_ranker", "v1")
11) Взаимодействие с другими контурами
Rate limits/квоты: флаги могут понижать RPS/включать троттлинг на время инцидента.
Circuit breaker/degradation: kill-switchи отключают тяжелые пути и включают деградацию.
Каталог/персонализация: флаги меняют веса/правила ранжирования (через Remote Config).
Миграции БД: флаги поэтапно переводят чтения/записи на новую схему (read-replica → dual-write → write-primary).
12) Плейбуки (runbooks)
1. Инцидент после включения 25%
Автокатоф сработал → флаг OFF для всех/сегмента, тикет в on-call, сбор статов, RCA.
Временно включить деградацию/старую ветку через migration-флаг.
2. Рост p95 по каталогу
Порог `p95_ms>200` — автокатоф; зафиксировать снапшот логов с `flag_key=catalog.new_ranker`.
Включить упрощенные сигналы ранжирования (payload config).
3. Несоответствие юрисдикции
Флаг permission ошибочно открыл игру в `NL` — OFF + пост-факт аудит, добавление guard-правила «region deny».
4. Дисперсия в A/B
Остановить эксперимент, выполнить CUPED/stratified анализ, перераскатать с обновленными весами.
13) Тестирование
Unit: детерминированная оценка правил/приоритетов/пререквизитов.
Contract: схема флагов (JSON/YAML), валидаторы, CI-проверка перед мерджем.
Property-based: «deny > allow», «most specific wins», стабильный bucketing.
Replay: воспроизведение реальных контекстов на новой конфигурации.
E2E: канареечные скрипты (step-up/step-down), проверка автокатофа и аудит-событий.
Chaos: обрыв стриминга, устаревший снапшот, массовое обновление флагов.
14) Типичные ошибки
Секретная логика в клиентских флагах (утечки/подмена).
Отсутствие TTL → «кладбище» флагов в коде.
«Универсальные» флаги без сегментации → невозможно локализовать проблему.
Нет guardrails/автокатофов — ручные инциденты.
Несовместимые зависимости между флагами → циклы/рассинхрон.
Оценка флагов в каждом запросе без кэша → всплески латентности.
Отсутствие аудита/окна изменений — риски комплаенса.
15) Чек-лист перед продом
- Флаг создан с типом, owner, описанием, TTL и требованием тикета.
- Правила таргетинга определены; `deny` на нежелательные регионы/роли.
- Sticky bucketing детерминирован; идентификатор выбран стабильно.
- Пререквизиты и health-флаги готовы; дефолт безопасный.
- Дашборды и алерты на p95/p99, error_rate, бизнес-guardrails.
- Автокатоф настроен; стоп-порог rollout и условия отката.
- Канареечный план: проценты/этапы/окно изменений/ответственные.
- Конфиги валидируются в CI; снапшот распределен по кластерам/регионам.
- Документация для поддержки/продукта; плейбуки инцидентов.
- План удаления веток кода и самого флага после 100%.
16) Пример «миграционного» флага (БД/индекс)
yaml flag:
key: "search. use_index_v2"
type: "migration"
description: "Switching reads to index v2"
prerequisites:
- key: "search. index_v2_built"
must_be: "on"
rules:
- when: { tenant_id: ["brand_eu"], user_pct: 5 } then: "on"
- when: { tenant_id: ["brand_eu"], user_pct: 25 } then: "on"
safeguards:
auto_rollback_on:
search_p95_ms: ">180"
error_rate: ">1%"
ttl: "2026-02-01"
Заключение
Feature Flags — это не только «вкл/выкл», а дисциплина управления рисками изменений. Четкие типы флагов, детерминированный таргетинг, прогрессивные выкладки с guardrails, автокатоф, аудит и план удаления делают релизы предсказуемыми, а инциденты — краткими и контролируемыми. Встраивайте флаги в архитектуру как первый класс граждан — и вы сможете доставлять ценность чаще, безопаснее и осмысленнее.