Флаги функций: постепенный rollout и мгновенное отключение

Чек-лист безопасности релиза и реестр изменений разбирают процесс вокруг выкладки. Флаг функции — конкретный технический механизм, который разделяет два разных действия: деплой кода (когда новый код физически появляется на серверах) и включение функции (когда пользователи реально начинают её видеть). Разделение даёт возможность откатить функцию мгновенно без повторного деплоя, если включённая функция оказалась проблемой.
Статья опирается на логику направления «Управление изменениями» из методики зрелости Группы Сбер (источник); это методика для крупных компаний, мы используем её как ориентир и пересказываем своими словами. Проверка выполнена на тестовом скрипте, код приведён полностью.
Что должно получиться
| Уровень | Что это значит на практике |
|---|---|
| 2. Гигиенический | Рискованные изменения включаются постепенно и могут быть отключены немедленно без нового релиза, а не раскатываются сразу на всех пользователей одним деплоем |
Критерии сформулированы нами по мотивам методики, это ориентир, а не оценка соответствия.
Скрипт
#!/usr/bin/env python3
"""Простой флаг релиза с постепенным раскатыванием по процентам и мгновенным отключением (kill switch).
Без внешнего сервиса: один JSON-файл с конфигурацией флагов, читается при каждом запросе."""
import hashlib
import json
def load_flags(path="flags.json"):
with open(path) as f:
return json.load(f)
def is_enabled(flag_name, user_id, flags=None, flags_path="flags.json"):
flags = flags or load_flags(flags_path)
flag = flags.get(flag_name)
if flag is None:
return False
if not flag.get("enabled", False):
return False # kill switch: мгновенно отключает всем, независимо от процента
percent = flag.get("rollout_percent", 0)
if percent >= 100:
return True
# стабильный хэш пользователя и имени флага: один и тот же пользователь
# либо всегда внутри раскатки, либо всегда снаружи, пока процент не меняется
h = int(hashlib.sha256(f"{flag_name}:{user_id}".encode()).hexdigest(), 16)
return (h % 100) < percent
Ключевая деталь — хэш берётся от пары «имя флага + идентификатор пользователя», а не случайное число при каждом запросе. Это даёт стабильность: один пользователь либо постоянно видит новую функцию, либо постоянно не видит, пока вы сами не измените процент. Без этого пользователь видел бы разное поведение от запроса к запросу, что ломает и тестирование, и доверие к продукту.
Результат проверки
| Проверка | Результат |
|---|---|
| Раскатка на 20% из 10 000 условных пользователей | Включено для 1985 пользователей — около 20%, как и задано |
| Повторные вызовы для одного пользователя при неизменном проценте | Три вызова подряд дали одинаковый результат (True, True, True для пользователя, попавшего в раскатку) |
Снижение процента раскатки до 0 без изменения enabled |
Пользователь, ранее видевший функцию, перестал её видеть |
enabled: false при rollout_percent: 100 |
Функция выключена для всех — kill switch имеет приоритет над процентом |
Последняя строка — самое важное свойство для реагирования на инцидент: в панике после обнаружения проблемы достаточно поменять одно значение (enabled) на false, не вычисляя, какой процент сейчас активен и как его аккуратно снизить.
Порядок внедрения
- Заводите флаг для любого изменения с заметным риском — новая логика оплаты, смена алгоритма, изменение, затрагивающее много пользователей одновременно.
- Начинайте с малого процента (1–5%) и повышайте постепенно, наблюдая за метриками между шагами.
- Отдельно документируйте, кто может переключить флаг и где хранится текущее состояние — то же требование, что для любой карточки изменения.
- Проверяйте kill switch до того, как он понадобится: тестовое отключение на стейджинге должно работать мгновенно, без деплоя.
- Удаляйте флаг после полного раскатывания (100% и стабильно) — забытые флаги накапливаются и усложняют код сильнее, чем решают проблему.
Диагностика
| Симптом | Причина и что делать |
|---|---|
| Пользователь видит функцию то включённой, то выключенной | Хэш не стабилен (например, используется случайное число вместо хэша от идентификатора). Проверьте, что ключ хэширования включает постоянный идентификатор пользователя |
| Kill switch не сработал мгновенно | Конфигурация флагов кэшируется приложением надолго. Уменьшите время кэширования или добавьте принудительную инвалидацию при изменении флага |
| Процент раскатки показывает не то число, что задано | На малых выборках процент будет отличаться от точного значения статистически — это нормально, не ошибка хэш-функции. Проверяйте на большой выборке, как в примере |
| В коде десятки забытых флагов на 100% | Нет процесса уборки после полного раскатывания. Включите удаление отработавших флагов в чек-лист закрытия задачи |
Ограничения
Файл с флагами в примере читается с диска при каждом вызове — для простого случая и малой нагрузки этого достаточно, но для распределённой системы с десятками серверов нужен общий источник конфигурации (база данных, централизованный сервис флагов), чтобы изменение состояния применялось одновременно везде, а не с задержкой на каждом сервере отдельно.
Где это в модели зрелости
Материал относится к направлению «Управление изменениями» (материалы направления, карта зрелости). Уровень 2 требует, чтобы риск изменения был управляемым, а откат — быстрым: флаг функции разделяет деплой и включение, сокращая время отката с «новый релиз» до «один переключатель».
Как проверить
- Рискованные изменения проходят через флаг с постепенным раскатыванием, а не включаются сразу на 100%.
- Kill switch проверен тестовым отключением на стейджинге и работает без нового деплоя.
- Распределение по процентам стабильно для каждого пользователя, а не меняется от запроса к запросу.
- Полностью раскатанные флаги удаляются из кода по чек-листу закрытия задачи.
Источники
- Методика оценки уровня зрелости кибербезопасности компании, Группа Сбер: sberbank.ru.


















