フィーチャーフラグ:設定ではなく負債である
フラグは到達可能なコード経路を倍にし、そして自動では消えません。精巧な仕組みを作るより、各フラグに削除日を決める方がはるかに重要です。
フラグが解く問題は明快です。配備と公開を分離し、段階公開と巻き戻しを可能にします。生む問題も同じくらい明快です。フラグを一つ足すごとに取り得る状態の組み合わせが倍になり、削除する担当者がいません。
三種、寿命はまったく違う
| 種類 | 寿命 | 例 |
|---|---|---|
| 公開フラグ | 数日から数週 | 新しいトップの段階公開 |
| 運用フラグ | 数か月、永続も | 依存先の緊急停止 |
| 実験フラグ | 実験一回分 | A/B テスト |
三つを一つの仕組みで管理することが混乱の始まりです。公開フラグは必ず削除します。 公開作業の足場にすぎません。
真偽値ではなく日付を使う
フラグに期限の欄を持たせる方が、どんな文書の取り決めより効きます。
const FLAGS = {
newCheckout: { enabled: false, expiresAt: '2026-10-31' },
legacySearch: { enabled: true, expiresAt: '2026-12-01' },
} as const;
テストを一つ添えます。期限切れのフラグはビルドを失敗させます。
for (const [name, f] of Object.entries(FLAGS)) {
if (Date.parse(f.expiresAt) < Date.now()) {
throw new Error('flag ' + name + ' is past its removal date');
}
}
この表明が「後で片付ける」を「今すぐ処理する」に変えます。無ければフラグは永久に残ります。
読み取りには既定値を持たせる
function isEnabled(name: keyof typeof FLAGS): boolean {
const override = process.env['FLAG_' + name];
return override ? override === '1' : FLAGS[name].enabled;
}
既定値はコードに置き、環境変数は一時的な上書きに限定します。逆(既定は無効、設定で有効化)にすると、新しい環境で設定を忘れて機能が黙って無効になります。
分岐は一箇所に
フラグはただ一箇所に現れるべきです。散在する if (flag) は削除をほぼ不可能にします。
// 良い:一箇所で実装を選ぶ
export const checkout = isEnabled('newCheckout') ? newCheckout : legacyCheckout;
// 悪い:実装の内部に分岐が散る
export function checkout(cart) {
if (isEnabled('newCheckout')) { /* ... */ }
if (isEnabled('newCheckout')) { /* ... */ }
}
前者の削除は一行と一ファイルを消すだけです。後者は数十箇所で統合を戻す必要があります。
フラグは業務規則ではない
三か月有効で、無効化の予定が無いなら、それはもうフラグではありません。設定であり、正式なコード経路に統合すべきです。 フラグと呼び続けると、いつでも消せると誤解されます。
フラグの価値は公開の数日間にあります。その後一日残るごとに、コードを読みにくく、試験しにくくする負債が増します。

コメント
…