一覧に戻る
    インシデント9件を分析したら、影響範囲調査の本質が見えてきた
    SRE
    PRこの記事には広告が含まれています

    インシデント9件を分析したら、影響範囲調査の本質が見えてきた

    19 分で読める

    まず正直に書くと、これは「うまくいった話」ではありません。

    1Q で9件のインシデントが発生した。その現実から始まっています。「影響範囲調査の不備が原因だった」とわかっていても、個々のインシデントに反省文を書いて終わるサイクルを繰り返していた。そこで一歩立ち止まって、データをもとに構造を分解してみました。

    この記事は、その作業ログをベースにした分析の記録です。

    「影響範囲調査」とは何を調べることなのか

    そもそも「影響範囲調査」という言葉は、現場でよく使われるわりに定義があいまいです。

    コードを変更したとき、その変更が「どこに波及するか」を事前に把握すること——これが影響範囲調査です。でも実際には、コードだけを見ていれば済む話ではありません。

    大きく4つのレイヤーに分けて考えると整理しやすくなります。

    ソースコードレイヤーは、コードがコードを呼び出している関係のことです。あるファイルを変更したとき、それを import している別のファイルが壊れないか、関数の引数や戻り値の型を変えたとき呼び出し元が対応しているか、といった確認が必要です。データベースのテーブルやカラムの参照箇所も、ここに含まれます。

    インフラ・構成レイヤーは、サーバーやネットワークの設定ファイルのことです。最近は Terraform などのツールで「インフラの構成をコードとして管理」(IaC: Infrastructure as Code と呼ばれます)するケースが増えています。この設定を変えると、意図せず別のサーバーやサービスに影響が出ることがあります。

    サービス間連携レイヤーは、複数のサービスが互いに API を叩き合ったり、メッセージキュー(非同期でデータをやりとりする仕組み)を通じて連携している部分です。あるサービスの API の仕様を変えたとき、それを呼び出しているほかのサービスが正常に動くかを確認する必要があります。

    運用・観測レイヤーは、ログやアラートの設定のことです。ログの出力フォーマットを変えると、それを読んで分析している仕組みが壊れることがあります。

    インシデントを「見落としのパターン」で分類する

    今回の分析を進める上で、まず「どういう見落としがあるのか」を4つのパターンに分けて整理しました。

    A. 明示的な依存の見落とし——コード上で直接呼び出している関係を追いきれなかったケース。呼び出しのチェーンが深くなると、変更の波及が何段階にも連鎖します。ただし、これはツールで補いやすい類の問題です。

    B. 暗黙的な依存の見落とし——コード上の呼び出し関係には現れないけれど、実は依存している部分を見落としたケース。たとえば「複数のサービスが同じデータベースのテーブルを参照していた」「非同期のメッセージキュー経由で連携していた」など。コードを読むだけでは気づきにくい。

    C. 契約変更の影響読み誤り——変更そのものは把握していたけれど、その変更が依存先に与える影響を読み誤ったケース。たとえば外部ライブラリのバージョンを上げたら、内部で使っている React のバージョンも変わっていて、別のライブラリが動かなくなった、など。

    D. タイミング・順序の問題——静的にコードを読むだけでは見えない、実行時のタイミングや環境差異によって起きたケース。「開発者の PC の CPU アーキテクチャが本番環境と違っていた」「デプロイの順序を間違えた」など。

    データを見て、驚いた

    1Q の9件のインシデントを上のパターンに当てはめました。

    A が0件だったのです。

    「コードの呼び出し関係を追いきれなかった」インシデントは、9件のうち1件もなかった。一方で、「そもそも見えていなかった依存」の見落とし(B)が5件、過半数を占めていました。

    これは大事な発見でした。なぜなら、「コードの静的解析ツール(コードを実行せずに依存関係を解析するツール)を入れよう」という対策は、今回のインシデントには1件も刺さらないということを意味するからです。

    B の5件を深掘りする

    さらに5件を見ていくと、2つのパターンがありました。

    パターン1:新旧システム並行運用時のデータ同期漏れ(3件)

    プロダクト A でリニューアルを進めていた時期のインシデントです。旧仕様のテーブルと新仕様のテーブルが共存している期間に発生しました。

    たとえば「新しいテーブルだけ更新する処理を書いた。でも旧テーブルを参照しているコードがまだ残っていた」というケースです。あるいは「外部の認証サービス(Auth0)からデータベースへデータを移行するスクリプトを実行したが、移行処理と元データの抽出にタイムラグがあり、古い情報が DB に入ってしまった」というものも含まれます。

    共通点は「同じ概念のデータが2箇所に存在していて、片方を更新したときにもう片方への影響を考慮していなかった」ということです。

    パターン2:共有データストアの依存把握漏れ(2件)

    プロダクト B で起きたインシデントです。複数のサービスや言語(たとえば Rust で書かれた API と Java で書かれた API の両方)が同じデータベースのテーブルを参照している状況で、片方だけ考慮してスキーマ変更(テーブルの構造変更)を行ったケースです。

    もうひとつは「テーブル定義書に『不要テーブル』と書いてあったからテーブルを削除したら、実はレガシーな(古い)API がまだそのテーブルを参照していた」というインシデントです。

    プロジェクト別に見ると、性質の違いが見えてくる

    プロダクト A(B: 3件)プロダクト B(B: 2件)
    B の本質新旧移行期の二重管理マルチサービスの共有 DB
    時間軸移行完了で収束する可能性あり構造的問題なので放置すると再発し続ける

    プロダクト A は「リニューアルの移行が完了すれば、新旧テーブルが共存する状況自体がなくなる」ので、時間が解決する余地もあります。一方プロダクト B は「複数のサービスが同じ DB を共有している」という構造的な問題なので、対策しなければ同種のインシデントが出続けます。

    ただ、表面の違いに惑わされてはいけない。根っこにある問題は両方同じです。

    「あるデータを変更したとき、他に誰がそれを参照・更新しているかがわからない」

    SaaS で解決できないか調べた

    「これを解決してくれるサービスがあれば導入したい」と思って調べました。カテゴリ別に整理します。

    コード横断検索(Sourcegraph、GitHub Code Search など)

    複数のリポジトリにまたがってコードを一括検索できるツールです。「このテーブル名を参照しているコードを全部見つけたい」というとき、手でひとつひとつのリポジトリを grep する(テキスト検索コマンドで探す)代わりに、一括で検索できます。導入コストは比較的低い。ただし、「調べる人が何を調べるべきか知っていること」が前提になるため、チェックを忘れれば同じことが起きます。

    データカタログ / データリネージ(DataHub、OpenMetadata など)

    「データがどこにあり、どこから来て、どこへ流れているか」を可視化・管理するためのツールです。テーブルやカラムに「このデータは何のためのものか」「誰が管理しているか」といったメタデータを付与でき、変更したときに下流(そのデータを受け取る側)への影響を確認できます。

    ただし、ここに大きな落とし穴がありました。これらのツールが想定しているのは、主にデータ基盤のパイプライン(たとえば BI ツール向けのデータ加工フロー)の依存関係です。アプリケーションコードからデータベースへのアクセス——Laravel という PHP のフレームワークで書かれたコードがテーブルを参照している、といったケース——は、自動では追跡できません。手動で登録するか、別の仕組みを組み合わせる必要があります。

    今回のインシデントの大半はまさに「アプリケーションコードから DB へのアクセス」の見落としなので、このカテゴリのツールを入れるだけでは解決しないとわかりました。

    APM ツール(Datadog など)

    APM(Application Performance Monitoring)は、本番環境で実際にアプリケーションが動いている様子を自動で記録するツールです。「どのサービスがどのテーブルに実際にアクセスしているか」を事後的に把握できます。設定し忘れた依存関係も、実際に動けば記録されるのが強みです。ただし、「本番ではまだ実行されていないコードパス(エラー時の分岐、バッチ処理など)」は見えない、という限界があります。

    スキーマ変更の検知ツール(Atlas など)

    CI(継続的インテグレーション:コードの変更を自動でテスト・検証する仕組み)のパイプラインの中で、データベースのスキーマ変更(テーブルの構造変更)を自動でチェックするツールです。既存のデータが壊れる可能性のある変更をプルリクエストの段階で検知できます。ただし対応している言語や ORM(データベースを操作するためのライブラリ)が限られていて、複数言語が混在する環境では全部をカバーしにくいという制約があります。

    銀の弾丸はなかった

    正直に言います。今回のインシデントパターンにドンピシャで対応できる SaaS は存在しませんでした。

    理由はシンプルで、今回の問題が「複数のプロダクト・複数のプログラミング言語にまたがるアプリケーションコードから、データベースへのアクセスを横断的に可視化する」という課題だからです。各ツールのカテゴリの境界にある問題で、どれか一つを入れれば解決するものではありませんでした。

    「調べればわかった」のに、なぜ調べられなかったのか

    9件すべてに共通する事実があります。

    「調べようと思えば調べられたが、調べるべきだという認識がなかった」

    ただここで注意が必要です。「だからチェックリストを追加しよう」と結論するのは早い。なぜなら、「調べれば分かったのに調べなかった」理由が、実は調べること自体のコストが高すぎたからです。

    具体的には3つの壁がありました。

    ひとつは「リポジトリが複数にまたがっていて、横断的に検索する手段がない」という問題です。プロダクト A とプロダクト B でリポジトリが分かれており、「このテーブルを参照しているコードを全部洗い出す」ためには、手作業でリポジトリを移動しながら検索する必要がありました。

    ふたつめは「テーブル名で検索しても引っかからない」という問題です。Laravel というフレームワークでは Eloquent(エロクアント)という ORM(データベースとコードを紐づけてくれるライブラリ)を使っています。Eloquent では ProjectUsage というクラス名がテーブル project_usage に対応するので、project_usage というテーブル名でコードを検索しても見つかりません。Rust では SQLX というライブラリを使っていて、こちらはクエリ文字列から追う必要があります。言語やフレームワークによって探し方が変わるのです。

    みっつめは「バッチ処理や外部連携など、メインのコードベース以外からも参照されている」という問題です。アプリケーションのコードだけ調べても、夜間のバッチ処理や管理画面から同じテーブルを触っているケースが見落とされます。

    ツールの問題ではなく「何を確認すべきか」という観点の問題ではある。でも、その観点があったとしても調べる手段がなければ意味がない。この両方が揃っていないとインシデントは防げません。

    最初の一手をどう考えるか

    大きく3つのアプローチが考えられます。

    案 A は「変更するたびに、その都度影響範囲を調べる」アプローチです。コストが低くすぐ始められますが、「調べるべきタイミングを忘れた」「どこまで調べたか記録が残らない」といったリスクが残ります。

    案 B は「常に依存マップ(どのコードがどのデータを触っているかの地図)を最新状態で維持しておく」アプローチです。変更のたびに地図を見れば済むので属人性は下がりますが、地図を維持するコストと、メンテナンスが追いつかず地図が古くなるリスクがあります。

    案 C は「CI のパイプラインに組み込んで、変更を検知したら自動で通知する」アプローチです。人の意識に依存しなくなるのが最大の強みですが、構築コストが最も高く、対象範囲の定義も必要です。

    スモールスタートで横展開するなら、A から C へ段階的に進めるのが現実的だと考えています。ただし、最初の A の段階から「データストアを起点に、依存先を逆引きする」というフレームを固定することが重要です。

    このフレームが共通であれば、プロダクトが変わっても同じアプローチが適用できます。プロダクト固有のチェックリストとして始めてしまうと、横展開のときにゼロから作り直すことになります。

    具体的な最初の一手

    PR レビュー(コードの変更を他のメンバーに確認してもらうプロセス)に、データ影響チェックを組み込みます。

    以下のいずれかに該当する PR では確認を必須にします。

    • テーブルのスキーマ変更(テーブルの構造を変える ALTER コマンドや、マイグレーションファイルの追加)
    • テーブルの追加・削除
    • データの参照先の変更
    • データを更新するロジックの変更

    確認する内容はシンプルです。変更するテーブルやデータソースを、他にどのサービス・コードが参照・更新しているか。同じデータが別の場所にも保存されていないか。他サービスへの影響があるか。

    PR のテンプレートに以下を追加します。それだけです。

    ## データ影響チェック(該当する場合は必須)
    - [ ] 変更対象のテーブル/データソースを他に参照しているコードを検索した
      - 検索結果:(ここに記載)
    - [ ] 同じデータの二重管理がないか確認した
    - [ ] 他サービスへの影響有無を確認した
      - 影響がある場合の対応:(ここに記載)

    これで「grep すればわかった」という状況を仕組み化できます。書かれていなければレビュアーが確認を求める。記録が残るので後から「何を確認したか」が追跡できる。

    「でも全リポジトリを横断して検索するのが大変だからそれができなかったんじゃないか」という疑問はもっともです。今の段階では完全には解消できていません。ただ、まずこのプロセスを一つのプロダクトで始めて、「毎回検索するのが大変」「検索漏れが出る」という課題が実感として出てきたら、そこで初めて Sourcegraph のようなツール投資の判断ができます。課題が具体化してからツールを選ぶ方が、正しい投資判断につながります。

    この分析を経て気づいたこと

    技術的なインシデント対応を考えるとき、どうしても「ツールを導入すれば解決する」という方向に引っ張られがちです。私自身もそうでした。

    でも今回の分析を通じて、問題の構造を先に理解しないとツールを選べないということが改めてわかりました。データを見てはじめて「コードの静的解析ツールへの投資は今回のインシデントには効かない」と言えるようになった。直感ではなく、事実から。

    次のステップとして、リポジトリ数・言語・ORM(データベース操作ライブラリ)構成の全体像を整理した上で、「データストア起点の逆引き」を実現するツール設計を具体化していく予定です。それはまた改めて書きます。

    参考

    インシデント分析やサービス間の依存構造を体系的に学びたい方には、以下の書籍が参考になります。

    この記事は役に立ちましたか?

    Coffee cup

    この記事が、何かの整理につながったら

    コーヒー1杯分の応援をもらえると嬉しいです。

    ※ これは応援とは別の話ですが、

    同じようなテーマを自分の文脈で整理したい場合は、 (文章だけだと詰まりやすい人向けに) 思考整理の壁打ちという形で対話の時間も取っています。

    対話の時間について