Короткий ответ
Нужно разделить флаги по времени жизни (временные флаги для постепенного релиза с обязательным планом удаления после полного раскатывания, и более долгоживущие операционные переключатели) и явно определить, где проверяется флаг — ближе к middleware/входу запроса для крупных изменений поведения или глубже в бизнес-логике для точечных веток, а также кто и как может менять значение флага в production (аудит изменений).
Как это работает подробнее
Feature flag, проверяемый в middleware/на входе запроса, удобен для изменений, затрагивающих весь путь обработки запроса (например, включение нового алгоритма маршрутизации целиком), но менее удобен для точечных изменений внутри бизнес-логики, где естественнее проверять флаг прямо в нужном месте кода. Несогласованность флага между несколькими сервисами (один сервис уже включил новое поведение, другой ещё нет) может создавать скрытые баги на границах интеграции.
Проверка флага на уровне middleware проще для крупных переключений поведения всего запроса, но менее гибка для точечных изменений в глубине бизнес-логики; проверка глубоко в коде даёт точность, но увеличивает число мест, которые нужно отследить и в итоге убрать после полного релиза. Хранение состояния флагов в быстро обновляемом внешнем сервисе (LaunchDarkly и подобные) даёт гибкость и аудит, но добавляет внешнюю зависимость и риск, что её недоступность повлияет на доступность самого API. Критерий выбора — масштаб изменения (всё поведение запроса против точечной ветки), частота изменения значения флага и готовность команды дисциплинированно убирать флаги после релиза.
Пример кода
# Пример с явным владельцем и плановой датой удаления флага (документация в коде)
FEATURE_FLAGS = {
# owner: "team-payments", remove_after: "2026-01-01", rollout: 25%
"new_pricing_engine": True,
}
@app.get("/price")
def get_price(user_id: int):
if is_flag_enabled("new_pricing_engine", user_id):
return new_pricing_engine(user_id)
return legacy_pricing_engine(user_id)
# Ревизия флагов раз в квартал: удалить "new_pricing_engine" и legacy_pricing_engine,
# если rollout давно достиг 100% и флаг больше не меняется.Что использовать на практике
- Практика. Важно вести реестр активных флагов с владельцем и плановой датой удаления, периодически проводить ревизию и удалять флаги для функциональности, полностью раскатанной на 100% пользователей — иначе кодовая база накапливает условные ветки, которые никто не удаляет, и тестовая матрица комбинаций флагов растёт неконтролируемо. Для флагов, влияющих на целостность данных (а не только UI/поведение), нужно особенно тщательно продумать, что происходит при переключении флага туда-обратно для одного и того же пользователя.
Подводные камни
- Риск. Используют feature flags как постоянный механизм конфигурации «навсегда» без плана удаления, из-за чего кодовая база через год содержит десятки устаревших условных веток; не учитывают, что один и тот же пользователь может видеть противоречивое поведение при переключении флага между запросами (например, если флаг читается из конфигурации, которая меняется посреди пользовательской сессии).