用語集 · CI/CD と DevOps
フィーチャーフラグ
英語: Feature flag
別名: フィーチャートグル, 機能フラグ, feature flags, feature toggle
定義
デプロイせずにコードパスをオン・オフできる実行時のスイッチで、未完成の機能を隠したまま出荷し、段階的に公開し、素早くロールバックできます。
フィーチャーフラグ(フィーチャートグル)とは、値が実行時に設定から決まるコード内の条件です。このユーザーや環境でフラグがオンなら新しいパスを通り、そうでなければ古いパスを通ります。これにより、コードのデプロイと機能のリリースを切り離せます。コードはオフの状態で本番環境に置いておき、社内ユーザー、顧客の 10%、全員へと順に有効化し、問題が起きれば数秒でオフに戻せます。
具体例
if (flags.isEnabled('new-checkout', { userId: user.id })) {
return renderNewCheckout(cart);
}
return renderLegacyCheckout(cart);
フラグサービス(商用製品、オープンソース製品、あるいは自社データベースのテーブル)が、多くの場合は割合や属性に基づいて、ユーザーごとの値を決めます。
テストにとっての意味
フラグが 1 つ増えるごとに、そのコード部分を通るパスの数は 2 倍になります。テスターは次のことを把握する必要があります。
- どのフラグ状態の組み合わせで出荷されるか。 「すべてオン」だけでなく、本番で実際に有効になる組み合わせをテストします。
- 有効な各フラグの両側。 フラグが削除されるまで古いパスも本番コードなので、回帰テストスイートでカバーする必要があります。
- 切り替えそのもの。 セッション中にフラグをオン・オフしても、データが壊れたり、ユーザーがフローの途中で立ち往生したりしてはいけません。
- テストでの制御。 自動テストでは、共有環境の現在の設定に依存するのではなく、テスト専用 API、Cookie、設定の上書きなどでフラグの値を明示的に設定すべきです。そうしないと、誰かがフラグを切り替えただけで無関係なテスト実行が壊れ、それはフレーキーテストとまったく同じように見えます。
test('new checkout shows delivery slots', async ({ page, context }) => {
await context.addCookies([
{ name: 'ff_new-checkout', value: 'on', url: 'https://staging.example.com' },
]);
await page.goto('https://staging.example.com/checkout');
await expect(page.getByRole('heading', { name: 'Choose a delivery slot' })).toBeVisible();
});
ここでの Cookie 名は説明用の例です。実際には、お使いのフラグシステムが提供する上書きの仕組みを使ってください。
よくある間違い
長期的な最大のコストは、いつまでも削除されないフラグです。デッドブランチ、誰も理解していない組み合わせ、静かに劣化していく古いパスが残ります。リリース用のフラグは一時的なものとして扱い、オーナーと削除日を決め、ロールアウトが完了したら使われなくなった側の分岐とそのテストを削除します。