メインコンテンツへスキップ

概要

リリースとは、コードが本番環境に届くタイミングとは独立に、変更をいつ・どのようにユーザーへ見せるかを決める一連の判断です。核となる原則は「デプロイ」と「リリース」の分離です。デプロイはコードを本番環境へ反映する機械的な作業であり、リリースは機能を公開するという判断です。 本ページではこの分離の考え方、それを実装するFeature Flagの仕組み、トランクベース開発との関係、そして安全なロールアウト・切り戻し・フラグの掃除までを解説します。 デプロイ自体を自動化するパイプラインはCI/CDのページで扱い、本ページは「いつユーザーに見せるか」の判断と手法を扱います。

デプロイとリリースは別の出来事

コードのデプロイと機能のリリースは同じ瞬間に起こることが多いものの、答える問いも、判断の主体も異なります。

デプロイ

コードを本番環境へ反映することです。自動化でき、1日に何度でも実行できる、機械的で反復可能なステップです。

リリース

機能をユーザーが使える状態にすることです。告知やサポート体制、ビジネス上の節目との調整を含む、タイミングの判断です。
この2つを結合したままにすると、トレードオフを強いられます。デプロイが公開タイミングを待つなら変更が溜まって大きくリスクの高いバッチになり、コードの到達と同時に機能が公開されるなら準備が整っていなくても公開されてしまいます。 両者の分離は継続的デリバリーの土台です。ソフトウェアを常にリリース可能な状態に保ち、小さな変更を高頻度でデプロイし、リリースのタイミングを意図して選びます。小さく頻繁なデプロイは、個々の変更のレビュー・テスト・取り消しを容易にするため、速度と安定性を同時に高めます。

Feature Flag

Feature Flagは、コードを変更せずに振る舞いのオン・オフを切り替える条件分岐です。最もシンプルで確実な実装は、コード内の1箇所の分岐点で環境変数を読むことです。 次の例では、環境変数FEATURE_NEW_LABEL'true'のときだけ新しいラベルを表示します。ローカル環境でのみ有効にして実装を進め、本番環境では無効のままマージします。
本番環境での機能の有効化は環境設定へのFEATURE_NEW_LABEL=trueの追加という設定変更であり、無効化はその行を取り除くだけです。 フラグによって「コードが本番環境にある」ことと「機能が見えている」ことが独立した事実になります。これにより次が可能になります。

未完成の作業をマージできる

開発途中の機能をフラグの裏に隠したままメインブランチへマージできます。長命なブランチで寝かせる必要がありません。

頻繁にデプロイし、慎重にリリースする

1日に何度も本番環境へデプロイしながら、各機能をいつリリースするかを選べます。

即時の切り戻し

リリース済みの機能に問題があればフラグをオフにするだけです。復旧にかかる時間は軽微です。

本番環境で安全に確認できる

検証環境や社内利用でだけ先に有効化し、本番環境のユーザーには従来の挙動を見せたまま確認できます。
環境変数ベースのフラグは追加のインフラ無しで実現できるため、はじめの一歩としてはオススメです。フラグ管理のライブラリやサービスの導入を検討するのもいいでしょう。

トランクベース開発

トランクベース開発は、すべての作業を小さな単位でメインブランチへ統合し続け、数日から数週間生き続けるブランチを避けるプラクティスです。長命なブランチはconflictを蓄積し、統合の問題を最後まで隠し、レビューしにくい大きなプルリクエストを生みます。 複数のプルリクエストにまたがる機能でこれを成立させるのがFeature Flagです。各増分はレビューを通過し次第フラグに守られてマージされ、最後のピースが揃ったときに初めて機能を有効化します。 結果として、同じ領域を複数人で開発してもconflictが起きにくく、すべてのマージが適切にレビューできる大きさに保たれます。 次の図では、1つの機能を3つの増分に分け、それぞれフラグをオフにしたままbaseブランチへマージし、最後の増分がマージされた後にフラグをオンにしてリリースしています。 これは作業を小さな単位に分割する原則と同じ考え方です。作業の切り方はタスク分解を、個々のマージをレビュー可能に保つ方法はプルリクエストを参照してください。

topic branch運用で本番環境への影響を切り離す

フラグの裏に隠せない変更もあります。画面全体の刷新やフレームワークの移行のように、本番環境への影響を隠すことが出来ない場合です。 topic branch運用は、そうした変更でも粒度を維持するための、baseブランチと作業ブランチの間にtopic branchを挟む3層のブランチ構成です。レビューと統合はtopic branch上で完結し、baseブランチにはリリースまで変更が反映されないため、本番環境に影響を与えずに小さなプルリクエストを出し続けられます。 運用の流れは次のとおりです。
  1. baseブランチからtopic branchを切り、そこからさらに作業用のdevelop branchを切ります。
  2. develop branchからtopic branchへのプルリクエストでレビューします。粒度を維持するのはこの段階です。
  3. 本番環境への反映の準備が整ったら、topic branchからbaseブランチへのプルリクエストをマージします。このタイミングのレビューは必要最低限で問題ありません。手順2で既にレビュー済みだからです。
  4. baseブランチの修正内容は最低1日1回はtopic branchへ取り込み、conflictを最小限に抑えます。
トレードオフは、topic branch自体がリリースまで生き続ける長命なブランチである点です。baseブランチとの統合が後ろ倒しになるのはトランクベース開発が避けようとすることそのものであり、手順4の毎日の取り込みがconflictを管理可能に保つ前提になります。 フラグの裏に隠せる変更ではFeature Flagを優先し、隠せない変更にtopic branch運用を使います。

段階的ロールアウトと切り戻し

デプロイとリリースが分離されていると、リリースは一度きりのイベントではなく、制御された一連のステップになります。
1

フラグをオフにしたままデプロイする

コードは無効化された状態で本番環境に到達します。ユーザーには何の変化も見えず、デプロイ自体のリリースリスクはほぼゼロです。
2

検証環境で有効化する

まず検証環境でフラグをオンにします。ユーザーに公開される前に問題が表面化し、欠陥は早く見つかるほど修正コストが小さくなります。
3

本番環境でリリースする

選んだタイミングで本番環境のフラグをオンにします。公開範囲を絞れる場合は、特定のユーザーから展開を始め、徐々に公開範囲を広げます。
4

問題があればフラグをオフにして切り戻す

機能に問題があればフラグを無効化します。
フラグによる切り戻しが機能するのは、従来のコードパスが残っていて、そのテストが通り続けている場合だけです。フラグが有効な間は、フォールバック側のパスを削除したり壊したりしてはいけません。

両方の状態をテストし、フラグを削除する

フラグはコードが取りうる状態を2倍にし、機能のライフサイクルのどこかで両方の状態が本番環境で動きます。分岐の両側にテストを書きます。
オフ側のテストは省略できません。機能の開発が進んでも切り戻し先が動き続けることを保証するのがこのテストです。信頼できるテストの条件はテストコードを参照してください。 フラグは設計上、一時的な存在です。フラグをオンにした状態で安定稼働し、オフに戻すシナリオがなくなったら、フラグ・不要になった分岐・オフ側のテストを1つのプルリクエストでまとめて削除します。 放置されたフラグは、誰もテストしていない状態の組み合わせとして積み上がります。信頼できるテストスイートがあるからこそ、この削除を先送りせずに実行できます。

関連ページ

タスク分解

フラグの裏でマージできる大きさまで機能を分割します。

プルリクエスト

個々のマージを小さくレビュー可能に保ちます。

テストコード

フラグの両状態を守り、フラグの削除を安全にするテストです。