デプロイとリリースはなぜ違うのか:プログレッシブデリバリーが切り離す2つの概念

チームが新しいチェックアウトフローを完成させたとしよう。コードはテスト済み、プルリクエストはマージ済み、デプロイパイプラインもグリーン。あなたはデプロイボタンを押す。新しいバージョンが本番環境に反映される。すると、すべてのユーザーに再デザインされたボタン、並び替えられたフォームフィールド、新しい確認画面が表示される。

しかし、新しいフローのパフォーマンスを全員に公開する前に確認したい場合はどうだろう?最初は5%のユーザーだけに試してもらい、実際のデータに基づいて拡大したい場合は?

多くのチームでは、デプロイとリリースが同時に行われる。新しいコードがサーバーに配置されると、その中の機能がユーザーに利用可能になる。しかし、この2つのアクションは必ずしも連動させる必要はない。それらを分離することで、ソフトウェアの出荷に対する考え方が変わるほどのコントロールが得られる。

デプロイは技術的、リリースは体験的

デプロイとは、コードをサーバーに配置する行為である。これは技術的な操作だ。アーティファクトをビルドし、環境に転送し、新しいバージョンを起動する。サーバーは新しいコードを実行する。

リリースとは、機能をユーザーが利用できるようにする行為である。これは体験的な判断だ。コードはすでにサーバー上で実行されているが、機能はスイッチの背後に隠れている。そのスイッチを切り替えると、ユーザーは新しい動作を目にする。

同じデプロイに、複数の未リリース機能を含めることができる。一度デプロイして、段階的にリリースすることも可能だ。また、同じアプリケーションバージョンが動作している状態で、あるユーザーグループには機能をリリースし、別のグループにはリリースしない、といった制御もできる。

以下の図は、この分離を表している:

flowchart TD A[コードマージ] --> B[サーバーにデプロイ] B --> C[すべてのコードがサーバー上で実行] C --> D{フィーチャーフラグ確認} D -- フラグON --> E[ユーザーに機能をリリース] D -- フラグOFF --> F[ユーザーから機能を隠蔽] E --> G[メトリクス監視] G --> H{データは良好?} H -- はい --> I[ロールアウト率を拡大] H -- いいえ --> J[フラグをOFFに] I --> K[フルリリース] K --> L[コードからフラグを削除] J --> F

この分離こそが、プログレッシブデリバリーの基盤である。

具体例

チームが新しい「今すぐ購入」ボタンを開発したとしよう。それはより大きく、よりカラフルで、より目立つ場所に配置されている。チームはコードに自信があるが、既存ユーザーがどう反応するかは確信が持てない。突然のレイアウト変更は、何ヶ月も古いインターフェースを使ってきたユーザーを混乱させるかもしれない。

プログレッシブデリバリーを使えば、次のように進められる:

  1. 新しいバージョンを本番環境にデプロイする。新しいボタンのコードはサーバー上で実行されているが、非表示になっている。ユーザーには古いボタンが表示される。
  2. フィーチャーフラグシステムを設定し、新しいボタンを5%のユーザーにのみ表示する。これらのユーザーはランダムに選ばれる。
  3. 1週間後、データを確認する。新しいボタンを見たユーザーは、より高い割合で購入を完了していた。混乱の報告はない。
  4. ロールアウトを50%のユーザーに拡大する。さらに1週間経過。データは依然として良好。
  5. 100%のユーザーにリリースする。機能が完全に公開される。

ここで注目すべき点:1回のデプロイ、複数回のリリース。コードは本番環境に一度だけ配置された。機能は実際のユーザーの行動に基づいて、段階的に可視化された。

フィーチャーフラグが仕組みを支える

デプロイとリリースを分離するには、フィーチャーフラグが必要だ。フィーチャーフラグとは、現在のユーザーに対して機能を有効にするべきかどうかをチェックする、コード内の条件分岐である。フラグは外部から制御され、通常は設定サービスや専用のフィーチャーフラグプラットフォームを通じて管理される。

シンプルなフィーチャーフラグは、コード上では次のようになる:

if feature_flags.is_active("new_checkout_button", user_id):
    render_new_button()
else:
    render_old_button()

フラグは新しいデプロイなしで切り替えられる。設定を変更すれば、そのユーザーからの次のリクエストで新しい動作が適用される。コード変更、ビルド、デプロイは一切不要だ。

フィーチャーフラグは実験も可能にする。異なるユーザーセグメントを異なる機能バリアントにルーティングすることで、A/Bテストを実行できる。あるグループは赤いボタン、別のグループは青いボタンを見る。データがどちらがより効果的かを教えてくれる。

カナリアリリースやステージドロールアウトとの違い

カナリアデプロイとステージドロールアウトは、ユーザーを異なるバージョンのアプリケーションに誘導する方法だ。2つのバージョンを並行して実行し、ロードバランサーが新しいバージョンに一定割合のトラフィックを送る。問題が発生した場合は、トラフィックを古いバージョンに戻す。

プログレッシブデリバリーは異なるアプローチをとる。アプリケーションは1つのバージョンのみを実行する。すべてのユーザーが同じサーバーにアクセスする。しかし、そのバージョン内で、ユーザーごとに異なる機能が表示される。分離はアプリケーションレベルではなく、機能レベルで行われる。

この違いは、同じデプロイに含まれる他の変更とは独立して機能をリリースしたい場合に重要になる。カナリアでは、新しいバージョンの他の部分を隠したまま、新しいボタンだけを5%のユーザーにリリースすることはできない。ユーザーを新しいバージョン全体に送るか、古いバージョン全体に送るかのどちらかだ。プログレッシブデリバリーでは、各機能を個別に制御できる。

フィーチャーフラグのコスト

フィーチャーフラグにはコストが伴う。フラグを追加するたびに、コードに条件分岐が1つ増える。時間が経つにつれてフラグは蓄積される。適切にクリーンアップしなければ、コードベースは使われていない条件分岐で埋め尽くされ、ロジックの可読性やテストが難しくなる。

一般的なパターンは、機能に対してフラグを使用し、検証し、完全にロールアウトした後、次のスプリントでフラグを削除することだ。しかし、チームはこのステップを忘れがちだ。フラグがコードに残り、誰もそれが何を制御しているのか、まだ有効なのかを覚えていない。

ここでは規律が重要だ。フィーチャーフラグはデフォルトで一時的なものとして扱う。機能がすべてのユーザーに完全にリリースされたら、クリーンアップ作業をスケジュールする。フィーチャーフラグプラットフォームを使用している場合、ほとんどのツールは完全にロールアウトされ削除準備ができたフラグを表示するダッシュボードを提供している。

プログレッシブデリバリーが有効なケース

すべてのチームにプログレッシブデリバリーが必要なわけではない。アプリケーションが小さく、ユーザーベースが均質で、機能がシンプルな場合、フィーチャーフラグのオーバーヘッドは見合わないかもしれない。

しかし、プログレッシブデリバリーは以下の場合に価値を発揮する:

  • ユーザーの行動を大きく変える機能を出荷する場合。
  • ユーザーベースが大規模または多様で、反応が異なる場合。
  • フルリリース前に実際のデータで機能を検証したい場合。
  • チームが頻繁にリリースし、デプロイの頻度とリリースのタイミングを切り離したい場合。

重要な洞察は、プログレッシブデリバリーが「全員に出荷する」と「まったく出荷しない」の中間地点を提供するということだ。コードを出荷し、影響を観察し、証拠に基づいてリリースを拡大できる。

プログレッシブデリバリー導入の実践的チェックリスト

デプロイとリリースを分離することを決めた場合、以下のステップで始めるとよい:

  • 段階的な公開が有効と思われる機能を1つ選ぶ。すべての機能から始める必要はない。
  • その機能にフィーチャーフラグを追加する。シンプルな設定ファイルか専用サービスを使用する。
  • フラグをオフにした状態でコードをデプロイする。機能が非表示になっていることを確認する。
  • 少数のユーザーに対してフラグをオンにする。メトリクスとログを監視する。
  • データに基づいて割合を拡大する。問題が発生した場合は、すぐにフラグをオフにする。
  • 機能が完全にロールアウトされたら、コードからフラグを削除する。

まとめ

デプロイとリリースは同じではない。デプロイはコードをサーバーに配置することだ。リリースは機能をユーザーに可視化することだ。プログレッシブデリバリーはこれら2つのアクションを分離し、コードはチームのスケジュールで出荷し、機能はデータのスケジュールでリリースできるようにする。

次回チームが機能を完成させたとき、自問してほしい:これを今すぐ全員にリリースする必要があるのか、それともデータに判断を委ねることができるのか?