開発効率化

Vercel Flags入門——Next.jsとFirestoreで段階リリースを運用する

Vercel Flagsの最新発表をもとに、Next.js App Router、Firestore、GitHub Actionsで安全な段階リリースを作る実践手順を解説します。

2026年6月23日
Vercel FlagsFeature FlagsNext.jsFirestoreGitHub Actions
Vercel Flags入門——Next.jsとFirestoreで段階リリースを運用する

はじめに

新機能をリリースするとき、こんな運用になっていませんか?

  • main に入れたいが、全ユーザーへ出すにはまだ怖い
  • Preview環境では見えるのに、本番の一部ユーザーだけで試す方法が弱い
  • リリース後に問題が出たとき、再デプロイかrevertでしか止められない
  • 「誰に、いつ、どの機能を開けたか」がFirestoreに残っていない

2026年6月23日(JST)時点で、Vercelは公式ブログ「Vercel Flags: Platform-native feature flags」を公開しています。記事では、Vercel Flagsが2026年4月にGAになったこと、Flags Explorerでブラウザ上からセッション単位の上書き検証ができること、v0のようなプロダクトで新機能、AIモデルルーティング、kill switch、データベース移行などをフラグ配下に置いていることが紹介されています。

本記事では、架空の請求管理SaaS「InvoiceFlow」を題材に、Next.js App Router + TypeScript + Firestore + GitHub Actionsで、段階リリースを安全に回す構成を作ります。この記事を読み終わると、機能フラグを単なるオンオフではなく、リリース台帳、監査ログ、CIチェックまで含めた運用として設計できるようになります。

参考にした一次情報は次の通りです。

Vercel Flagsで変わること

Vercel Flagsの実務上の価値は、デプロイと公開判断を分けられる点です。コードは main に統合し、VercelのPreviewで確認し、本番では内部ユーザー、早期利用チーム、5%、25%、50%、100%のように段階を踏んで公開できます。

観点環境変数だけで切り替える場合Vercel Flagsを使う場合
公開範囲環境単位になりやすいユーザー、チーム、割合で制御しやすい
検証PreviewとProductionの差が大きいFlags Explorerでセッション単位に上書きできる
緊急停止revertや再デプロイに寄りがちkill switchとして即時に閉じやすい
実装文字列キーが散らばりやすいflags SDKで型付き関数として呼べる

公式ドキュメントでは、Next.jsでは flags@flags-sdk/vercel を使う構成が案内されています。@flags-sdk/vercelvercelAdapter()FLAGS 環境変数を読み、Vercel Flagsプロジェクトへ接続します。

yarn add flags @flags-sdk/vercel firebase-admin

今回のInvoiceFlowでは、請求書一覧の新しい集計パネルを invoice-summary-panel というフラグで段階公開します。

ハンズオン1: フラグをコードで定義する

まず、flags.ts にフラグを定義します。getSessionFromCookie はInvoiceFlow側で用意する認証ヘルパーです。Vercel Flagsは「誰がログインしているか」を自動で知るわけではないので、評価に使うユーザー属性はアプリ側から渡します。

// src/flags.ts
import { dedupe, flag } from "flags/next";
import { vercelAdapter } from "@flags-sdk/vercel";
import { getSessionFromCookie } from "@/lib/auth/session";

type Entities = {
  user?: {
    id: string;
    email: string;
    plan: "free" | "pro" | "enterprise";
  };
  team?: {
    id: string;
    name: string;
  };
};

const identify = dedupe(async (): Promise<Entities> => {
  const session = await getSessionFromCookie();

  return {
    user: session?.user
      ? {
          id: session.user.id,
          email: session.user.email,
          plan: session.user.plan,
        }
      : undefined,
    team: session?.team
      ? {
          id: session.team.id,
          name: session.team.name,
        }
      : undefined,
  };
});

export const invoiceSummaryPanel = flag<boolean, Entities>({
  key: "invoice-summary-panel",
  adapter: vercelAdapter(),
  identify,
  description: "請求書一覧の新しい集計パネルを表示する",
});

dedupe を使うと、同じリクエスト内で複数のフラグが identify を呼んでも、セッション取得を重複させにくくなります。請求画面ではServer Componentからフラグを評価し、表示を切り替えます。

// src/app/(dashboard)/invoices/page.tsx
import { invoiceSummaryPanel } from "@/flags";
import { InvoiceList } from "@/components/invoices/InvoiceList";
import { InvoiceSummaryPanel } from "@/components/invoices/InvoiceSummaryPanel";

export default async function InvoicesPage() {
  const showSummaryPanel = await invoiceSummaryPanel();

  return (
    <main className="space-y-6">
      {showSummaryPanel ? <InvoiceSummaryPanel /> : null}
      <InvoiceList />
    </main>
  );
}

ここで重要なのは、Client Component側で「あとから表示を変える」のではなく、サーバー側で評価してからHTMLを返すことです。公式ブログでも、Vercel Flagsはサーバー側評価により、クライアント側評価の追加ラウンドトリップを避けられる点が説明されています。

ハンズオン2: Flags Explorer向けのDiscovery Endpointを作る

Flags Explorerでフラグを見つけるには、アプリがフラグ定義を返すDiscovery Endpointを公開します。公式ドキュメントでは、Next.js App Routerで .well-known/vercel/flags 配下にRoute Handlerを作る例が示されています。

// src/app/.well-known/vercel/flags/route.ts
import { createFlagsDiscoveryEndpoint } from "flags/next";
import { getProviderData } from "@flags-sdk/vercel";
import * as flags from "../../../../flags";

export const GET = createFlagsDiscoveryEndpoint(async () => {
  return getProviderData(flags);
});

これにより、開発者はVercel ToolbarのFlags Explorerから invoice-summary-panel を探し、自分のブラウザセッションだけ true にして画面を確認できます。共有設定を直接変えずに試せるため、「本番データで表示だけ確認したい」という場面で使いやすくなります。

ハンズオン3: Firestoreにリリース台帳を残す

フラグの値そのものはVercel側で管理し、業務アプリでは「いつ公開判断を変えたか」「問題が起きたとき何を閉じたか」を残します。InvoiceFlowでは featureRollouts コレクションを台帳にします。

// src/lib/rollouts/createRolloutEvent.ts
import { FieldValue, getFirestore } from "firebase-admin/firestore";

type RolloutEvent = {
  flagKey: string;
  action: "created" | "expanded" | "paused" | "completed";
  actorId: string;
  reason: string;
  target: "internal" | "beta" | "percentage" | "all";
};

export async function createRolloutEvent(event: RolloutEvent) {
  await getFirestore().collection("featureRollouts").add({
    ...event,
    createdAt: FieldValue.serverTimestamp(),
  });
}

管理画面では、フラグ操作そのものをVercel Dashboardで行い、InvoiceFlowには意思決定の記録を残す設計にします。Vercel Flagsの全APIを自前で包むより、責務がはっきりします。

Firestoreに残すもの
flagKeyinvoice-summary-panel
actionexpanded
targetbeta
reasonIF-014からIF-021までの検証が完了したため
actorId操作した社内ユーザーID

架空チケットは IF-001 から IF-030 のように管理します。実際の顧客名、請求額、契約番号はログに入れず、公開判断に必要な最小限の情報だけを保存します。

GitHub Actionsで期限切れフラグを検知する

機能フラグは便利ですが、残り続けると技術的負債になります。GitHub Actionsでは、src/flags.ts に定義したフラグと、Firestoreの台帳にある expiresAt を突き合わせるスクリプトを実行します。TypeScriptスクリプトを直接実行する例では、実在するnpmパッケージの tsx を使います。

yarn add -D tsx
name: rollout-check

on:
  pull_request:
    branches: [main]

jobs:
  check-flags:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "yarn"
      - run: yarn install --frozen-lockfile
      - run: yarn tsx scripts/check-expired-flags.ts
      - run: yarn build

tsx を使う場合は、プロジェクトに依存関係として追加してから使います。既存プロジェクトでNode.jsだけに寄せるなら、TypeScriptをビルドしてから node で実行する形でも構いません。

// scripts/check-expired-flags.ts
import { getFirestore } from "firebase-admin/firestore";

const snapshot = await getFirestore()
  .collection("featureRollouts")
  .where("status", "==", "active")
  .where("expiresAt", "<", new Date())
  .limit(20)
  .get();

if (!snapshot.empty) {
  const keys = snapshot.docs.map((doc) => doc.get("flagKey")).join(", ");
  throw new Error(`期限切れのfeature flagがあります: ${keys}`);
}

Vercelの公式料金ページでは、1リクエストで同じソースプロジェクトの複数フラグを評価しても1 flag requestとして扱われる例が示されています。一方で、フラグ数、セグメント数、サイズには上限があります。だからこそ、フラグには期限と削除条件を持たせ、ロールアウト完了後にコードからも消す運用が必要です。

まとめ

Vercel Flagsは、Next.jsアプリのリリース運用を「デプロイする」と「公開する」に分けるための現実的な選択肢です。

要点は次の通りです。

  • flags@flags-sdk/vercel を使うと、Next.js App Routerでフラグを型付き関数として扱える
  • identify でユーザーやチーム属性を返すと、Vercel Dashboard側のターゲティングに使える
  • Flags ExplorerのDiscovery Endpointを用意すると、セッション単位の検証がしやすい
  • Firestoreにはフラグ値ではなく、公開判断、理由、期限、担当者を残す
  • GitHub Actionsで期限切れフラグを検知し、リリース後にコードを掃除する

まずは、失敗時に即閉じたい小さな新UIから始めるのが扱いやすいです。請求書一覧、検索条件、AI要約、Firestoreクエリの切り替えのように、リスクを限定できる機能を1つ選び、フラグ、台帳、CIチェックをセットで導入してみてください。

参考資料