feature-flags について
アプリに PostHog のフィーチャーフラグを組み込む開発スキルです。サーバー側評価、クライアント側フック、ローカルでの上書きを扱い、既存のプロジェクト設定を確認して PostHog の構成を拡張します。
主な機能
- 01既存の PostHog 設定を確認
- 02サーバー側でフラグを評価
- 03操作後にクライアントフックを使用
- 04ローカル開発でフラグを上書き
- 05PostHog でのフラグ作成を案内
活用シーン
- 01初期表示のちらつきを避けたルートやページ表示制御
- 02ユーザーのフラグに応じた API 動作制御
- 03ローカルでのフラグバリアント検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tushaarmehtaa/ tushar-skills --skill feature-flagsインストール後、Claude Code で/feature-flagsを入力して読み込まれたか確認します。
詳細
PostHog のフィーチャーフラグをエンドツーエンドで接続します。サーバーサイド評価、クライアントサイドのフック、ローカル開発時のオーバーライドに対応します。まずプロジェクトを確認し、PostHog の既存設定があれば拡張します。
PostHog のフラグが気付かないうちに壊れる 2 つの原因
- サーバーサイドでの動作が必要なのに、クライアントサイドでフラグを評価している。
useFeatureFlagEnabledはハイドレーション後にブラウザーで実行されます。フラグを使ってページのセクションを表示/非表示にしている場合、そのセクションが一瞬表示されたあとに消えます。ファーストビューに表示されるコンテンツやルーティングに影響するものは、サーバーでフラグを評価してください。 - サーバーサイドの呼び出しに
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 ダッシュボードで次のように設定します。
- Feature Flags → New Feature Flag
- Key:
new-dashboard(コード内の値と一致させます) - Rollout:0% から開始し、10%、50%、100% と段階的に増やします
- 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