リリース計画とは戻す計画
dev、staging、productionと順に上げることはリスクを下げますが、何も保証しません。リリース前に書いておくべきなのは、本番をどう元に戻すかです。
- デプロイ
- 本番で確認
直前の成果物をデプロイする
- 項目を追加
- 両方に書く
- 過去分を埋める
- 新しい方を読む
- 古い方への書き込みを止める
直前の段階を戻す。印の付いた手順まで
- フラグの裏側
- 検証
- 公開
戻せません。見える前に検証します。
私が一緒に働いたチームはどこも変更を環境づたいに上げていき、どのチームにも、すべての環境を通過したのに本番で問題を起こした変更がありました。これは規律の失敗ではありません。環境とはそういうもの、つまり自分で選んだ条件のもとで取った現実の標本だからです。
00 / ステージングが実際に証明すること
ステージングが証明するのは、その変更が「こちらが思いついた壊れ方」では壊れない、ということです。これは本物の、役に立つ結論であり、同時に感じるより小さい結論でもあります。興味深い障害を生む部分が、ステージングには揃っていないからです。
- データ量。そして、ほかの誰とも違う形のデータを持つ、あの1テナント。
- 同時実行。実在の利用者が同時に操作し、キャッシュはすでに温まっている状態。
- 連携先。今日実際にどうなっているか、という状態の連携先。
- クライアント。利用者が実際に動かしているバージョンのクライアント。
ですからリリースの結果を決める問いは「ステージングを通ったか」ではなく、「本番で誤っていた場合、次の10分で何をするか」です。稼働中のプロダクトでは、その答えはデプロイ前に存在していなければなりません。あとで答えることになれば、それは時間に追われながら、顧客の目の前で、しかも自分で選んだ時間帯に答えることになります。
01 / 変更は3種類あり、安く戻せるのは1つだけ
再デプロイで戻せるもの。 コードだけで、スキーマの変更も、新しい形でのデータ書き込みもないもの。戻すとは直前の成果物をデプロイすることであり、つまりそれを保管しておくことと、一度は意図的に戻してみて、経路が理屈ではなく既知になっていることを意味します。
作業すれば戻せるもの。 スキーマやデータの変更を加算的に行う場合です。新しい項目を追加し、両方に書き、過去分を埋め、新しいほうを読み始め、古いほうへの書き込みを止めるのは最後にする。各段階が個別に戻せること、それがこの順番の目的です。どの段階が後戻りできない地点なのかを書き出し、そこを越えることは独立した判断として、独自の日付を持たせてください。
戻せないもの。 破壊的な移行、送信済みのメール、他社APIへの呼び出し、すでに利用者の端末に入っているモバイルアプリ。これらに「ロールバック」は計画ではありませんし、もともと計画ではありませんでした。代わりに効くのは、検証が済むまで変更を見えなくしておくことです。フラグの裏側、シャドーテーブルの中、まだ誰も読んでいない場所に書くジョブの中に。
この領域で私が見てきた障害の多くは、2番目の種類の変更を1番目のつもりで扱ったものです。
02 / 影響範囲は、検証中ではなく変更前に
私が関わった梱包業界向けSaaSでは、フロントエンド、バックエンド、モバイルアプリがそれぞれ別のリポジトリにあり、デプロイのパイプラインも環境も別々でした。これは良い構造です。各部分が自分の都合でリリースできます。同時にこの構造は、あることを隠します。私はそれを計画時ではなく検証時に学びました。3つはいずれも1つのデータ層で出会うので、共有スキーマへの変更は、いま開いているリポジトリより遠くまで届きます。
実務上の意味はこうです。変更の前に「ほかに何がこれを読んでいるか」を、記憶ではなく実際に見て答えること。GraphQLスキーマから削ろうとしている項目は、自分がデプロイしていないクライアントから選択されています。Webクライアントなら半日で直せます。モバイルはそうはいきません。誰かのポケットにあるバージョンは、その人が更新するまでその人のバージョンであり、いちばん更新の遅い利用者が、バックエンドが古い形を返し続ける期間を決めます。
ここから出てくるアーキテクチャ上の規則は、地味ですが守る価値があります。共有層は加算的に変更し、削除は自分が望むより1リリース後に行う。
03 / 夜間リリースが買うのは猶予であって、安全ではない
顧客が1つのタイムゾーンに集まっているなら、業務時間外のリリースは安上がりで本物の優位であり、誰にでもあるものではありません。ただし、それが何を買っているのかは正確にしておく価値があります。誤る確率が下がるのではなく、誰かに影響が出る前に気づいて動くための時間が手に入るだけです。
その時間は、誰かが使って初めて意味を持ちます。つまりリリース前に、最初の15分で何を見るのか — 移行が意図どおりに働いたと分かる、あのクエリ、あのダッシュボード、あの画面 — と、誰が判断するのかを決めておくということです。
04 / 実際に効く段落
規程文書ではありません。全体像がまだ頭に入っているうちに書く、変更1つあたり5行です。
- 何が変わるのか。
- ほかに何がそれに触れているのか。
- うまくいったことを本番でどう確認するのか。
- どうやって戻すのか。
- どの時点で前方修正をやめて戻すのか。
5行目が重要で、たいてい抜けています。午前2時、顧客のデータが不明な状態にあるとき、あとどれだけ粘るかを上手に決められる人はいません。何の費用もかからないうちに先に決めておくこと、それがこの手法のすべてです。
3行目の具体例を、同じプロダクトでのNode.js 16から20への移行から。確認は「テストが通ること」ではありませんでした。あらかじめ定めた手順 — テナントを作成し、そのうえで顧客が普段行うことを行う — を、デプロイ済みのシステムに対して手作業で実行することでした。あの移行が生んだ依存関係とランタイムの問題は、ユニットテストが気づく種類のものではなかったからです。
05 / 次のリリースの前に片づけておくこと
- その変更がどの種類なのかを文章で、そして後戻りできない地点がどこかを。
- 直前の成果物とは何で、それを意図的にデプロイした人が一度でもいるのか。
- まだ使われている最も古いクライアントのバージョンと、その版にとって変更が安全かどうか。
- 変更がうまくいったと示す確認1つと、いかなかったと示す確認1つ。
- 起きているのは誰で、その人が単独で決めてよいのはどこまでか。
ここに出てくる案件の長い版 — 何を診断し、何を移行し、どう出したか — はケーススタディにあります。