リファクタする前に観測せよ:スプリントを救うSREの反射神経
最悪のリファクタは、データなしで始めたものです。サービスが遅い、または不安定なとき、最初に出すべきはコードではなく可観測性です。
リファクタする前に観測せよ:スプリントを救うSREの反射神経
サービスが遅い。サービスが週に2回原因不明で落ちる。チームの反射はほぼ常に「書き直す必要がある」です。誰かがホワイトボードを取り出し、新しいアーキテクチャを提案し、3スプリント後には同じ問題を持つ真新しいサービスができ上がっています。
SREの反射はその逆:リファクタする前に観測せよ。教義としてではなく、経験として。
データなしでリファクタする隠れたコスト
データなしのリファクタは、3つの目隠しの賭けをすることに等しい:
- 正しい問題を特定したという賭け。サービスが遅い?どこが遅い?どのエンドポイント?どの種類のリクエスト?1日のどの時間帯?
- 新しいアーキテクチャがその特定の問題を解決するという賭け。ベースラインがなければ、後で良くなったかすら分からない。
- 新しい問題が出ないという賭け。リファクタ = 再びバグ発生。古いコードを計装していなければ、比較もできない。
3つの賭け、3回外す可能性。統計的には少なくとも1回は外します。
「観測する」とは具体的に何か
観測とは「ログをいくつか追加する」ことではありません。業務コードに触れる前に、明確な質問に答える可観測性のサブプロジェクトを出荷することです。
遅いサービスに対しては、最低限以下が欲しい:
- エンドポイント別、パーセンタイル別のレイテンシ(p50、p95、p99)。平均は嘘をつく。
- 応答時間の分解:DB内で何秒、外部呼び出しで何秒、アプリケーションロジック内で何秒。可能なら分散トレーシング。
- エンドポイント別・コード別のエラー率(5xxは何も語らない;502と504と500はそれぞれ違う物語を語る)
- リソース飽和:CPU、RAM、ファイルディスクリプタ、DB接続
- 依存関係のレイテンシをクライアント側で測定(依存先自身が公開するメトリクスだけでは不十分)
不安定なサービスに対しては:
- 構造化ログ:完全な経路を再構築するためにリクエストIDを伝播
- 継続的プロファイリング(実現可能な場合):Pyroscope、ランタイム統合プロファイラ
- 事前構築済みのインシデントダッシュボード:午前3時の危機の最中に作るのではなく
- 問題のあるリクエストのリプレイ:pre-prod環境で
「あ」の瞬間
これを丁寧に行うたびに、実際の診断は想定された診断と異なっていました。
- 「遅い」サービスが遅かったのは、47本のエンドポイントのうち1本がトラフィックの80%を運んでおり、DBに対してN+1を起こしていたから。サービスの残りは正常だった。
- 「落ちる」サービスが落ちていたのは、月次のcronジョブが特定の瞬間にメモリを爆発させていたから。誰もcronを疑わなかったのは、誰もグラフに表れているのを見ていなかったから。
- 「CPU飽和」しているサービスは、CPU飽和ではなく、進行中のマイグレーションが想定の10倍時間がかかったためにDBロックでブロックされていた。
これらの診断はどれも、汎用的なリファクタでは修正できなかったでしょう。すべて、数時間の開発で外科的にピンポイント修正できました。
それでもリファクタするとき
観測せよということは、決してリファクタするなということではありません。データと測定可能な目標を持ってリファクタせよということです。正当なリファクタはこういう形になります:
- 「このエンドポイントのp99が4秒、目標は400ms。トレースから時間の90%が毎リクエストで読み込む巨大blobのシリアライゼーションにあると判明。キャッシュする」
- 「サービスAとBの共有DBカップリングに起因するインシデントが月12件。スキーマを分離する」
- 「クラウドコストが半年で倍増。トレースから70%が使っていない冗長ログだと判明。削減する」
3つの例、3つの機能するリファクタ。なぜか?データが行動に先行し、目標が測定可能だから。
反論
「でも計装する時間がない、緊急なんだ」
緊急だからこそ最初に計装するのです。観測に1スプリントを失うのは、間違ったサービスのリファクタに3スプリントを失うより安い。可観測性の構築に2日かかるなら、その2日はプロジェクトで最も収益性の高い2日になります。
そして、可観測性が完全に欠如していて構築に数週間かかるなら、それこそが本当のプロジェクトです。リファクタではなく。
1文での要約
サービスが壊れたとき、最初にデリバリーすべきは決して業務コードではありません。どこが壊れているかを見る能力です。残りは自然について来ます。