ロールバックできない本番運用を改善する方法
「新機能をリリースしたら不具合が出た。すぐ元に戻したいのに、戻し方が分からない」——こうした状況に陥ったことはないでしょうか。開発チームに聞くと「ロールバックはできません」「戻すには昨日のコードに手動で書き換えて、DBも直さないと…」という答えが返ってくる。結局、復旧までの数時間、サービスは不具合を抱えたまま稼働し続け、ユーザーからの問い合わせだけが増えていく。この記事では、なぜロールバックできない本番運用が生まれるのか、どう改善すれば「切り戻せる」体制に変えられるのかを整理します。
「戻せない」がもたらす実害
ロールバックできない状態の怖さは、障害そのものより「障害からの復旧に時間がかかること」にあります。
- 不具合が本番に居座り続ける: 原因調査→修正→テスト→再デプロイという通常のサイクルを踏まないと直せないため、数時間〜下手をすると1日以上、不具合のあるバージョンが本番で動き続ける
- 判断が「直す」一択になる: 本来なら「まず戻して被害を止め、落ち着いて原因を調べる」という選択肢があるはずなのに、それが取れないため、焦った状態のまま本番で修正作業をすることになり、二次障害のリスクが上がる
- リリースそのものが怖くなる: 「戻せないなら失敗できない」という心理が働き、リリース前の確認に時間がかかる・リリース頻度が下がる・ますます1回あたりの変更量が増える、という悪循環に陥る
ロールバック手段の有無は、平常時には見えないコストです。しかし障害が起きた瞬間、それは「数分で収束する障害」と「数時間サービスが劣化し続ける障害」の分かれ目になります。
なぜロールバックできなくなるのか
多くの場合、意図的に「戻せない設計」にしたわけではありません。3つの典型的な原因が積み重なった結果です。
原因1: そもそも手順が存在しない
デプロイ自体が手作業で属人化していると、「進める手順」はあっても「戻す手順」は用意されていないことがほとんどです。前に進むことしか想定していないため、いざ戻そうとすると「何を、どの順番で、どこまで戻せばいいか」が誰にも分かりません。デプロイの属人化についてはデプロイが属人化したシステムを改善する方法でも詳しく扱っています。
原因2: DBマイグレーションが非可逆
アプリケーションのコードだけなら、1つ前のバージョンに戻すのは比較的簡単です。問題はデータベースです。
- カラムを削除するマイグレーションを実行してしまうと、そのカラムのデータは失われており、単純に「前のコードに戻す」だけでは動かない(古いコードはそのカラムを使おうとしてエラーになる)
- カラム名の変更、型の変更、NOT NULL制約の追加なども同様に、一度適用すると「アプリだけ戻す」では帳尻が合わなくなる
- 「マイグレーションのロールバック(down)」を書いていても、本番データに対して実際に動作確認したことがなく、いざという時に動かないケースも多い
DBマイグレーションが非可逆だと、コードのロールバックとDBのロールバックが噛み合わず、「戻すこと自体が新たな障害を生む」状態になります。
原因3: 一度きりの非可逆な変更が紛れ込む
外部APIへの一方向の通知、決済の確定処理、キャッシュの破壊的な更新など、「実行したら取り消せない」処理がデプロイに含まれていることもあります。こうした処理があると、コードを戻しても現実の状態は戻らず、ロールバックの意味が薄れてしまいます。
改善の道筋:ロールバック可能なデプロイに変えていく
いきなり全てを完璧にする必要はありません。優先度の高いものから順に手を入れていきます。
1. ロールバック可能なデプロイの仕組みを用意する
まず土台として、「1つ前のリリースに戻すコマンドが1つ存在する」状態を作ります。CapistranoやKubernetesのようなデプロイツールには、多くの場合ロールバック機能が標準で備わっています。これを実際に動かして確認しておくことが重要です。手順書に書いてあるだけで、一度も実行したことがないロールバックコマンドは、いざという時に動かないことが珍しくありません。
2. マイグレーションを後方互換にする(Expand-Contract パターン)
DBマイグレーションを非可逆にしないための定石が、Expand-Contract(展開してから収縮する)パターンです。
- Expand: 新しいカラムを「追加」する形でスキーマを変更する。既存のカラムはまだ消さない
- 移行: アプリケーションコードを、新旧どちらのカラムでも動く状態にしてデプロイする。この時点なら旧バージョンにも新バージョンにもロールバックできる
- Contract: 新バージョンが安定稼働したことを確認してから、後日改めて古いカラムを削除するマイグレーションを流す
「変更」と「削除」を1回のリリースにまとめず、複数回のリリースに分けることで、常に「1つ前に戻せる」状態を維持できます。地味に見えますが、これが最も効果の大きい改善です。
3. feature flag でコードのリリースと機能の公開を分離する
新機能のコードを本番にデプロイすることと、その機能をユーザーに公開することを、feature flag(機能フラグ)で切り離します。
- 新機能はコードごとデプロイするが、フラグはOFFのままにしておく
- 問題があれば、デプロイをロールバックしなくてもフラグをOFFに戻すだけで機能を止められる
- 特定のユーザーグループだけにフラグをONにする段階的な公開(カナリアリリース)もしやすくなる
feature flagは「コードのロールバック」より速く、DBマイグレーションの非可逆性にも影響されないため、ロールバック手段の中でも最も復旧が速い部類です。
4. 段階リリースと監視をセットで整える
一部のユーザー・一部のサーバーにだけ新バージョンを出し、問題がないことを確認してから全体に広げる段階リリース(カナリアリリース、Blue-Greenデプロイなど)も有効です。ただし、これは異常を検知できる監視とセットでなければ意味がありません。エラー率やレスポンスタイムの悪化を自動で検知できて初めて、「一部にだけ出して様子を見る」戦略が機能します。
外部チームに依頼する場合の進め方
ロールバック体制の整備を外部に依頼する場合、次のような進め方が現実的です。
- まず現状のデプロイ・マイグレーション手順を棚卸しする: 「戻せない」原因がデプロイ手順にあるのか、DBマイグレーションの書き方にあるのか、非可逆な外部連携にあるのかを切り分けないと、的外れな改善になります
- リスクの高いテーブル・処理から後方互換化を進める: 全テーブルを一度に直す必要はなく、障害時の被害が大きい主要機能から優先的に着手します
- ロールバック手順は必ず一度、実際に動かして確認する: ドキュメント上の手順で満足せず、ステージング環境などで実際にロールバックを実行し、動くことを確認した上で「使える手順」として引き渡してもらいます
ロールバック体制は一度整えて終わりではなく、その後の開発でも「非可逆な変更を安易に混ぜない」規律を保ち続けることが必要です。保守・運用として、こうした運用改善を継続的に支援することも可能です。
なお、ロールバックの問題は「既存プロダクト改善」の中でも初回の診断で見落とされやすい観点の一つです。改善に着手する前に何を確認すべきかは、既存プロダクトの改善、外部チームが最初に見る観点で整理しています。また、デプロイそのものが属人化しているケースについてはデプロイが属人化したシステムを改善する方法も合わせてご覧ください。
まとめ
- ロールバックできない本番運用は、障害発生時に「戻して被害を止める」選択肢がなく、不具合が本番に居座り続ける・焦った状態で本番修正をすることになる、というリスクを抱えている
- 原因の多くは、デプロイ手順の未整備・DBマイグレーションの非可逆性・一度きりの非可逆な処理の混入にあり、Expand-Contractパターンによる後方互換マイグレーションとfeature flagの導入が特に効果が大きい
- 外部チームに依頼する場合は、現状のデプロイ・マイグレーション手順の棚卸しから始め、リスクの高い箇所から優先的に後方互換化し、ロールバック手順は必ず実際に動かして確認してもらうことが重要
「リリースのたびに戻せないことが不安」「障害時の復旧に毎回時間がかかっている」という段階でも、torcheees はご相談を受け付けています。まずは「既存プロダクト改善」の診断で、現状のデプロイ・マイグレーション体制を確認し、改善の優先順位と概算費用をご提示します。お問い合わせフォームからお気軽にご連絡ください。