Когда пользователи на самом деле могут использовать новую функцию?

Вы только что задеплоили новую функцию в продакшен. Команда выдохнула. Задача закрыта. Но тут начинаются вопросы: «Мы уже можем объявить об этом?», «Может, подождём маркетинг?», «А что, если тот пограничный случай, который мы упустили, проявится под реальной нагрузкой?»

Эта ситуация встречается чаще, чем готовы признать большинство команд. Код на сервере, но никто не уверен, должны ли пользователи его видеть. И эта неопределённость вскрывает пробел, о котором многие даже не подозревают — пока что-то не пойдёт не так.

Деплой — это не релиз

Вот ключевое различие, которое меняет подход к поставке софта: деплой кода и релиз функции — это разные вещи.

Деплой — это процесс размещения нового кода на сервере. Релиз — это момент, когда пользователь может увидеть и использовать эту функцию. Во многих командах эти два события происходят одновременно по умолчанию. Код на сервере — функция доступна. Но они не обязаны быть связаны.

Представьте, что вы управляете приложением, которым пользуются тысячи людей. Вы добавили более быстрый поиск. Код задеплоен на все сервера. Но после включения функция вызывает скачок нагрузки на сервер. Пользователи начинают видеть медленные страницы. Некоторые даже не могут открыть главную. Теперь у вас серьёзная проблема: нужно откатывать всё приложение целиком, хотя сломана только одна функция. Или писать фикс и проходить весь процесс деплоя заново, надеясь, что на этот раз сработает. Всё это время пользователи страдают.

Этой ситуации можно избежать, если разделить момент попадания кода на сервер и момент, когда функция становится видимой пользователям. Вы можете задеплоить код, который уже содержит новую функцию, но держать её выключенной. И только после того, как убедитесь, что всё безопасно, включить её.

Следующая диаграмма последовательности иллюстрирует, как фича-флаг создаёт безопасный разрыв между деплоем кода и его релизом для пользователей:

sequenceDiagram participant Dev as Разработчик participant CI as CI/CD participant Prod as Продакшен participant Flag as Фича-флаг participant User as Пользователь Dev->>CI: Закоммитить код CI->>Prod: Собрать и задеплоить Note over Prod: Код на сервере, флаг ВЫКЛ Prod->>Flag: Проверить флаг Flag-->>Prod: Отключён Prod->>User: Старое поведение Note over Dev,Flag: Наблюдение и тестирование Dev->>Flag: Включить флаг Flag-->>Prod: Включён Prod->>User: Новая функция видна

Фича-флаги: переключатель в вашем коде

Механизм для такого разделения называется фича-флаг (feature flag). Это условный переключатель в коде, который определяет, должна ли выполняться конкретная функция. Вы можете изменить значение этого переключателя без деплоя нового кода. Обновили конфигурацию — и функция включилась или выключилась в работающем приложении.

Вот простой пример. Вместо прямого вызова новой логики поиска мы оборачиваем её в проверку:

if feature_flags.is_enabled("fast_search"):
    results = fast_search(query)
else:
    results = old_search(query)

Когда fast_search отключён, приложение выполняет старый код. Когда вы его включаете, начинает работать новая логика. Для переключения не нужен деплой.

Это даёт вам контроль над тем, когда пользователи начнут использовать функцию. Вы можете деплоить когда угодно, не беспокоясь, что пользователи сразу увидят незаконченную работу. Вы можете сначала протестировать функцию в продакшене на ограниченной группе пользователей. Вы можете отключить проблемную функцию за секунды без отката всего приложения.

Что дают фича-флаги

Когда фича-флаги внедрены, становятся возможны несколько практичных сценариев.

Постепенный rollout. Вместо того чтобы включать функцию всем сразу, вы можете активировать её для 1% пользователей, затем для 10%, потом для 50%. Если что-то пошло не так на 10%, вы заметите это до того, как проблема затронет всех. Это особенно полезно для функций, которые сложно тестировать в стейджинге, так как они зависят от реальных паттернов трафика или объёма данных.

Canary-релизы. Вы можете направить небольшую группу пользователей на новую функцию, пока все остальные работают по-старому. Мониторите частоту ошибок, задержки и поведение пользователей. Если в canary-группе проблем нет — расширяйте rollout. Если появились проблемы — выключайте мгновенно.

Аварийный выключатель (kill switch). Когда функция вызывает неожиданные проблемы в продакшене, вы можете отключить её мгновенно. Не нужно откатывать коммиты, пересобирать и передеплоивать. Просто выключите флаг. Это снижает давление на команду, потому что вы знаете, что всегда можете быстро выдернуть штепсель.

Тестирование в продакшене. Звучит рискованно, но на самом деле это безопаснее, чем альтернатива. С фича-флагами вы можете сначала включить функцию для внутренних пользователей или бета-тестеров. Они работают с ней на реальных данных и в реальных сценариях. Вы находите проблемы до того, как функция дойдёт до широкой аудитории.

Скоординированные релизы. Иногда функцию нужно включить в определённое время по бизнес-причинам. Например, у маркетинга запланирована кампания. Или функция привязана к регуляторному дедлайну. С фича-флагами вы можете задеплоить код за дни или недели до срока и включить его ровно в тот момент, когда это необходимо.

Цена фича-флагов

Фича-флаги не бесплатны. Они добавляют сложности в кодовую базу. Каждый флаг вводит условную ветку, которую нужно тестировать. Если флаги накапливаются, код становится сложнее читать и поддерживать. Старые флаги, которые всегда включены или всегда выключены, создают мёртвые ветки кода, сбивающие с толку будущих разработчиков.

Есть и операционные издержки. Вам нужен способ управлять конфигурациями флагов в разных окружениях. Нужно решить, на каком уровне оцениваются флаги: на уровне сервера, пользователя или с другой гранулярностью. Нужно обеспечить быстрое и надёжное распространение изменений флагов.

Самая распространённая ошибка команд — относиться к фича-флагам как к чему-то постоянному. У флага должен быть чёткий жизненный цикл: он вводится для конкретной цели, остаётся активным, пока функция внедряется, и удаляется, когда функция полностью выпущена и стабильна. Флаги, которые остаются в коде навсегда, превращаются в технический долг.

Практический чеклист по использованию фича-флагов

Если вы решили внедрить фича-флаги, вот краткий чеклист, чтобы держать всё под контролем:

  • Начните с простой реализации. Для небольших команд достаточно JSON-конфига или переменной окружения. Не усложняйте систему флагов до того, как поймёте свои потребности.
  • Называйте флаги понятно. Используйте имена, описывающие функцию, а не детали реализации. fast_search лучше, чем search_v2_algorithm.
  • Удаляйте флаги после стабилизации функции. Поставьте напоминание или создайте задачу на очистку кода флага после завершения rollout.
  • Тестируйте оба состояния флага. Убедитесь, что тесты покрывают поведение как при включённом, так и при выключенном флаге. Флаг, который ломает старый код, хуже, чем отсутствие флага.
  • Логируйте проверки флагов. При отладке проблем в продакшене нужно знать, какие флаги были активны для каких пользователей и в какое время.
  • Ограничивайте количество активных флагов. Слишком много флагов усложняет анализ кода. Если у вас одновременно активно несколько десятков флагов, подумайте, не используете ли вы флаги для того, что должно быть постоянной конфигурацией.

Вывод

В следующий раз, когда ваша команда закончит работу над функцией, задайте вопрос: «Код задеплоен или функция выпущена?» Если по умолчанию ответ один и тот же, вы упускаете возможность поставлять безопаснее и с меньшим стрессом. Фича-флаги дают вам возможность разделить эти два события. Они позволяют деплоить по вашему графику и выпускать, когда вы готовы. Начните с одного флага для вашей следующей рискованной функции. Посмотрите, как это изменит подход команды к поставке.