Unleashによるフィーチャーフラグの導入:コード再デプロイ不要でシステムに「安全ブレーキ」を

Development tutorial - IT technology blog
Development tutorial - IT technology blog

午前2時の悪夢:『人力ロールバック』の代償

Sentryからの通知でSlackの画面が真っ赤に染まりました。Grafanaのダッシュボードでは、CPU使用率のチャートが電柱のように垂直に跳ね上がっています。これは、先週新しい決済機能を「マージ」した直後に私が経験した恐ろしいシナリオです。

時計は午前2時を回っていました。プロモーション処理のロジックにバグがあり、システムがフリーズしてしまったのです。救い出す唯一の方法はロールバックでした。しかし、あいにく会社のCI/CDプロセスは、イメージのビルド、テストの実行、そしてKubernetesへのデプロイに丸15分かかります。システムが「仮死状態」にある15分間は、2,000件以上の注文がキャンセルされ、信頼が失墜することを意味していました。

Jenkinsのプログレスバーが進むのを待つ以外に何もできない、あのサーバーエラーを眺める無力感は、本当にトラウマになります。

問題の根本:デプロイとリリースの混同

私たちの多くが陥る根本的な間違いは、ビジネスロジックとデプロイプロセス(Deployment)を密結合させてしまっていることです。

機能を有効にしたいときはコードをデプロイし、バグがあって無効にしたいときはコードをロールバックする。この伝統的なやり方には、3つの大きなリスクが潜んでいます。

  • 反応の遅さ: 本番環境が炎上しているときにCI/CDを待つのは苦行でしかありません。
  • 一か八かの勝負: 5%の小規模なグループでテストする代わりに、100%のユーザーに機能を一気に開放せざるを得ません。
  • コードの乱雑化: if (env === 'production') のような条件分岐がキノコのように増殖し、コードベースが混乱に陥ります。

一般的な「応急処置」は効果的か?

多くのエンジニアは、環境変数(Environment Variables)を使うことをまず考えるでしょう。環境変数を修正してPodを再起動する。この方法はコードのデプロイよりは早いですが、依然としてサーバーの再起動が必要であり、数秒間ユーザーの接続が途切れる原因になります。

また、データベースに設定を保存する方法を選ぶ人もいます。ユーザーのリクエストごとにDBをチェックするのです。この方法は柔軟ですが、キャッシュメカニズムがないとデータベースに負荷をかけ、システムのレイテンシを増加させてしまいます。

Unleash – デプロイとリリースを分離するソリューション

その「手痛い教訓」から、私はフィーチャーフラグ(Feature Flags)システムを本格的に導入することに決めました。LaunchDarkly(小規模チームで月額約75ドルと高価)やFlagsmithを検討した結果、オープンソース版のUnleashを選びました。

Unleashを使えば、いつでもコードを本番環境にプッシュできますが、新機能は「スイッチ」の後ろに隠れて静かに待機します。準備が整い確信が持てた時に、UnleashのUI上でスイッチを切り替えるだけで完了です。コードを一行も触ることなく、すべての変更が即座に反映されます。

Dockerを使用して5分でUnleashサーバーを構築する

データの完全な制御とコスト削減のため、Docker Composeを使用してセルフホストする方法を選びました。これは技術チームにとって最も効率的な方法です。

# docker-compose.yml
version: "3.9"
services:
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: unleash
      POSTGRES_PASSWORD: password
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres", "-d", "unleash"]
      interval: 2s
      timeout: 5s
      retries: 10

  unleash:
    image: unleashorg/unleash-server
    ports:
      - "4242:4242"
    environment:
      DATABASE_URL: postgres://postgres:password@db/unleash
      DATABASE_SSL: "false"
      DATABASE_MAX_CONNECTIONS: 20
      UNLEASH_SECRET: your-secure-secret
    depends_on:
      db:
        condition: service_healthy

docker-compose up -d コマンドを実行し、localhost:4242 にアクセスします。ユーザー名 admin、パスワード unleash42 でログインすれば、フラグの管理を開始できます。

Node.jsアプリケーションとの連携:レイテンシほぼゼロ

ロジックをハードコーディングする代わりに、Unleash Clientでラップします。このライブラリは非常にスマートで、フラグを取得してアプリケーションのメモリ内にキャッシュしてくれます。

ライブラリのインストールは非常に簡単です:

npm install unleash-client

実際のコードでの設定例:

const { initialize, isEnabled } = require('unleash-client');

const unleash = initialize({
  url: 'http://your-unleash-api/api/',
  appName: 'payment-service',
  customHeaders: { Authorization: 'YOUR_API_TOKEN' },
});

async function processPayment(order) {
  // メモリ内の 'new-payment-gateway' フラグをチェック
  if (isEnabled('new-payment-gateway')) {
    return useNewGateway(order);
  }
  return useOldGateway(order);
}

Node.jsアプリケーションとの連携では、キャッシュから直接フラグをチェックするため、レイテンシは1ms未満です。万が一Unleashサーバーに障害が発生しても、クライアントは自動的に最後のキャッシュ値を使用するか、デフォルト値にフォールバックするため、システムが停止することはありません。

技術的負債を避けるための実践的な経験

フィーチャーフラグは強力なツールですが、乱用するとコードがカオスになります。

1. 「宴のあとの片付け」ルール

機能が全ユーザーに対して100%安定して動作するようになったら、すぐにそのフラグを削除しましょう。ずっと前にリリースされた機能のための if-else 文がコード内に溢れないようにしてください。

2. 段階的なロールアウト戦略 (Gradual Rollout)

全員に対して有効にするのではなく、まずは userId に基づいて10%のユーザーでテストします。メトリクスが安定していれば、25%、50%、そして100%へと徐々に増やしていきます。

ヒント:複雑なJSON戦略の設定を素早くテストしたいときは、toolcraft.app/ja/tools/developer/json-formatter を使ってUnleash APIからのレスポンスデータを確認し、正確にフォーマットするのに役立てています。

3. 目的に応じたフラグの命名

test-flag-1 のような無意味な名前は避けましょう。[action]-[feature]-[version] という形式を使います。例えば enable-stripe-v3-migration とすれば、チームの誰もがその目的を一目で理解できます。

結びに代えて

Unleashを導入してから、私はぐっすり眠れるようになりました。何か問題が起きても、スマホを取り出してダッシュボードにアクセスし、OFFボタンを押すだけです。1秒足らずで、すべてのユーザーが安全なバージョンに戻されます。

プロジェクトが拡大し、デプロイ頻度が高まっているなら、今すぐフィーチャーフラグを導入しましょう。夜中にサーバーに張り付いて後悔するまで待つ必要はありません。

Share: