LaunchDarkly

LaunchDarkly A/B分析 完全技術ガイド

目次

  1. はじめに
  2. LaunchDarklyの概要
  3. フィーチャーフラグの基本概念
  4. A/Bテストとエクスペリメンテーション
  5. アーキテクチャの全体像
  6. SDK統合とデータフロー
  7. メトリクスとイベント設計
  8. 統計エンジンと分析手法
  9. ターゲティングとセグメンテーション
  10. 実装パターンと具体例
  11. Relay Proxyとエンタープライズアーキテクチャ
  12. データパイプラインと外部連携
  13. セキュリティとガバナンス
  14. 運用のベストプラクティス
  15. トラブルシューティング
  16. まとめ

1. はじめに

1.1 本書の目的

本書は、LaunchDarklyのA/B分析(エクスペリメンテーション)機能を中心に、フィーチャーフラグプラットフォームとしてのLaunchDarklyの全体像を技術的に深く掘り下げた解説書である。単なる機能紹介にとどまらず、アーキテクチャ設計、統計手法、SDK統合、運用パターンまでを網羅し、エンジニアリングチームがLaunchDarklyを活用したデータドリブンな意思決定基盤を構築するための実践的な知識を提供する。

1.2 対象読者

  • ソフトウェアエンジニア(バックエンド/フロントエンド)
  • SRE / プラットフォームエンジニア
  • プロダクトマネージャー(技術的背景を持つ方)
  • データエンジニア / データサイエンティスト
  • テクニカルリード / アーキテクト

1.3 フィーチャーフラグとA/Bテストの関係

フィーチャーフラグ(Feature Flag)は、コードのデプロイとリリースを分離する技術であり、A/Bテストはユーザーに異なるバリエーションを提示し、その影響を統計的に評価する手法である。LaunchDarklyはこの2つを統合し、フィーチャーフラグの基盤上でA/Bテストを実行できるプラットフォームを提供している。

従来のA/Bテストツール(Google Optimize、Optimizelyなど)が主にフロントエンドのUI変更に焦点を当てていたのに対し、LaunchDarklyはバックエンドロジック、アルゴリズム、インフラ構成の変更まで含めた広範なエクスペリメンテーションを可能にする点が大きな差別化要因である。

従来のA/Bテスト:
  UI変更 → フロントエンドツール → コンバージョン測定

LaunchDarklyのアプローチ:
  あらゆるコード変更 → フィーチャーフラグ → メトリクス測定 → 統計分析

2. LaunchDarklyの概要

2.1 プラットフォームの位置づけ

LaunchDarklyは、2014年に設立されたフィーチャーマネジメントプラットフォームのリーディングカンパニーである。単なるフィーチャーフラグのON/OFF管理にとどまらず、以下の包括的な機能を提供する。

カテゴリ機能説明
フィーチャーマネジメントフラグ管理ブール値、マルチバリエート、JSON型フラグ
ターゲティングユーザー属性ベースの配信制御
段階的ロールアウトパーセンテージベースの展開
エクスペリメンテーションA/B/nテスト複数バリエーションの統計比較
メトリクス管理カスタムイベント、ファネル分析
統計エンジン逐次テスト、ベイズ推定
運用監査ログすべてのフラグ変更の追跡
ワークフロー承認フロー、スケジュール変更
インテグレーションSlack、Datadog、PagerDutyなど

2.2 コアコンポーネント

LaunchDarklyのプラットフォームは、以下のコアコンポーネントで構成される。

+-------------------------------------------------------------------+
|                    LaunchDarkly Platform                           |
|                                                                   |
|  +------------------+  +------------------+  +------------------+ |
|  |   Flag Engine    |  |  Experimentation |  |   Data Pipeline  | |
|  |                  |  |     Engine       |  |                  | |
|  |  - Evaluation    |  |  - Allocation    |  |  - Event Stream  | |
|  |  - Targeting     |  |  - Statistics    |  |  - Analytics     | |
|  |  - Scheduling    |  |  - Metrics       |  |  - Export        | |
|  +--------+---------+  +--------+---------+  +--------+---------+ |
|           |                      |                      |         |
|  +--------v----------------------v----------------------v-------+ |
|  |              Unified Event Bus / Streaming Layer              | |
|  +--------------------------------------------------------------+ |
|           |                      |                      |         |
|  +--------v---------+  +--------v---------+  +--------v--------+ |
|  |  Server-side SDK |  | Client-side SDK  |  |   Relay Proxy   | |
|  +------------------+  +------------------+  +-----------------+ |
+-------------------------------------------------------------------+

2.3 プロジェクトと環境の概念

LaunchDarklyでは、リソースを以下の階層で管理する。

Organization(組織)
  └── Project(プロジェクト)
        ├── Environment: Production
        ├── Environment: Staging
        ├── Environment: Development
        └── Environment: QA

各環境は独立したSDKキー、フラグ状態、メトリクスデータを持つ。エクスペリメンテーションも環境ごとに個別に実行・管理される。

設定例: プロジェクトと環境の構成

{
  "project": {
    "key": "payment-service",
    "name": "Payment Service",
    "environments": [
      {
        "key": "production",
        "name": "Production",
        "color": "FF0000",
        "sdkKey": "sdk-prod-xxxxxxxx",
        "mobileKey": "mob-prod-xxxxxxxx",
        "clientSideId": "client-prod-xxxxxxxx"
      },
      {
        "key": "staging",
        "name": "Staging",
        "color": "FFAA00",
        "sdkKey": "sdk-stg-xxxxxxxx"
      },
      {
        "key": "development",
        "name": "Development",
        "color": "00FF00",
        "sdkKey": "sdk-dev-xxxxxxxx"
      }
    ]
  }
}

2.4 ライセンスモデルとエクスペリメンテーション

LaunchDarklyのエクスペリメンテーション機能は、Enterprise プラン以上で利用可能なアドオンとして提供される(2025年時点)。ライセンスモデルは以下の通り。

プランフィーチャーフラグエクスペリメンテーション高度な分析
Starter基本機能非対応非対応
Pro全機能基本機能一部対応
Enterprise全機能全機能全機能

エクスペリメンテーション機能の利用には、Monthly Experimentation Key(MEK)というメトリクスが使用され、実験に参加したユニークユーザー数に基づいて課金される。


3. フィーチャーフラグの基本概念

3.1 フラグの種類

LaunchDarklyは以下の4種類のフラグタイプを提供する。

3.1.1 ブールフラグ(Boolean Flag)

最もシンプルなON/OFF型のフラグ。機能の有効化/無効化に使用する。

{
  "key": "enable-new-checkout",
  "name": "Enable New Checkout Flow",
  "kind": "boolean",
  "variations": [
    { "value": true, "name": "Enabled", "description": "新しいチェックアウトフローを有効化" },
    { "value": false, "name": "Disabled", "description": "既存のチェックアウトフローを使用" }
  ],
  "defaults": {
    "onVariation": 0,
    "offVariation": 1
  }
}

3.1.2 マルチバリエートフラグ(Multivariate Flag)

3つ以上のバリエーションを持つフラグ。A/B/nテストに最適。

{
  "key": "checkout-button-color",
  "name": "Checkout Button Color Experiment",
  "kind": "multivariate",
  "variations": [
    { "value": "blue", "name": "Control - Blue" },
    { "value": "green", "name": "Variant A - Green" },
    { "value": "orange", "name": "Variant B - Orange" },
    { "value": "red", "name": "Variant C - Red" }
  ]
}

3.1.3 数値フラグ(Number Flag)

数値パラメータの最適化に使用する。

{
  "key": "api-rate-limit",
  "name": "API Rate Limit Experiment",
  "kind": "number",
  "variations": [
    { "value": 100, "name": "Conservative" },
    { "value": 500, "name": "Moderate" },
    { "value": 1000, "name": "Aggressive" }
  ]
}

3.1.4 JSONフラグ(JSON Flag)

複雑な設定オブジェクトをフラグとして管理する。

{
  "key": "recommendation-config",
  "name": "Recommendation Algorithm Config",
  "kind": "json",
  "variations": [
    {
      "value": {
        "algorithm": "collaborative-filtering",
        "numRecommendations": 10,
        "minScore": 0.7,
        "includePopular": true
      },
      "name": "Collaborative Filtering"
    },
    {
      "value": {
        "algorithm": "content-based",
        "numRecommendations": 8,
        "minScore": 0.5,
        "includePopular": false
      },
      "name": "Content-Based"
    }
  ]
}

3.2 フラグの評価ロジック

フラグの評価は以下の優先順位で行われる。

評価フロー:
  1. フラグが OFF → offVariation を返す
  2. 個別ターゲティング(ユーザー指定)に該当 → 指定されたバリエーションを返す
  3. ターゲティングルール(条件ベース)に該当 → ルールに基づくバリエーションを返す
  4. フォールスルー(デフォルト)→ デフォルトバリエーションを返す

評価フローの詳細図:

                    +------------------+
                    |   Flag Request   |
                    | (user + context) |
                    +--------+---------+
                             |
                    +--------v---------+
                    |  Is Flag ON?     |
                    +--------+---------+
                        |          |
                       No         Yes
                        |          |
               +--------v--+  +----v--------------+
               | Return     |  | Check Individual  |
               | offVariation|  | Targeting         |
               +------------+  +----+--------------+
                                    |          |
                                  Match     No Match
                                    |          |
                          +---------v--+  +----v--------------+
                          | Return     |  | Evaluate Rules    |
                          | Specified  |  | (top to bottom)   |
                          | Variation  |  +----+--------------+
                          +------------+       |          |
                                             Match     No Match
                                               |          |
                                     +---------v--+  +----v--------------+
                                     | Return     |  | Return Fallthrough|
                                     | Rule       |  | (default %        |
                                     | Variation  |  |  rollout)         |
                                     +------------+  +-----------------+

3.3 コンテキスト(Context)

LaunchDarkly v6以降では、ユーザーの概念が「コンテキスト」に拡張された。これにより、ユーザーだけでなく、組織、デバイス、アプリケーションなど複数のエンティティに対してフラグ評価を行える。

{
  "kind": "multi",
  "user": {
    "key": "user-123",
    "name": "Taro Yamada",
    "email": "taro@example.com",
    "custom": {
      "plan": "enterprise",
      "company": "Acme Corp",
      "signupDate": "2024-01-15"
    }
  },
  "organization": {
    "key": "org-456",
    "name": "Acme Corp",
    "custom": {
      "industry": "technology",
      "employeeCount": 500,
      "tier": "premium"
    }
  },
  "device": {
    "key": "device-789",
    "custom": {
      "os": "iOS",
      "version": "17.4",
      "model": "iPhone 15 Pro"
    }
  }
}

4. A/Bテストとエクスペリメンテーション

4.1 エクスペリメンテーションの概要

LaunchDarklyのエクスペリメンテーション(Experimentation)は、フィーチャーフラグの上に構築されたA/Bテストフレームワークである。以下の特徴を持つ。

  • フィーチャーフラグネイティブ: 既存のフラグ基盤上で実験を実行
  • サーバーサイド対応: UIだけでなくバックエンドロジックの実験が可能
  • 逐次テスト: 固定サンプルサイズ不要、リアルタイムで結果を評価
  • 複数メトリクス同時評価: 1つの実験で複数のKPIを同時に追跡
  • 自動サンプルサイズ計算: 統計的に有意な結果を得るための推奨サイズ

4.2 実験の構成要素

Experiment(実験)
  ├── Flag(フラグ): どのフラグを実験するか
  │     └── Variations(バリエーション): 比較する選択肢
  ├── Metrics(メトリクス): 何を測定するか
  │     ├── Primary Metric: 主要評価指標
  │     └── Secondary Metrics: 副次評価指標
  ├── Audience(オーディエンス): 誰を対象にするか
  │     ├── Traffic Allocation: トラフィック配分率
  │     └── Targeting Rules: ターゲティング条件
  └── Duration(期間): いつまで実行するか
        ├── Minimum Runtime: 最小実行期間
        └── Stopping Criteria: 停止条件

4.3 実験のライフサイクル

Draft(下書き)
  │
  ├── 実験設計、メトリクス設定、オーディエンス定義
  │
  v
Recording(記録中)
  │
  ├── データ収集開始、バリエーション配信開始
  │
  v
Running / Analyzing(実行中 / 分析中)
  │
  ├── 統計分析実行、有意差の検出
  │
  v
Winning Variation Identified(勝者バリエーション特定)
  │
  ├── 統計的に有意な差を確認
  │
  v
Stopped(停止)
  │
  ├── 勝者バリエーションを100%展開
  │   または実験中止
  │
  v
Archived(アーカイブ)
    │
    └── 実験結果の保存、フラグのクリーンアップ

4.4 実験の種類

4.4.1 機能実験(Feature Experiment)

新機能のON/OFFによるビジネスインパクトを測定する。

実験名: "New Recommendation Engine Impact"
フラグ: enable-new-recommendations
バリエーション:
  - Control (OFF): 既存のレコメンドエンジン
  - Treatment (ON): 新しいMLベースレコメンドエンジン
メトリクス:
  - Primary: click_through_rate(クリック率)
  - Secondary: average_order_value(平均注文額)
  - Secondary: page_load_time(ページ読み込み時間)

4.4.2 最適化実験(Optimization Experiment)

パラメータの最適値を見つける。

実験名: "Optimal Search Results Count"
フラグ: search-results-per-page
バリエーション:
  - 10件: 従来の表示件数
  - 20件: 中間オプション
  - 50件: 大量表示オプション
メトリクス:
  - Primary: search_satisfaction_score
  - Secondary: time_on_page
  - Secondary: bounce_rate

4.4.3 コンフィグ実験(Configuration Experiment)

システムの設定パラメータを最適化する。

実験名: "Cache TTL Optimization"
フラグ: cache-ttl-seconds
バリエーション:
  - 300 (5分): 短いTTL
  - 1800 (30分): 中間TTL
  - 3600 (1時間): 長いTTL
メトリクス:
  - Primary: cache_hit_rate
  - Secondary: p99_response_time
  - Secondary: origin_server_load

4.5 実験設定の具体例(API / UI)

LaunchDarkly API を使用した実験作成

curl -X POST \
  'https://app.launchdarkly.com/api/v2/projects/payment-service/environments/production/experiments' \
  -H 'Authorization: api-xxxxxxxx' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Checkout Flow Optimization",
    "description": "新しいチェックアウトフローのコンバージョン率への影響を測定",
    "maintainerId": "user-id-xxx",
    "key": "checkout-flow-optimization",
    "iteration": {
      "hypothesis": "ステップ数を削減したチェックアウトフローにより、コンバージョン率が5%以上向上する",
      "canReshuffleTraffic": true,
      "metrics": [
        { "key": "checkout-conversion-rate", "isGroup": false },
        { "key": "average-order-value", "isGroup": false },
        { "key": "checkout-abandonment-rate", "isGroup": false }
      ],
      "primarySingleMetricKey": "checkout-conversion-rate",
      "treatments": [
        {
          "name": "Control - Current Checkout",
          "baseline": true,
          "allocationPercent": "34",
          "parameters": [
            { "flagKey": "checkout-flow-version", "variationId": "variation-current" }
          ]
        },
        {
          "name": "Treatment A - Simplified",
          "baseline": false,
          "allocationPercent": "33",
          "parameters": [
            { "flagKey": "checkout-flow-version", "variationId": "variation-simplified" }
          ]
        },
        {
          "name": "Treatment B - One-Page",
          "baseline": false,
          "allocationPercent": "33",
          "parameters": [
            { "flagKey": "checkout-flow-version", "variationId": "variation-one-page" }
          ]
        }
      ],
      "flags": {
        "checkout-flow-version": {
          "ruleId": "rule-id-xxx",
          "flagConfigVersion": 12
        }
      },
      "randomizationUnit": "user"
    }
  }'

5. アーキテクチャの全体像

5.1 システムアーキテクチャ

LaunchDarklyのアーキテクチャは、高可用性・低レイテンシを実現するために、以下の主要コンポーネントで構成されている。

                           +-----------------------------+
                           |     LaunchDarkly Cloud       |
                           |                             |
                           |  +------------------------+ |
                           |  |   Management API        | |
                           |  |   (REST API v2)         | |
                           |  +----------+-------------+ |
                           |             |               |
                           |  +----------v-------------+ |
                           |  |   Flag Configuration    | |
                           |  |   Store                 | |
                           |  +----------+-------------+ |
                           |             |               |
                           |  +----------v-------------+ |
                           |  |   Streaming Service     | |
                           |  |   (Server-Sent Events)  | |
                           |  +----------+-------------+ |
                           |             |               |
                           |  +----------v-------------+ |
                           |  |   Events Pipeline       | |
                           |  |   (Ingestion/Analysis)  | |
                           |  +------------------------+ |
                           +--------------+--------------+
                                          |
              +---------------------------+---------------------------+
              |                           |                           |
    +---------v----------+    +-----------v----------+   +----------v----------+
    |  Server-side SDK   |    |   Client-side SDK    |   |    Relay Proxy      |
    |  - Node.js         |    |  - JavaScript        |   |  - On-premise      |
    |  - Python          |    |  - React             |   |  - Flag cache      |
    |  - Java / Go       |    |  - iOS / Android     |   |  - Event proxy     |
    |  - .NET / Ruby     |    |  - Flutter / RN      |   |  - HA deployment   |
    +--------------------+    +----------------------+   +---------------------+

5.2 ストリーミングアーキテクチャ

LaunchDarklyは、フラグ変更をリアルタイムでクライアントに配信するために、Server-Sent Events(SSE)ベースのストリーミングアーキテクチャを採用している。

フラグ変更時のデータフロー:

  管理者がフラグを変更
        |
        v
  Management API  -- フラグ設定をストアに書き込み
        |
        v
  Configuration Change Stream  -- 変更を検知
        |
        v
  SSE Streaming Service  -- 接続中の全SDKにプッシュ
        |
     +--+--+
     |  |  |
     v  v  v
   SDK1 SDK2 SDK3  -- 各SDKがローカルのフラグ状態を更新
     |  |  |
     v  v  v
   即座にフラグ評価に反映(ミリ秒レベル)

ストリーミングとポーリングの比較:

特性ストリーミング (SSE)ポーリング
レイテンシミリ秒レベルポーリング間隔に依存(通常30秒)
帯域使用量変更時のみデータ送信定期的にフル状態を取得
接続維持長期接続リクエストごとに接続
フォールバックポーリングにフォールバック-
推奨用途デフォルトネットワーク制約がある場合

5.3 フラグ評価のアーキテクチャ

サーバーサイドSDKでのフラグ評価は完全にローカルで行われる。これは、外部API呼び出しが不要であることを意味し、極めて低いレイテンシ(マイクロ秒レベル)を実現する。

サーバーサイドSDKのフラグ評価フロー:

  +----------------------------------------------+
  |              Server-side SDK                  |
  |                                              |
  |  +----------------------------------------+  |
  |  |         In-Memory Flag Store           |  |
  |  |  Flag A: { rules: [...], fallthrough } |  |
  |  |  Flag B: { rules: [...], fallthrough } |  |
  |  +------------------+---------------------+  |
  |                     |                        |
  |  +------------------v---------------------+  |
  |  |         Evaluation Engine              |  |
  |  |  1. Prerequisites Check               |  |
  |  |  2. Individual Targeting              |  |
  |  |  3. Rule Evaluation                   |  |
  |  |  4. Percentage Rollout (Hash)         |  |
  |  |  5. Fallthrough                       |  |
  |  +------------------+---------------------+  |
  |                     |                        |
  |  +------------------v---------------------+  |
  |  |         Event Buffer                   |  |
  |  |  Evaluation events -> batch送信        |  |
  |  |  (非同期、フラグ評価をブロックしない)    |  |
  |  +----------------------------------------+  |
  +----------------------------------------------+

クライアントサイドSDKとの違い:

特性サーバーサイドSDKクライアントサイドSDK
フラグデータ全フラグの完全データ特定ユーザー向けの評価済み値のみ
評価場所SDK内(ローカル)LaunchDarklyサーバー
セキュリティSDKキーは秘密クライアントIDは公開可
初期化全フラグデータのダウンロード該当ユーザーの評価結果のみ受信
適用場面バックエンド、マイクロサービスブラウザ、モバイルアプリ

5.4 パーセンテージロールアウトのハッシュアルゴリズム

LaunchDarklyのパーセンテージロールアウト(トラフィック配分)は、決定論的なハッシュアルゴリズムに基づいている。これにより、同じユーザーは常に同じバリエーションを受け取る(一貫性の保証)。

# LaunchDarklyのバケット配分アルゴリズム(概念的な擬似コード)
import hashlib

def bucket_user(flag_key: str, user_key: str, salt: str) -> float:
    """ユーザーを0.0-1.0の範囲のバケットに配分する。"""
    hash_input = f"{flag_key}.{salt}.{user_key}"
    hash_value = hashlib.sha1(hash_input.encode()).hexdigest()[:15]
    int_value = int(hash_value, 16)
    bucket = int_value / 0xFFFFFFFFFFFFFFF
    return bucket

def evaluate_rollout(flag_key, user_key, salt, variations, weights):
    """ロールアウト設定に基づいてバリエーションを割り当てる。"""
    bucket = bucket_user(flag_key, user_key, salt)
    cumulative = 0.0
    for i, weight in enumerate(weights):
        cumulative += weight / 100000.0
        if bucket < cumulative:
            return variations[i]
    return variations[-1]

ハッシュの重要な性質:

  1. 決定論性: 同じ入力に対して常に同じバケット値を返す
  2. 均一分布: ハッシュ値が均一に分布するため、配分が偏らない
  3. 独立性: 異なるフラグ間でのバケット配分は独立している(ソルトが異なるため)
  4. 再シャッフル: ソルトを変更することで、トラフィック配分を再シャッフルできる

5.5 イベントパイプラインのアーキテクチャ

イベントのフロー:

  SDK (Evaluation/Custom Events)
        |  バッチ送信(デフォルト: 5秒間隔)
        v
  Event Ingestion Service
  (受信/検証/重複排除/ストリーム書き込み)
        |
     +--+--+
     |  |  |
     v  v  v
  Real-time   Batch      Data Export
  Analysis    Analysis   Pipeline
     |          |            |
     v          v            v
  実験ダッシュ  統計レポート  外部データウェアハウス
  ボード

6. SDK統合とデータフロー

6.1 サーバーサイドSDKの統合

6.1.1 Node.js SDK

const LaunchDarkly = require('@launchdarkly/node-server-sdk');

const client = LaunchDarkly.init('sdk-key-xxxxxxxx', {
  stream: true,
  streamUri: 'https://stream.launchdarkly.com',
  eventsUri: 'https://events.launchdarkly.com',
  flushInterval: 5,
  allAttributesPrivate: false,
  privateAttributes: ['email', 'phone'],
  featureStore: LaunchDarkly.integrations.Redis({
    redisOpts: { host: 'redis.example.com', port: 6379 },
    prefix: 'launchdarkly',
    cacheTTL: 30
  }),
  diagnosticOptOut: false,
  diagnosticRecordingInterval: 900
});

await client.waitForInitialization({ timeout: 10 });

const context = {
  kind: 'multi',
  user: {
    key: 'user-123',
    name: 'Taro Yamada',
    email: 'taro@example.com',
    custom: { plan: 'enterprise', country: 'JP' }
  },
  organization: {
    key: 'org-456',
    name: 'Acme Corp',
    custom: { tier: 'premium' }
  }
};

// フラグの評価
const checkoutVersion = await client.variation('checkout-flow-version', context, 'current');

// 評価の詳細情報を取得
const detail = await client.variationDetail('checkout-flow-version', context, 'current');
console.log('Value:', detail.value);
console.log('Variation Index:', detail.variationIndex);
console.log('Reason:', detail.reason);

// カスタムイベントの送信(メトリクス計測用)
client.track('checkout-completed', context, {
  orderId: 'order-789', amount: 15000, currency: 'JPY', items: 3
}, 15000);

// 数値メトリクスの送信
client.track('page-load-time', context, null, 1250);

// グレースフルシャットダウン
process.on('SIGTERM', async () => {
  await client.flush();
  await client.close();
  process.exit(0);
});

6.1.2 Python SDK

import ldclient
from ldclient import Context
from ldclient.config import Config

config = Config(
    sdk_key='sdk-key-xxxxxxxx',
    http=ldclient.config.HTTPConfig(connect_timeout=10, read_timeout=15),
    events=ldclient.config.EventsConfig(
        capacity=10000, flush_interval=5,
        private_attributes=['email', 'ssn']
    ),
    stream=True, send_events=True
)
ldclient.set_config(config)
client = ldclient.get()

multi_context = Context.create_multi(
    Context.builder("user-123").kind("user").name("Taro").set("plan", "enterprise").build(),
    Context.builder("org-456").kind("organization").set("tier", "premium").build()
)

recommendation_config = client.variation('recommendation-config', multi_context,
    {"algorithm": "default", "numRecommendations": 5})

client.track('recommendation-click', multi_context, metric_value=1)
client.track('purchase-amount', multi_context, metric_value=12500)

6.1.3 Go SDK

package main

import (
    "fmt"
    "time"
    ld "github.com/launchdarkly/go-server-sdk/v7"
    "github.com/launchdarkly/go-server-sdk/v7/ldcomponents"
    ldcontext "github.com/launchdarkly/go-sdk-common/v3/ldcontext"
    "github.com/launchdarkly/go-sdk-common/v3/ldvalue"
)

func main() {
    config := ld.Config{
        Events: ldcomponents.SendEvents().Capacity(10000).FlushInterval(5 * time.Second),
        DataSource: ldcomponents.StreamingDataSource().InitialReconnectDelay(1 * time.Second),
    }

    client, _ := ld.MakeCustomClient("sdk-key-xxxxxxxx", config, 10*time.Second)
    defer client.Close()

    multiContext := ldcontext.NewMultiBuilder().
        Add(ldcontext.NewBuilder("user-123").Kind("user").SetString("plan", "enterprise").Build()).
        Add(ldcontext.NewBuilder("org-456").Kind("organization").SetString("tier", "premium").Build()).
        Build()

    value, _ := client.JSONVariation("recommendation-config", multiContext,
        ldvalue.ObjectBuild().SetString("algorithm", "default").SetInt("numRecommendations", 5).Build())

    fmt.Printf("Algorithm: %s\n", value.GetByKey("algorithm").StringValue())
    client.TrackMetric("checkout-completed", multiContext, ldvalue.Null(), 15000)
}

6.2 クライアントサイドSDKの統合

6.2.1 React SDK

import { LDProvider, useFlags, useLDClient } from 'launchdarkly-react-client-sdk';

function App() {
  return (
    <LDProvider
      clientSideID="client-id-xxxxxxxx"
      context={{ kind: 'user', key: 'user-123', name: 'Taro', custom: { plan: 'enterprise' } }}
      options={{ streaming: true, bootstrap: 'localStorage' }}
    >
      <CheckoutPage />
    </LDProvider>
  );
}

function CheckoutPage() {
  const flags = useFlags();
  const ldClient = useLDClient();
  const checkoutVersion = flags['checkout-flow-version'] || 'current';

  const handleCheckoutComplete = (orderAmount) => {
    ldClient.track('checkout-completed', { orderId: 'order-xxx' }, orderAmount);
  };

  switch (checkoutVersion) {
    case 'simplified': return <SimplifiedCheckout onComplete={handleCheckoutComplete} />;
    case 'one-page':   return <OnePageCheckout onComplete={handleCheckoutComplete} />;
    default:           return <CurrentCheckout onComplete={handleCheckoutComplete} />;
  }
}

6.2.2 iOS SDK (Swift)

import LaunchDarkly

// SDK設定
var config = LDConfig(mobileKey: "mob-key-xxxxxxxx", autoEnvAttributes: .enabled)
config.streamingMode = .streaming
config.eventFlushInterval = 5.0

var userBuilder = LDContextBuilder(key: "user-123")
userBuilder.kind("user")
userBuilder.trySetValue("plan", .string("enterprise"))
let context = try! userBuilder.build().get()

LDClient.start(config: config, context: context) {
    print("LaunchDarkly SDK initialized")
}

// フラグ評価
let client = LDClient.get()!
let version = client.variation(forKey: "checkout-flow-version", defaultValue: "current") as String

// メトリクスの送信
try? client.track(key: "checkout-completed", metricValue: 15000)

7. メトリクスとイベント設計

7.1 メトリクスの種類

7.1.1 コンバージョンメトリクス(Conversion Metrics)

バイナリイベント(発生/非発生)を測定する。コンバージョン率として算出される。

{
  "key": "checkout-completed",
  "name": "Checkout Completed",
  "kind": "custom",
  "eventKey": "checkout-completed",
  "isNumeric": false,
  "successCriteria": "HigherThanBaseline"
}

7.1.2 数値メトリクス(Numeric Metrics)

連続値を測定する。平均値、中央値などの統計量で評価される。

{
  "key": "page-load-time",
  "name": "Page Load Time (ms)",
  "kind": "custom",
  "eventKey": "page-load-time",
  "isNumeric": true,
  "unit": "ms",
  "successCriteria": "LowerThanBaseline"
}
client.track('page-load-time', context, null, 1250);       // 1250ms
client.track('purchase-amount', context, null, 15000);      // 15000円
client.track('api-response-time', context, null, 45.3);     // 45.3ms

7.1.3 ファネルメトリクス(Funnel Metrics)

複数のステップからなるユーザージャーニーの完了率を測定する。

ファネル: チェックアウトプロセス
  Step 1: カート閲覧     --- cart-viewed
  Step 2: 配送先入力     --- shipping-entered
  Step 3: 支払い情報入力  --- payment-entered
  Step 4: 注文確認       --- order-confirmed
  Step 5: 注文完了       --- order-completed

7.2 メトリクスの設計原則

良い設計:
  Primary: checkout_conversion_rate(主要KPI)
  Secondary: average_order_value(収益影響)
  Secondary: checkout_time(UX品質)
  Guardrail: error_rate(品質保証)
  Guardrail: page_load_time(パフォーマンス保証)

7.3 イベント設計のベストプラクティス

// 命名規約: {domain}-{action}-{detail}
client.track('checkout-completed', context, null, orderAmount);       // 良い
client.track('search-results-viewed', context, { query }, resultCount); // 良い
client.track('click', context);          // 悪い - 何のクリックか不明
client.track('event1', context);         // 悪い - 意味が不明

8. 統計エンジンと分析手法

8.1 統計手法の概要

手法用途特徴
逐次テスト(Sequential Testing)リアルタイム分析固定サンプルサイズ不要
頻度論的検定(Frequentist)信頼区間の提示従来のA/Bテスト手法
ベイズ推定(Bayesian)確率的な意思決定直感的な確率表現
CUPED分散削減実験前データを活用

8.2 逐次テスト(Sequential Testing)

LaunchDarklyの主要な統計手法は逐次テストである。従来の固定水平線テスト(Fixed-Horizon Test)では、事前にサンプルサイズを決定し、そのサンプルが集まるまで結果を見てはならないという制約があった。逐次テストはこの制約を取り除く。

固定水平線テスト:
  [データ収集期間 (途中で見てはいけない)] -> 結果評価(1回のみ)

逐次テスト:
  [データ収集期間]
   ^  ^  ^  ^  ^  ^  ^
  評価 評価 評価 評価 ... (いつでも結果を確認可能)

LaunchDarklyは、Always Valid Inference(AVI)フレームワークに基づく逐次テストを実装しており、任意の時点で有効な信頼区間を提供する。

信頼区間の計算(概念的な数式):

  通常の信頼区間:      CI = x_bar +/- z * sigma / sqrt(n)
  逐次テスト(AVI):   CI_t = x_bar_t +/- sqrt(2 * sigma^2 * (log(log(n_t)) + C) / n_t)

8.3 ベイズ推定

ベイズ推定の出力例:

  Treatment A vs Control:
    Probability of Being Best:   92.3%
    Expected Improvement:        +4.2%
    Credible Interval (95%):     [+1.1%, +7.3%]
    Risk (Expected Loss):        0.15%

  Treatment B vs Control:
    Probability of Being Best:   5.1%
    Expected Improvement:        -1.8%
    Credible Interval (95%):     [-4.2%, +0.6%]
    Risk (Expected Loss):        2.34%

8.4 CUPED(Controlled-experiment Using Pre-Experiment Data)

CUPEDは、実験前のデータを活用して分散を削減する手法である。

CUPEDの原理:
  Y_cuped = Y - theta * (X - E[X])
  Var(Y_cuped) = Var(Y) * (1 - rho^2)

分散削減率:
  rho = 0.3 -> 9%削減
  rho = 0.5 -> 25%削減
  rho = 0.7 -> 51%削減
  rho = 0.9 -> 81%削減

8.5 多重比較補正

LaunchDarklyは、バリエーション間の比較にはDunnett補正、メトリクス間にはBenjamini-Hochberg法(FDR制御)を自動的に適用する。

8.6 サンプルサイズ計算

例:
  ベースライン: 3.0%  期待改善: 3.3% (相対10%)  有意水準: 5%  検出力: 80%
  n = 約44,600 ユーザー/バリエーション
  2バリエーション合計: 約89,200ユーザー
  1日10,000ユーザーの場合: 約9日間

8.7 実験結果の解釈

実験結果ダッシュボード:

 Experiment: Checkout Flow Optimization
 Status: Running (Day 12 of minimum 14)
 Total Participants: 87,432

 PRIMARY METRIC: Checkout Conversion Rate

 Variation          | Users  | Conv. Rate | Change  | CI (95%)
 -------------------|--------|------------|---------|------------------
 Control (Current)  | 29,144 | 3.02%      | base    | -
 Simplified [win]   | 29,156 | 3.28%      | +8.6%   | [+2.1%,+15.3%]
 One-Page           | 29,132 | 3.09%      | +2.3%   | [-4.0%,+8.9%]

 GUARDRAIL METRIC: Error Rate

 Variation          | Error Rate | Change  | Status
 -------------------|------------|---------|--------
 Control (Current)  | 0.12%      | base    | -
 Simplified         | 0.11%      | -8.3%   | Safe
 One-Page           | 0.18%      | +50.0%  | Warning

9. ターゲティングとセグメンテーション

9.1 ターゲティングルールの構造

{
  "flagKey": "checkout-flow-version",
  "on": true,
  "targets": [
    { "contextKind": "user", "values": ["user-001", "user-002"], "variation": 1 }
  ],
  "rules": [
    {
      "id": "rule-1",
      "description": "Premium users in Japan",
      "clauses": [
        { "contextKind": "user", "attribute": "country", "op": "in", "values": ["JP"] },
        { "contextKind": "organization", "attribute": "tier", "op": "in", "values": ["premium", "enterprise"] }
      ],
      "variation": 1,
      "trackEvents": true
    },
    {
      "id": "rule-2",
      "description": "50/50 rollout for US users",
      "clauses": [
        { "contextKind": "user", "attribute": "country", "op": "in", "values": ["US"] }
      ],
      "rollout": {
        "variations": [
          { "variation": 0, "weight": 50000 },
          { "variation": 1, "weight": 50000 }
        ],
        "bucketBy": "key",
        "contextKind": "user"
      }
    }
  ],
  "fallthrough": {
    "rollout": {
      "variations": [
        { "variation": 0, "weight": 90000 },
        { "variation": 1, "weight": 10000 }
      ]
    }
  },
  "offVariation": 0
}

9.2 演算子(Operators)

演算子説明
in値がリストに含まれるcountry in ["JP", "US"]
endsWith文字列が特定の値で終わるemail endsWith "@example.com"
startsWith文字列が特定の値で始まるname startsWith "test-"
matches正規表現にマッチemail matches "^.*@corp\.com$"
contains文字列が含まれるname contains "admin"
lessThan / greaterThan数値比較age lessThan 18
before / after日時比較createdAt after "2024-06-01T00:00:00Z"
semVerEqual / semVerLessThan / semVerGreaterThanバージョン比較appVersion semVerGreaterThan "1.5.0"
segmentMatchセグメントに属するsegmentMatch "beta-users"

9.3 トラフィック配分と相互排他的実験

全トラフィック (100%)
  |-- 実験対象 (20%)
  |     |-- Control (50% of 20% = 10%)
  |     |-- Treatment A (25% of 20% = 5%)
  |     +-- Treatment B (25% of 20% = 5%)
  +-- 実験非対象 (80%) -> デフォルト

相互排他グループ:
  |-- 実験A用 (30%): バケット 0-30%
  |-- 実験B用 (30%): バケット 30-60%
  +-- 未割当 (40%): バケット 60-100%

10. 実装パターンと具体例

10.1 バックエンドA/Bテスト: レコメンデーションアルゴリズム

class RecommendationService:
    def __init__(self):
        self.ld_client = ldclient.get()
        self.algorithms = {
            'collaborative-filtering': CollaborativeFilteringEngine(),
            'content-based': ContentBasedEngine(),
            'hybrid-ml': HybridMLEngine()
        }

    def get_recommendations(self, user_id, user_attrs, num_items=10):
        context = Context.builder(user_id).kind("user") \
            .set("plan", user_attrs.get("plan", "free")).build()

        config = self.ld_client.json_variation('recommendation-config', context,
            {"algorithm": "collaborative-filtering", "numRecommendations": 10})

        start_time = time.time()
        engine = self.algorithms[config["algorithm"]]
        recommendations = engine.generate(user_id, config["numRecommendations"])
        latency_ms = (time.time() - start_time) * 1000

        self.ld_client.track('recommendation-latency', context, metric_value=latency_ms)
        return recommendations

10.2 マイクロサービスでのコンテキスト伝播

HTTP Headers:
  X-LaunchDarkly-User-Key: user-123
  X-LaunchDarkly-User-Plan: enterprise
  X-LaunchDarkly-Org-Key: org-456
  X-LaunchDarkly-Org-Tier: premium
@app.before_request
def extract_ld_context():
    user_key = request.headers.get('X-LaunchDarkly-User-Key')
    if user_key:
        g.ld_context = Context.builder(user_key).kind("user") \
            .set("plan", request.headers.get('X-LaunchDarkly-User-Plan', 'free')).build()

11. Relay Proxyとエンタープライズアーキテクチャ

11.1 Relay Proxyの概要

Relay Proxyは、SDKとLaunchDarklyクラウドの間に配置されるプロキシサーバーである。

  • 接続数の削減: 多数のSDKインスタンスからの接続を集約
  • レイテンシの削減: フラグデータをローカルにキャッシュ
  • セキュリティ: SDKがインターネットに直接アクセス不要
  • 高可用性: LaunchDarklyサービスの障害からの保護

11.2 Relay Proxyの設定

# relay-proxy-config.yaml
main:
  port: 8030
  metricsPort: 8031
  logLevel: "info"

environment:
  production:
    sdkKey: "sdk-prod-xxxxxxxx"
    mobileKey: "mob-prod-xxxxxxxx"

redis:
  host: "redis.example.com"
  port: 6379
  localTtl: 30000
  prefix: "ld-relay"
  tls: true
# Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ld-relay-proxy
  namespace: feature-flags
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: ld-relay
          image: launchdarkly/ld-relay:8
          ports:
            - containerPort: 8030
            - containerPort: 8031
          resources:
            requests: { cpu: "250m", memory: "256Mi" }
            limits: { cpu: "1000m", memory: "512Mi" }
          livenessProbe:
            httpGet: { path: /status, port: 8030 }

11.3 SDK からの Relay Proxy 接続設定

const client = LaunchDarkly.init('sdk-key-xxxxxxxx', {
  streamUri: 'http://ld-relay-proxy.feature-flags.svc.cluster.local:8030',
  baseUri: 'http://ld-relay-proxy.feature-flags.svc.cluster.local:8030',
  eventsUri: 'http://ld-relay-proxy.feature-flags.svc.cluster.local:8030'
});

12. データパイプラインと外部連携

12.1 Data Export

LaunchDarklyは、Kinesis Firehose、Google Pub/Sub、Segment、mParticle等への イベントデータエクスポートをサポートする。

12.2 エクスポートイベントのスキーマ

{
  "kind": "feature",
  "creationDate": 1709827200000,
  "key": "checkout-flow-version",
  "context": { "kind": "user", "key": "user-123" },
  "variation": 1,
  "value": "simplified",
  "reason": { "kind": "RULE_MATCH", "ruleIndex": 0, "inExperiment": true }
}

12.3 Terraform Provider

resource "launchdarkly_feature_flag" "checkout_flow" {
  project_key = "payment-service"
  key         = "checkout-flow-version"
  name        = "Checkout Flow Version"
  variation_type = "string"

  variations {
    value = "current"
    name  = "Current Flow"
  }
  variations {
    value = "simplified"
    name  = "Simplified Flow"
  }
  variations {
    value = "one-page"
    name  = "One-Page Flow"
  }

  defaults {
    on_variation  = 0
    off_variation = 0
  }
  tags = ["experiment", "checkout"]
}

resource "launchdarkly_metric" "checkout_conversion" {
  project_key      = "payment-service"
  key              = "checkout-conversion-rate"
  name             = "Checkout Conversion Rate"
  kind             = "custom"
  event_key        = "checkout-completed"
  is_numeric       = false
  success_criteria = "HigherThanBaseline"
}

13. セキュリティとガバナンス

13.1 認証と認可

トークンの種類:
  1. API Access Token: 管理API操作用(サーバーサイドのみ)
  2. SDK Key: フラグデータ取得/イベント送信用(環境ごとに固有、秘密)
  3. Mobile Key: モバイルアプリ組み込み用
  4. Client-side ID: ブラウザSDK用(公開可)

13.2 カスタムロール

{
  "name": "experiment-manager",
  "policy": [
    {
      "resources": ["proj/*:env/production:flag/*"],
      "actions": ["createExperiment", "updateExperiment", "stopExperiment"],
      "effect": "allow"
    },
    {
      "resources": ["proj/*:env/production:flag/*"],
      "actions": ["updateOn", "updateFallthrough"],
      "effect": "deny"
    }
  ]
}

13.3 承認ワークフロー

本番環境でのフラグ変更に、複数のレビュアーによる承認を強制可能。

13.4 プライベート属性

const client = LaunchDarkly.init('sdk-key-xxx', {
  privateAttributes: ['email', 'phone', 'ssn']
});

const context = {
  kind: 'user',
  key: 'user-123',
  name: 'Taro Yamada',
  email: 'taro@example.com',
  _meta: { privateAttributes: ['email'] }
};

14. 運用のベストプラクティス

14.1 フラグのライフサイクル管理

1. 作成 (Creation) -> OFF状態
2. テスト (Testing) -> 開発/ステージング環境で検証
3. ロールアウト (Rollout) -> 段階的に本番展開 (1% -> 10% -> 50% -> 100%)
4. 実験 (Experiment) -> A/Bテスト実行
5. 全展開 (Full Rollout) -> 勝者バリエーション100%
6. クリーンアップ (Cleanup) -> フラグ削除、コード除去 ← 最も忘れられがち

14.2 フラグの命名規約

パターン: "{domain}-{feature}-{detail}"
良い例: checkout-flow-version, api-rate-limit-config
悪い例: flag1, newFeature, test_flag_do_not_delete

タグの標準化:
  - team:checkout, type:experiment, quarter:Q1-2025, status:cleanup-needed

14.3 技術的負債の管理

# Code References CLI でコード内のフラグ参照を追跡
ld-find-code-refs \
  --accessToken="${LD_ACCESS_TOKEN}" \
  --projKey="payment-service" \
  --repoName="payment-api" \
  --dir="." \
  --defaultBranch="main"

14.4 段階的ロールアウト戦略

Phase 1 - Internal (Day 0-2):   社内ユーザーのみ、個別ターゲティング
Phase 2 - Canary (Day 3-5):     全ユーザーの1%
Phase 3 - Early Adopters (Day 6-10): 全ユーザーの10%
Phase 4 - Experiment (Day 11-25):    33% Control + 33% Treatment
Phase 5 - Broad Rollout (Day 26-30): 100%(勝者バリエーション)
Phase 6 - Cleanup (Day 31+):         フラグ削除、デッドコード除去

14.5 障害対応

// 障害時のフォールバック実装
const primaryClient = LaunchDarkly.init('sdk-key-xxx', { stream: true });
const offlineClient = LaunchDarkly.init('sdk-key-xxx', {
  offline: true,
  featureStore: LaunchDarkly.integrations.FileData.factory({
    filePaths: ['./flag-defaults.json']
  })
});

async function evaluateFlag(flagKey, context, defaultValue) {
  try {
    if (primaryClient.initialized()) {
      return await primaryClient.variation(flagKey, context, defaultValue);
    }
    return await offlineClient.variation(flagKey, context, defaultValue);
  } catch {
    return defaultValue;
  }
}

15. トラブルシューティング

15.1 フラグ評価が期待と異なる

const detail = await client.variationDetail('my-flag', context, false);
console.log('Value:', detail.value);
console.log('Reason:', detail.reason);
// reason.kind: "OFF", "TARGET_MATCH", "RULE_MATCH", "PREREQUISITE_FAILED",
//              "FALLTHROUGH", "ERROR"
// errorKind: "CLIENT_NOT_READY", "FLAG_NOT_FOUND", "WRONG_TYPE"

15.2 実験でデータが記録されない

チェックリスト:
  - trackEvents: true が設定されているか
  - 実験ステータスが "Recording" か
  - イベントキーがメトリクス定義と一致しているか
  - sendEvents: true(SDKのイベント送信有効)か
  - イベントバッファがフラッシュされているか
  - コンテキストキーが空文字列やnullでないか

15.3 デバッグモード

const client = LaunchDarkly.init('sdk-key-xxx', {
  logger: LaunchDarkly.basicLogger({ level: 'debug' }),
  diagnosticRecordingInterval: 60
});

16. まとめ

16.1 LaunchDarkly A/Bテストの利点

利点詳細
フルスタック対応UI、バックエンド、インフラ、アルゴリズムなどあらゆるレイヤーでの実験が可能
リアルタイム分析逐次テストにより、データ収集中もリアルタイムで結果を評価可能
低レイテンシサーバーサイドSDKのローカル評価により、マイクロ秒レベル
統合されたワークフローフラグ管理、実験、段階的ロールアウトが一つのプラットフォームで完結
エンタープライズ対応RBAC、承認ワークフロー、監査ログ、SOC2準拠
充実したSDK25以上の言語/フレームワーク対応
データエクスポート外部データウェアハウスへのイベントストリーム連携
Infrastructure as CodeTerraform、API、CLIによる自動化対応

16.2 導入にあたっての考慮点

技術的考慮点:
  - SDK初期化のタイムアウト設定とフォールバック値の設計
  - マイクロサービス間のコンテキスト伝播戦略
  - イベントバッファのサイズとフラッシュ間隔の最適化
  - Relay Proxyの導入判断(接続数、セキュリティ要件)

組織的考慮点:
  - フラグの命名規約とタグ付け標準の策定
  - カスタムロールと承認ワークフローの設計
  - フラグのライフサイクル管理プロセスの確立
  - 技術的負債(古いフラグ)の管理体制

統計的考慮点:
  - 適切なサンプルサイズの事前計算
  - 最小検出効果(MDE)の決定
  - 多重比較問題への対応
  - ガードレールメトリクスの選定

16.3 参考リソース

公式ドキュメント:
  - LaunchDarkly Docs: https://docs.launchdarkly.com/
  - API Reference: https://apidocs.launchdarkly.com/
  - SDK Reference: https://docs.launchdarkly.com/sdk/
  - Experimentation: https://docs.launchdarkly.com/home/about-experimentation/

統計に関する参考文献:
  - "Always Valid Inference" (Johari et al., 2017)
  - "Peeking at A/B Tests" (Johari et al., 2015)
  - "CUPED" (Deng et al., Microsoft, 2013)
  - "Trustworthy Online Controlled Experiments" (Kohavi et al., Cambridge Press)

本書は、LaunchDarkly A/B分析機能の技術的な全体像を解説したものである。実際の導入に際しては、最新の公式ドキュメントを参照し、自社の要件に合わせたアーキテクチャ設計を行うことを推奨する。