Flagship: Молниеносные feature-флаги для AI-разработки на базе Cloudflare

ИИ пишет больше кода, чем когда-либо. Теперь на долю вкладов, созданных с помощью ИИ, приходится быстро растущая часть нового кода на всей платформе. Инструменты агентного написания кода, такие как OpenCode и Claude Code, выпускают целые функции за считанные минуты.

Внедрение кода, созданного ИИ, в продакшн будет только ускоряться. Но более значительный сдвиг не только в скорости — он в автономности.

Сегодня агент ИИ пишет код, а человек проверяет, объединяет и развертывает его. Завтра агент будет делать всё это сам. Возникает вопрос: как позволить агенту выпускать в продакшн, не убирая все страховочные сети?

Функциональные флаги — это ответ. Агент пишет новый путь выполнения кода за флагом и развертывает его — флаг выключен, поэтому для пользователей ничего не меняется. Затем агент включает флаг для себя или небольшой тестовой когорты, проверяет функцию в рабочей среде и наблюдает за результатами. Если метрики выглядят хорошо, он расширяет развертывание. Если что-то ломается, он отключает флаг. Человеку не нужно участвовать на каждом шаге — он задает границы, а флаг контролирует радиус поражения.

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

Сегодня мы анонсируем Flagship — встроенный сервис функциональных флагов Cloudflare, построенный на базе OpenFeature, открытого стандарта CNCF для оценки функциональных флагов. Он работает везде — Workers, Node.js, Bun, Deno и в браузере — но быстрее всего на Workers, где флаги оцениваются внутри сети Cloudflare. С привязкой Flagship и OpenFeature интеграция выглядит так:

await OpenFeature.setProviderAndWait(
    new FlagshipServerProvider({ binding: env.FLAGS })
);

Flagship теперь доступен в закрытом бета-режиме.

Проблема с функциональными флагами на Workers

Многие разработчики Cloudflare прибегали к прагматичному обходному пути: жестко прописывали логику флагов прямо в своих Workers. И, честно говоря, в начале это работает достаточно хорошо. Workers развертываются за секунды, поэтому переключение булевого значения в коде и отправка его в продакшн достаточно быстры для большинства ситуаций.

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

Сетевой вызов к внешним сервисам

Другая распространенная модель, используемая на workers, — выполнение HTTP-запроса к внешнему сервису следующим образом:

const response = await fetch("https://flags.example-service.com/v1/evaluate", {
      ...
      body: JSON.stringify({
        flagKey: "new-checkout-flow",
        context: {
          ...
        },
      }),
    });
const { value } = await response.json();
if (value === true) {
    return handleNewCheckout(request);
}
return handleLegacyCheckout(request);

Этот исходящий запрос находится на критическом пути каждого пользовательского запроса. Он может добавить значительную задержку в зависимости от того, насколько далеко пользователь находится от региона сервиса флагов.

Это странная ситуация. Ваше приложение работает на границе сети, в миллисекундах от пользователя. Но проверка функционального флага заставляет его снова обращаться через Интернет к другому API, прежде чем он решит, что отображать.

Почему локальная оценка не решает проблему

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

На Workers ни одно из этих предположений не выполняется. Здесь нет долгоживущего процесса: изолят Worker может быть создан, обработать запрос и быть удален между одним запросом и следующим. Новый вызов может означать повторную инициализацию SDK с нуля.

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

Cloudflare KV — отличная примитивная система для этого!

Как работает Flagship

Flagship полностью построен на инфраструктуре Cloudflare — Workers, Durable Objects и KV. Нет внешних баз данных, сторонних сервисов и централизованных серверов-источников на пути оценки.

Когда вы создаете или обновляете флаг, плоскость управления атомарно записывает изменение в Durable Object — глобально уникальный экземпляр на основе SQLite, который служит источником истины для конфигурации флагов и журнала изменений этого приложения. В течение секунд обновленная конфигурация флага синхронизируется с Workers KV, глобально распределенным хранилищем ключ-значение Cloudflare, где она реплицируется по всей сети Cloudflare.

Когда запрос оценивает флаг, Flagship считывает конфигурацию флага прямо из KV на границе сети — из того же местоположения Cloudflare, которое уже обрабатывает запрос. Затем механизм оценки запускается прямо там, в изоляте: он сопоставляет контекст запроса с правилами таргетинга флага, определяет процент развертывания и возвращает вариант. И данные, и логика находятся на границе сети — ничего не отправляется в другое место для оценки.

Introducing Flagship: feature flags built for the age of AI

Использование Flagship: привязка для Worker

Для команд, использующих Cloudflare Workers, Flagship предлагает прямую привязку, которая оценивает флаги внутри среды выполнения Workers — без HTTP-запросов туда-обратно, без накладных расходов на SDK. Добавьте привязку в ваш wrangler.jsonc, и ваш Worker будет подключен:

{
  "flagship": [
    {
      "binding": "FLAGS",
      "app_id": "<APP_ID>"
    }
  ]
}

И всё. Ваш идентификатор учетной записи определяется из вашей учетной записи Cloudflare, а app_id привязывает привязку к конкретному приложению Flagship. В вашем Worker вы просто запрашиваете значение флага:

export default {
  async fetch(request: Request, env: Env) {
    // Простая проверка булевого значения
    const showNewUI = await env.FLAGS.getBooleanValue('new-ui', false, {
      userId: 'user-42',
      plan: 'enterprise',
    });
    // Полная информация об оценке, когда она вам нужна
    const details = await env.FLAGS.getStringDetails('checkout-flow', 'v1', {
      userId: 'user-42',
    });
    // details.value = "v2", details.variant = "new", details.reason = "TARGETING_MATCH"
  },
};

Привязка поддерживает типизированные аксессоры для каждого типа варианта - getBooleanValue(), getStringValue(), getNumberValue(), getObjectValue() - плюс варианты *Details(), которые возвращают вычисленное значение вместе с подобранным вариантом и причиной его выбора. При ошибках оценки значение по умолчанию возвращается корректно. При несоответствии типов привязка выдает исключение — это ошибка в вашем коде, а не временный сбой.

SDK: нативный для OpenFeature

Большинство SDK для функциональных флагов поставляются со своими собственными интерфейсами и моделями оценки. Со временем они глубоко внедряются в вашу кодовую базу — и смена провайдера означает переписывание каждого места вызова.

Мы не хотели создавать еще один такой. Flagship построен на OpenFeature, открытом стандарте CNCF для оценки функциональных флагов. OpenFeature определяет общий интерфейс для оценки флагов в разных языках и у разных провайдеров — это такие же отношения, какие OpenTelemetry имеет к наблюдаемости. Вы пишете код оценки один раз в соответствии со стандартом и меняете провайдеров, изменив одну строку конфигурации.

import { OpenFeature } from '@openfeature/server-sdk';
import { FlagshipServerProvider } from '@cloudflare/flagship/server';
await OpenFeature.setProviderAndWait(
  new FlagshipServerProvider({
    appId: 'your-app-id',
    accountId: 'your-account-id',
    authToken: 'your-cloudflare-api-token',
  })
);
const client = OpenFeature.getClient();
const showNewCheckout = await client.getBooleanValue(
  'new-checkout-flow',
  false,
  {
    targetingKey: 'user-42',
    plan: 'enterprise',
    country: 'US',
  }
);

Если вы работаете на Workers с привязкой Flagship, вы можете передать её напрямую провайдеру OpenFeature. Привязка уже несет контекст вашей учетной записи, поэтому нечего настраивать — аутентификация неявная.

import { OpenFeature } from '@openfeature/server-sdk';
import { FlagshipProvider } from '@cloudflare/flagship/server';
let initialized = false;
export default {
  async fetch(request: Request, env: Env) {
    if (!initialized) {
      await OpenFeature.setProviderAndWait(
        new FlagshipServerProvider({ binding: env.FLAGS })
      );
      initialized = true;
    }
    const client = OpenFeature.getClient();
    const showNewCheckout = await client.getBooleanValue('new-checkout-flow', false, {
      targetingKey: 'user-42',
      plan: 'enterprise',
    });
  },
};

Ваш код для оценки не меняется — интерфейс OpenFeature идентичен. Но под капотом Flagship оценивает флаги через привязку (binding), а не по HTTP. Вы получаете переносимость стандарта и производительность привязки.

Также доступен провайдер для клиентской части (браузера). Он предварительно загружает указанные вами флаги, кэширует их с настраиваемым TTL и синхронно обслуживает оценки из этого кэша.

Что можно делать с Flagship

Flagship поддерживает шаблоны, которые вы ожидаете от сервиса feature-флагов, и те, которые становятся критичными, когда AI-сгенерированный код ежедневно попадает в продакшен.

Значения флагов могут быть логическими, строковыми, числовыми или полноценными JSON-объектами — это полезно для блоков конфигурации, определений тем оформления UI или маршрутизации пользователей к разным версиям API без поддержки отдельных веток кода.

Правила таргетирования (Targeting Rules)

Каждый флаг может иметь несколько правил, которые оцениваются в порядке приоритета. Первое совпавшее правило побеждает.

Правило состоит из:

  • Условий, определяющих, применяется ли правило к данному контексту

  • Вариации флага для выдачи при совпадении правила

  • Опционального постепенного внедрения (rollout) для доставки на основе процентов

  • Приоритета, определяющего порядок оценки при наличии нескольких правил (меньшее число = более высокий приоритет)

Вложенные логические условия

Условия могут комбинироваться с использованием логики И/ИЛИ, вложенность допускается до пяти уровней. Одно правило может выражать такие вещи, как:

(plan == “enterprise” AND region == “us” ) OR (user.email.endsWith(“@cloudflare.com”))
= serve (“premium”)

На верхнем уровне правила несколько условий объединяются неявным оператором И, где все условия должны быть выполнены для совпадения правила. Внутри каждого условия вы можете вкладывать группы И/ИЛИ для более сложной логики.

Постепенное внедрение флагов по процентам (Rollout)

В отличие от постепенных развертываний, которые разделяют трафик между разными загруженными версиями вашего Worker, feature-флаги позволяют внедрять новое поведение по процентам в рамках одной версии, которая обслуживает 100% трафика.

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

Rollout использует консистентное хэширование указанного атрибута контекста. Одно и то же значение атрибута (например, userId) всегда хэшируется в одно и то же "ведро", поэтому пользователи не будут переключаться между вариациями от запроса к запросу. Вы можете увеличивать охват с 5% до 10%, затем до 50% и до 100% пользователей, при этом те, кто уже был в rollout, остаются в нём.

Создано для того, что будет дальше

Попадание AI-сгенерированного кода в продакшен будет только ускоряться. Агентские рабочие процессы (agentic workflows) подтолкнут это ещё дальше — агенты, которые автономно развертывают, тестируют и итерируют код в продакшене. Команды, которые преуспеют в этом мире, будут не теми, кто выпускает код быстрее всех. Они будут теми, кто сможет быстро выпускать код и при этом сохранять контроль над тем, что видят их пользователи, откатывать изменения за секунды, если что-то ломается, и постепенно открывать новые пути выполнения кода с уверенностью.

Для этого и создан Flagship:

  • Оценка по всему региону Земля, кэшируемая глобально с использованием K/V.

  • Полный аудит-трейл. Каждое изменение флага записывается с различиями на уровне полей, так что вы знаете, кто, что и когда изменил.

  • Интеграция с дашбордом. Любой член команды может переключить флаг или настроить rollout, не прикасаясь к коду.

  • Совместимость с OpenFeature. Внедряйте Flagship без переписывания кода оценки. Уходите от него — тоже без переписывания.

Начните работу с Flagship

Сегодня Flagship находится в стадии приватной беты. Вы можете запросить доступ здесь. Мы поделимся подробностями о ценах по мере приближения к публичной доступности (GA).

  • Посетите Cloudflare dashboard, чтобы создать своё первое приложение Flagship

  • Установите SDK: npm i @cloudflare/flagship; или используйте привязку Worker напрямую в вашем Worker

  • Ознакомьтесь с документацией для получения руководств по интеграции и справочника API

  • Изучите исходный код для примеров и возможности внести вклад