Flagshipの紹介:AI時代のために作られたフィーチャーフラグ
AIはこれまでにない速度でコードを書いています。AI支援の寄与はプラットフォーム上で新しいコードの急速に増える割合を占め、Agent的なコーディングツール(OpenCodeやClaude Codeのような)は数分で機能全体を出荷しています。AI生成コードが本番に入る流れはさらに加速しますが、より大きな変化は速度だけでなく自律性です。今日ではAIエージェントがコードを書き、人間がレビュー、マージ、デプロイします。明日にはそのエージェント自身がすべてを実行するでしょう。では、すべての安全網を取り払わずにエージェントを本番に出荷させるにはどうすればよいでしょうか。
フィーチャーフラグが答えです。エージェントは新しいコードパスをフラグの背後に書いてデプロイします — フラグはオフのままなのでユーザーへの挙動は変わりません。エージェントは自分自身や小さなテストコホートにフラグを有効にし、本番で機能を実行して結果を観察します。メトリクスが良ければロールアウトを段階的に拡大し、何かが壊れればフラグを無効にします。人間がすべてのステップに介入する必要はなく、境界を設定し、フラグがブラスト半径(影響範囲)を制御します。これはフィーチャーフラグが常に目指してきたワークフローであり、デプロイとリリースを切り離すだけでなく、出荷プロセスのあらゆる段階から人間の注意を切り離します。フラグがあるからエージェントは速く動いても安全に動けるのです。
本日、Flagshipを発表します — Cloudflareのネイティブなフィーチャーフラグサービスで、CNCFのフィーチャーフラグ評価のオープン標準である OpenFeature 上に構築されています。Workers、Node.js、Bun、Deno、ブラウザなどどこでも動作しますが、Workers上では最速です。Workers上ではフラグが Cloudflare ネットワーク内で評価されます。Flagship binding と OpenFeature を使った統合は次のようになります:
await OpenFeature.setProviderAndWait(
new FlagshipServerProvider({ binding: env.FLAGS })
);
Flagshipはクローズドベータで利用可能です。
Workers上のフィーチャーフラグにまつわる問題
多くのCloudflare開発者は実用的な回避策として、フラグロジックをWorkersに直接ハードコードすることに頼ってきました。最初はそれで十分に機能します。Workersは数秒でデプロイされるため、コード内のブール値を切り替えて本番にプッシュするのは多くの場合十分速いです。しかし、それは長続きしません。1つのハードコードされたフラグが10になり、10が50になり、異なるチームが所有するようになります。中央のビューが存在せず、監査トレイルもないため、問題発生時には誰が何を切り替えたかを 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を呼ぶ代わりにフラグルール全体をメモリにダウンロードしてローカルで評価します。評価ごとのアウトバウンドはなくなりますが、Workersではこの前提が崩れます。長時間生き続けるプロセスはなく、Workerのアイソレートは作成されリクエストに応答した後にエビクトされる可能性があります。新しい呼び出しはSDKを最初から初期化することを意味するかもしれません。サーバーレス環境では、すでにエッジに存在し、キャッシュが管理され、読み取りがローカルで、状態を最新に保つための永続接続を必要としない分配プリミティブが必要です。Cloudflare KV はこの用途に非常に適しています。
Flagshipの仕組み
FlagshipはCloudflareのインフラ(Workers、Durable Objects、KV)上に完全に構築されています。評価パスに外部データベースやサードパーティサービス、中央のオリジンサーバーは存在しません。フラグを作成または更新すると、コントロールプレーンは変更をDurable Objectに原子的に書き込みます。Durable ObjectはSQLiteをバックエンドに持つグローバルにユニークなインスタンスで、そのアプリのフラグ設定と変更ログの単一の信頼できるソースになります。数秒以内に更新はWorkers KV(Cloudflareのグローバル分散型キー・バリュー・ストア)に同期され、ネットワーク全体にレプリケートされます。
リクエストがフラグを評価すると、Flagshipはリクエストを処理しているのと同じエッジのKVからフラグ設定を直接読み取ります。評価エンジンはそのアイソレート内で実行され、リクエストコンテキストをターゲティングルールと照合し、ロールアウトのパーセンテージを解決して、バリエーションを返します。データもロジックもエッジ上に存在し、評価のために他所へ送られることはありません。
Flagshipの使用方法:Workerバインディング
Cloudflare Workersを実行するチーム向けに、FlagshipはWorkerランタイム内でフラグを評価する直接バインディングを提供します。HTTPラウンドトリップもSDKのオーバーヘッドも不要です。wrangler.jsonc にバインディングを追加するとWorkerがFlagshipに接続されます:
{
"flagship": [
{
"binding": "FLAGS",
"app_id": "<APP_ID>"
}
]
}
アカウント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',
});
},
};
バインディングはすべてのバリエーション型に対する型付きアクセサ(getBooleanValue(), getStringValue(), getNumberValue(), getObjectValue())をサポートし、解決された値と一致したバリアントおよび選択理由を返す *Details() 変種も提供します。評価エラー時はデフォルト値が優雅に返され、型の不一致があるとバインディングは例外を投げます — それは一時的な失敗ではなくコードのバグです。
SDK:OpenFeatureネイティブ
ほとんどのフィーチャーフラグSDKは独自のインターフェースと評価パターンを持ち、時間とともにコードベースに深く埋め込まれて、プロバイダを切り替えるにはすべての呼び出し箇所を書き換える必要が出ます。私たちはそれを繰り返したくありませんでした。FlagshipはOpenFeature上に構築されています。OpenFeatureは言語とプロバイダを横断するフラグ評価の共通インターフェースを定義するCNCFのオープン標準であり、OpenTelemetryが観測性に対して果たす役割に相当します。一度標準に対して評価コードを書けば、設定の1行を変えるだけでプロバイダを差し替えられます。
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はHTTPではなくバインディングを通してフラグを評価します。標準の移植性とバインディングの性能を両立できます。ブラウザ用のクライアントサイドプロバイダもあり、指定したフラグを事前取得し、設定可能なTTLでキャッシュして、そのキャッシュから同期的に評価を返します。
Flagshipでできること
Flagshipは一般に期待されるパターンと、AI生成コードが本番に頻繁に入る状況で重要になるパターンの両方をサポートします。フラグ値はboolean、string、number、または完全なJSONオブジェクトにでき、構成ブロックやUIテーマ定義、別々のコードパスを維持せずに異なるAPIバージョンへルーティングするなどに有用です。
-
ターゲティングルール
- 各フラグは優先順位順に評価される複数のルールを持てます。最初に一致したルールが勝ちます。
- ルールは次の要素で構成されます:
- そのコンテキストにルールが適用されるかを決める条件
- ルールが一致したときに返すフラグバリエーション
- 任意のパーセンテージロールアウト
- 複数ルールがある場合の評価順を決める優先度(数値が小さいほど高優先)
-
ネストされた論理条件
-
条件はAND/ORで構成でき、最大5レベルまでネスト可能です。単一ルールで例えば次のように表現できます:
(plan == "enterprise" AND region == "us") OR (user.email.endsWith("@cloudflare.com")) = serve ("premium")
-
ルールのトップレベルでは複数の条件が暗黙のANDで結合され、すべての条件が満たされればルールが一致します。各条件内ではAND/ORグループをネストして複雑な論理を表現できます。
-
パーセンテージによるロールアウト
- 段階的デプロイ(異なるアップロード済みWorkerバージョン間でトラフィックを分割する手法)とは異なり、フィーチャーフラグは単一バージョンで100%トラフィックを処理しているまま、パーセンテージ単位で振る舞いをロールアウトできます。任意のルールにパーセンテージロールアウトを含められ、条件に一致するすべてのユーザーではなくその一部にバリエーションを提供できます。ロールアウトは指定したコンテキスト属性に基づく一貫したハッシュを用います。同じ属性値(例:
userId)は常に同じバケットにハッシュされるため、リクエスト間でバリエーションが切り替わることはありません。5%→10%→50%→100%と段階的に増やしても、既にロールアウトに含まれているユーザーはそのまま維持されます。
次に来るものに向けて構築
AI生成コードの本番投入はさらに加速し、エージェント的ワークフローがそれを推し進めます。エージェントが自律的に本番でデプロイ、テスト、反復する未来において成功するチームは、単に最速で出荷するチームではありません。速く出荷しつつ、ユーザーに見せるものを制御し、問題時に秒でロールバックでき、新しいコードパスを段階的に自信をもって公開できるチームです。これがFlagshipの目標です。
- 地域全体での評価:KVでグローバルにキャッシュ
- 完全な監査トレイル:すべてのフラグ変更はフィールドレベルの差分で記録され、誰がいつ何を変更したかが分かる
- ダッシュボード統合:誰でもフラグの状態と変更履歴を確認できる