mcpField
F
feature-flags

feature-flags

github.com/tushaarmehtaa開発1240 回インストール2 週間前に更新
リポジトリ

feature-flags について

アプリに PostHog のフィーチャーフラグを組み込む開発スキルです。サーバー側評価、クライアント側フック、ローカルでの上書きを扱い、既存のプロジェクト設定を確認して PostHog の構成を拡張します。

主な機能

  1. 01既存の PostHog 設定を確認
  2. 02サーバー側でフラグを評価
  3. 03操作後にクライアントフックを使用
  4. 04ローカル開発でフラグを上書き
  5. 05PostHog でのフラグ作成を案内

活用シーン

  1. 01初期表示のちらつきを避けたルートやページ表示制御
  2. 02ユーザーのフラグに応じた API 動作制御
  3. 03ローカルでのフラグバリアント検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/tushaarmehtaa/tushar-skills --skill feature-flags

インストール後、Claude Code で/feature-flagsを入力して読み込まれたか確認します。

詳細

PostHog のフィーチャーフラグをエンドツーエンドで接続します。サーバーサイド評価、クライアントサイドのフック、ローカル開発時のオーバーライドに対応します。まずプロジェクトを確認し、PostHog の既存設定があれば拡張します。

PostHog のフラグが気付かないうちに壊れる 2 つの原因

  1. サーバーサイドでの動作が必要なのに、クライアントサイドでフラグを評価している。 useFeatureFlagEnabled はハイドレーション後にブラウザーで実行されます。フラグを使ってページのセクションを表示/非表示にしている場合、そのセクションが一瞬表示されたあとに消えます。ファーストビューに表示されるコンテンツやルーティングに影響するものは、サーバーでフラグを評価してください。
  2. サーバーサイドの呼び出しに distinctId がない。 PostHog のサーバー SDK でフラグを評価するには、ユーザーを識別する distinct ID が必要です。"anonymous" のようなプレースホルダーを使用すると、認証されていないすべてのユーザーに同じフラグのバリアントが割り当てられます。これはロールアウトではなく、単なる固定値です。

フェーズ 1:プロジェクトを検出する

cat package.json | grep -E "posthog"
grep -r "posthog" src/ app/ --include="*.ts" --include="*.tsx" -l 2>/dev/null | head -5
  • posthog-js がインストール済みで、プロバイダーも見つかった場合 → PostHog のクライアントサイド設定はすでに完了しています。サーバーサイド SDK を追加します。
  • posthog-node がすでにインストールされている場合 → 再インストールせず、既存の設定を拡張します。
  • 何もない場合 → まず add-analytics スキルを使って PostHog を最初から接続し、その後ここに戻ります。

フェーズ 2:サーバー SDK をインストールする

npm install posthog-node

.env.example に追加します(add-analytics スキルを実行済みの場合は設定済みです)。

NEXT_PUBLIC_POSTHOG_KEY=
POSTHOG_API_KEY=   # same as POSTHOG_KEY — use for server-side calls

フェーズ 3:サーバーサイドのフラグクライアント

サーバーサイド評価用のシングルトンを作成します。

// lib/posthog-server.ts
import { PostHog } from 'posthog-node';

let client: PostHog | null = null;

export function getPostHogServer(): PostHog {
  if (!client) {
    client = new PostHog(process.env.POSTHOG_API_KEY!, {
      host: 'https://us.i.posthog.com',
      flushAt: 1,      // flush immediately in serverless
      flushInterval: 0,
    });
  }
  return client;
}

export async function isFeatureEnabled(
  flagKey: string,
  distinctId: string,
  groups?: Record<string, string>
): Promise<boolean> {
  const ph = getPostHogServer();
  const result = await ph.isFeatureEnabled(flagKey, distinctId, { groups });
  return result ?? false;
}

export async function getFeatureFlagVariant(
  flagKey: string,
  distinctId: string
): Promise<string | boolean | undefined> {
  const ph = getPostHogServer();
  return ph.getFeatureFlag(flagKey, distinctId);
}

フェーズ 4:サーバーコンポーネントでの使用

サーバーコンポーネントとルートハンドラーでフラグを評価し、クライアントサイドでのちらつきを防ぎます。

// app/dashboard/page.tsx (Server Component)
import { isFeatureEnabled } from '@/lib/posthog-server';
import { getAuthUser } from '@/lib/auth';

export default async function DashboardPage() {
  const user = await getAuthUser();

  const showNewDashboard = user
    ? await isFeatureEnabled('new-dashboard', user.id)
    : false;

  return showNewDashboard ? <NewDashboard /> : <OldDashboard />;
}
// app/api/generate/route.ts (Route Handler)
import { isFeatureEnabled } from '@/lib/posthog-server';

export async function POST(req: Request) {
  const user = await getAuthUser(req);
  if (!user) return Response.json({ error: 'Unauthorized' }, { status: 401 });

  const hasStreamingAccess = await isFeatureEnabled('streaming-responses', user.id);

  if (hasStreamingAccess) {
    return handleStreaming(req, user);
  }
  return handleNonStreaming(req, user);
}

フェーズ 5:クライアントサイドでの使用

ユーザーの操作後に UI を変更するフラグ(初回レンダリング時には変更しないもの)には、次のようにします。

// components/feature-flagged-button.tsx
'use client';
import { useFeatureFlagEnabled } from 'posthog-js/react';

export function NewFeatureButton() {
  const isEnabled = useFeatureFlagEnabled('new-export-feature');

  // useFeatureFlagEnabled returns undefined while loading
  if (isEnabled === undefined) return null;
  if (!isEnabled) return null;

  return <button>Export (New)</button>;
}

ファーストビューに表示されるコンテンツには、クライアントフックを決して使用しないでください。 フックは初回レンダリング時に undefined を返し、レイアウトシフトが発生します。ページ読み込み時に表示されるものには、サーバーサイドの方法を使用してください。

フェーズ 6:ローカル開発時のオーバーライド

PostHog に触れずにローカルで両方のバリアントをテストできるよう、開発時のみ有効なフラグのオーバーライド機構を追加します。

// lib/posthog-server.ts (add to existing file)
export async function isFeatureEnabled(
  flagKey: string,
  distinctId: string,
  groups?: Record<string, string>
): Promise<boolean> {
  // Local override for development
  if (process.env.NODE_ENV === 'development') {
    const override = process.env[`FLAG_${flagKey.toUpperCase().replace(/-/g, '_')}`];
    if (override === 'true') return true;
    if (override === 'false') return false;
  }

  const ph = getPostHogServer();
  const result = await ph.isFeatureEnabled(flagKey, distinctId, { groups });
  return result ?? false;
}

.env.local に追加します。

FLAG_NEW_DASHBOARD=true    # force-enable new-dashboard flag locally
FLAG_STREAMING_RESPONSES=false  # force-disable

フェーズ 7:PostHog でフラグを作成する

ユーザーに、設定する内容を正確に伝えます。PostHog ダッシュボードで次のように設定します。

  1. Feature Flags → New Feature Flag
  2. Key:new-dashboard(コード内の値と一致させます)
  3. Rollout:0% から開始し、10%、50%、100% と段階的に増やします
  4. Conditions:ユーザープロパティ(plan = 'pro')、コホート、またはパーセンテージでターゲットを指定できます

A/B テストでは、ブール値の代わりに「Multiple Variants」を使用します。その後、getFeatureFlagVariant() を使い、'control' または 'test' を返します。

確認

[ ] posthog-node installed
[ ] POSTHOG_API_KEY in .env.example
[ ] Server-side evaluation uses real user ID as distinctId — not "anonymous"
[ ] Server Components use isFeatureEnabled() — not the client hook
[ ] Client hook used only for post-hydration interactions, not initial render
[ ] Local override via FLAG_* env vars works in development
[ ] Flags created in PostHog dashboard with matching key names
[ ] Test: set FLAG_[KEY]=false in .env.local — confirm feature is hidden
[ ] Test: set FLAG_[KEY]=true in .env.local — confirm feature is shown
[ ] PostHog flag set to 0% rollout first — expand after confirming it works

コメント