フィーチャーフラグとは
フィーチャーフラグ(機能フラグ)は、コードをデプロイした後でも機能のオン/オフを切り替えられる仕組みです。リスクを最小化しながら新機能をリリースできます。
flowchart TB
subgraph Deploy["デプロイ済みコード"]
Code["新機能コード"]
Flag["フラグ判定"]
Code --> Flag
end
Flag -->|ON| NewFeature["新機能表示"]
Flag -->|OFF| OldFeature["既存機能表示"]
Config["フラグ設定<br/>(管理画面)"] -.->|制御| Flag
フラグの種類
| 種類 | 用途 | 寿命 |
|---|---|---|
| リリースフラグ | 新機能の段階的公開 | 短期(数週間) |
| 実験フラグ | A/Bテスト | 中期(数ヶ月) |
| 運用フラグ | 機能の緊急停止 | 長期 |
| 権限フラグ | ユーザー種別による制御 | 永続 |
実装パターン
シンプルな実装
// フラグ設定
const featureFlags = {
newCheckout: true,
darkMode: false,
betaFeatures: process.env.NODE_ENV === 'development',
};
// 使用箇所
function CheckoutPage() {
if (featureFlags.newCheckout) {
return <NewCheckout />;
}
return <LegacyCheckout />;
}
コンテキストベース実装
interface FeatureFlagContext {
userId: string;
userRole: 'free' | 'premium' | 'enterprise';
country: string;
percentage?: number;
}
class FeatureFlagService {
private flags: Map<string, FlagRule[]> = new Map();
isEnabled(flagName: string, context: FeatureFlagContext): boolean {
const rules = this.flags.get(flagName);
if (!rules) return false;
for (const rule of rules) {
if (this.evaluateRule(rule, context)) {
return true;
}
}
return false;
}
private evaluateRule(rule: FlagRule, context: FeatureFlagContext): boolean {
// ユーザーロールチェック
if (rule.roles && !rule.roles.includes(context.userRole)) {
return false;
}
// パーセンテージロールアウト
if (rule.percentage !== undefined) {
const hash = this.hashUserId(context.userId);
return hash < rule.percentage;
}
return true;
}
private hashUserId(userId: string): number {
// 0-100の一貫したハッシュ値を生成
let hash = 0;
for (let i = 0; i < userId.length; i++) {
hash = ((hash << 5) - hash) + userId.charCodeAt(i);
hash |= 0;
}
return Math.abs(hash) % 100;
}
}
Reactでの使用
// フラグプロバイダー
const FeatureFlagContext = createContext<FeatureFlagService | null>(null);
export function useFeatureFlag(flagName: string): boolean {
const service = useContext(FeatureFlagContext);
const user = useCurrentUser();
if (!service || !user) return false;
return service.isEnabled(flagName, {
userId: user.id,
userRole: user.role,
country: user.country,
});
}
// コンポーネントで使用
function Dashboard() {
const showNewAnalytics = useFeatureFlag('new-analytics');
return (
<div>
{showNewAnalytics ? <NewAnalytics /> : <LegacyAnalytics />}
</div>
);
}
段階的ロールアウト
// 1% → 5% → 25% → 50% → 100% と段階的に公開
const rolloutConfig = {
'new-payment': {
stages: [
{ percentage: 1, startDate: '2025-01-10' },
{ percentage: 5, startDate: '2025-01-12' },
{ percentage: 25, startDate: '2025-01-15' },
{ percentage: 50, startDate: '2025-01-20' },
{ percentage: 100, startDate: '2025-01-25' },
],
},
};
運用ベストプラクティス
1. フラグの命名規則
// 良い例
'checkout-new-payment-form'
'experiment-pricing-page-v2'
'ops-maintenance-mode'
// 悪い例
'flag1'
'new_feature'
'test'
2. 技術的負債の管理
// フラグに期限を設定
interface FeatureFlag {
name: string;
enabled: boolean;
expiresAt: Date; // 期限切れアラート
owner: string; // 責任者
}
// 定期的なクリーンアップ
function auditFlags(flags: FeatureFlag[]) {
const expiredFlags = flags.filter(f => f.expiresAt < new Date());
if (expiredFlags.length > 0) {
notifyOwners(expiredFlags);
}
}
3. 緊急停止(キルスイッチ)
// 即座に機能を停止できる仕組み
async function killSwitch(flagName: string) {
await flagService.disable(flagName);
await invalidateCache(flagName);
await notifyTeam(`${flagName} disabled via kill switch`);
}
現場での失敗例と対策
- フラグの削除忘れ → 期限設定と定期レビュー
- テスト環境との不整合 → 環境別設定の明確化
- パフォーマンス低下 → フラグ評価のキャッシュ
関連記事
- ブルーグリーンデプロイメント - デプロイ戦略
- CI/CDパイプライン - 自動化パイプライン
- A/Bテスト設計 - テスト戦略
まとめ
フィーチャーフラグは、リリースリスクを軽減する強力なツールです。適切な設計と運用ルールを設けることで、安全かつ高速なリリースサイクルを実現できます。
← Back to list