SCALE — Build Lab
開発パターン · TYPESCRIPT PATTERN

フィーチャーフラグ

CATEGORY開発パターン TYPETypeScript Pattern EFFORT90〜240分 DIFFICULTY
PRIMARY CODE
ts
// フィーチャーフラグ(ユーザーID別 + % ロールアウト)
type Flag = { name: string; rollout: number; allowedUserIds?: string[] };

const FLAGS: Flag[] = [
  { name: 'new-dashboard', rollout: 30 },
  { name: 'beta-export', rollout: 0, allowedUserIds: ['user-1', 'user-2'] },
];

// 安定ハッシュ(ユーザーIDから0-99の数字を出す)
function userBucket(userId: string, flagName: string): number {
  const s = userId + ':' + flagName;
  let h = 0;
  for (let i = 0; i < s.length; i++) h = ((h << 5) - h) + s.charCodeAt(i);
  return Math.abs(h) % 100;
}

export function isFeatureEnabled(flagName: string, userId: string): boolean {
  const f = FLAGS.find((x) => x.name === flagName);
  if (!f) return false;
  if (f.allowedUserIds?.includes(userId)) return true;
  return userBucket(userId, flagName) < f.rollout;
}

// React Hook 版
export function useFeature(flagName: string, userId: string) {
  return isFeatureEnabled(flagName, userId);
}

// 使い方:
// const showNew = useFeature('new-dashboard', currentUser.id);
// {showNew ? <NewDashboard /> : <OldDashboard />}
前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • 新機能段階リリース

フィーチャーフラグ

:LiTarget: 用途

機能のON/OFFを実行時に切り替えるパターン。段階的リリースに活用。

:LiSparkle: 特徴

  • ユーザー別フラグ
  • %ロールアウト
  • A/Bテスト

:LiCode: コード(コピペ用)

// フィーチャーフラグ(ユーザーID別 + % ロールアウト)
type Flag = { name: string; rollout: number; allowedUserIds?: string[] };

const FLAGS: Flag[] = [
  { name: 'new-dashboard', rollout: 30 },
  { name: 'beta-export', rollout: 0, allowedUserIds: ['user-1', 'user-2'] },
];

// 安定ハッシュ(ユーザーIDから0-99の数字を出す)
function userBucket(userId: string, flagName: string): number {
  const s = userId + ':' + flagName;
  let h = 0;
  for (let i = 0; i < s.length; i++) h = ((h << 5) - h) + s.charCodeAt(i);
  return Math.abs(h) % 100;
}

export function isFeatureEnabled(flagName: string, userId: string): boolean {
  const f = FLAGS.find((x) => x.name === flagName);
  if (!f) return false;
  if (f.allowedUserIds?.includes(userId)) return true;
  return userBucket(userId, flagName) < f.rollout;
}

// React Hook 版
export function useFeature(flagName: string, userId: string) {
  return isFeatureEnabled(flagName, userId);
}

// 使い方:
// const showNew = useFeature('new-dashboard', currentUser.id);
// {showNew ? <NewDashboard /> : <OldDashboard />}

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加