45395 - シコウサクゴ -

本番ガードは『全呼び出し経路』に置かないと別の入口から漏れる:多層防御と経路の全列挙

2026-06-14
AI駆動開発
AI駆動開発
Claude Code
多層防御
fail-safe
コードレビュー
Last updated:2026-06-14
14 Minutes
2634 Words

「本番でのみ実行すべき操作が、無効化対象のレコードに対して誤って走ってしまう」——そういうバグの修正 PR をレビューしていました。

修正自体は丁寧で、症状への対処と根本へのガードを分けた多層防御になっていました。ところがレビューを進めるうちに、そのガードが1つの呼び出し経路にしか置かれていないことに気づきました。同じ本番操作には、別の入口からも到達できたのです。

本記事は、この経験から「AI に本番ガードを頼むときは、操作に到達しうる全呼び出し経路を先に列挙させる」という運用にした記録です。

事の発端:無効なレコードに本番操作が走った

問題になったのは、外部サービスへの本番操作——つまり「本番環境でのみ実行すべき外部 API コール」でした。本来はフィーチャーが有効なレコードに対してだけ走るべきなのに、無効化対象(フィーチャー無効)のレコードに対しても操作が走ってしまうバグがありました。

修正は「多層防御(multi-layer defense)」の発想で組まれていました。症状と根本を分けて、それぞれに手を打つ構成です。

対象内容
症状対処スケジューラ設定(ジョブ定義)当該ジョブを dry-run 化して、即座に被害を止める
恒久対処エントリポイントのコードis_feature_enabled が False なら操作をスキップする fail-safe ガードを追加

この「症状(設定)と根本(コードガード)を分離する」設計は、とても理にかなっています。設定変更で出血をすぐ止めつつ、コード側に恒久的な歯止めを入れる。さらにこのガードには環境変数によるバイパス機構もあり、デフォルトは有効、=0 を渡すと無効化できるので、検証時には意図的にガードを外して挙動を確かめられるようになっていました。

ここまでは申し分のない修正でした。

なぜ「1経路だけのガード」が危ういのか

レビューで引っかかったのは、この恒久ガードが1つのエントリポイント(1つの呼び出し経路)にしか置かれていない点でした。

その本番操作に到達する経路は、実はそこだけではありませんでした。同じ操作には、複数の別モジュールを経由する他の呼び出し経路が存在していて、それらは追加されたガードの対象外だったのです。今回直したエントリポイントから入れば確実に守られますが、別の入口から入れば、依然として無効なレコードに操作が漏れうる状態でした。

これは AI にガードを頼んだときに非常に起きやすいパターンです。

  • AI は、目の前で見えている1つの経路にガードを置いて「対処しました」と完了を報告しがち
  • しかし本番操作には、CLI / 別オーケストレータ / 別フロー / テストの直叩き、といった複数の到達経路が存在することが多い
  • AI も人も、「直した経路」を見て安心し、列挙されていない経路の存在を見落とす

「ガードを入れる」という指示は、暗黙に「どこに入れるか」を含みます。経路の全体像を持たないまま頼むと、見えている1箇所だけが守られ、防御の網に穴が残ります。

しかも、この穴は普段は見えません。今そのジョブが通る経路だけ守られていれば、テストも通り、本番も問題なく回ります。穴が顕在化するのは、別のオーケストレータが追加されたときや、運用者が普段と違う入口から操作したときといった、後日の変更がきっかけです。「直したときは正しかったが、構成が変わった瞬間に漏れる」——この時間差が、経路の見落としを特に厄介にします。

このPRのスコープは正しかった

ただし、今回の PR を「不十分」と切り捨てるのは間違いです。この PR のスコープとしては、1経路へのガードで正しかったからです。

理由は、対象のジョブ定義が使う経路を実際に確認したことにあります。

  • 対象のジョブ定義(スケジューラ設定)が、ガードを入れたエントリポイントを直接呼ぶことを、設定ファイルのパース(設定ファイルをパーサで解析)で確認した
  • そのエントリポイントの CLI 入口は、選択肢が制約されていて、未サポートの値が渡らない設計になっていた

つまり「今このジョブが通る経路」は1本で、そこは確実に塞がれている。だから PR の目的(このジョブからの漏れを止める)は達成されています。問題は「このジョブ」ではなく、「同じ本番操作に到達しうる他の経路すべて」を将来も塞げているか、という一段広い視点でした。

そこでレビューでは、防御を徹底するなら全呼び出し経路の列挙とガードの多層化が必要だ、と指摘し、フォローアップ Issue 化を推奨しました。PR をブロックするのではなく、スコープは認めたうえで、残るリスクを記録に残す形です。

学び:先に「全呼び出し経路」を列挙させる

この経験を一般化すると、本番操作を守るときの順番が大事だ、ということになります。「ガードを置く」が先ではなく、「どこに置くべきかを設計する」が先です。

AI に依頼するなら、こう頼みます。

この本番操作(外部サービスへの操作)に到達しうる全呼び出し経路を列挙せよ。 CLI・別オーケストレータ・別フロー・テストの直叩きを含め、漏れなく洗い出すこと。 そのうえで、どの経路にどうガードを置くべきかを設計してから実装せよ。

経路の全体像を先に出させることで、「見えている1箇所だけ守って完了」という事故を防げます。そして経路を列挙したうえで、防御を二段構えにします。

防御性質役割
症状対処設定変更(ジョブ定義の dry-run 化など)即座に出血を止める。可逆で速い
恒久対処コードの fail-safe ガード経路ごとに恒久的な歯止めを入れる

設定変更は速いが、設定を戻せば穴も戻ります。コードガードは恒久的だが、置いた経路の数だけしか効きません。だから「全経路の列挙 → 各経路へのガード → 設定での即時対処」をセットで設計するのが、多層防御の本質になります。

加えて、今回のようにガードに環境変数のバイパス機構(デフォルト有効、=0 で無効化)を持たせておくと、検証時に意図的にガードを外して「ガードが無ければ確かに漏れる」ことを再現できます。ガードがちゃんと効いているかを確認できる仕掛けは、ガードそのものと同じくらい重要です。効いているか分からないガードは、いつの間にか壊れていても気づけないからです。

運用Tips:経路の網羅を担保する小さな習慣

レビューや実装で、経路の見落としを防ぐための実務的なコツです。

  • 「ガードを入れた」と報告されたら、まずその操作の呼び出し元を全部洗ったかを確認する。1経路だけなら、それが全経路なのか1つなのかを問う。
  • ジョブ定義やスケジューラ設定がどのエントリポイントを呼ぶかを、推測ではなく設定ファイルのパースで裏取りする。今回の決め手もこれでした。
  • 「今のスコープでは1経路で十分」が正しくても、残る経路は Issue 化して記録する。スコープの正しさと、将来のリスクの記録は両立します。
  • CLI 入口の引数が制約されているなら、その制約も設計上の防御として明記しておく。未サポート値が来ない設計は、それ自体がガードの一部です。

この習慣の価値は、件数で測れる以前に、「1経路だけ守って安心する」という事故を構造的に防げる点にあると感じています。

まとめ:守るべきは『操作』であって『経路』ではない

本番でのみ走るべき操作を守るとき、つい「いま見えている入口」にガードを置いて満足してしまいます。AI に頼むと、なおさらこの傾向が強く出ます。

ですが、守るべき対象は操作そのものであって、特定の経路ではありません。同じ操作には複数の入口があり、1つを塞いでも別の入口から漏れうる。だから、

  1. AI に「この操作に到達しうる全呼び出し経路を列挙せよ」と先に頼む
  2. 経路を出させたうえで、どこにガードを置くかを設計してから実装する
  3. 症状対処(設定変更)と恒久対処(コードの fail-safe)を分離した多層防御にする
  4. 今のスコープが1経路で正しくても、残る経路は Issue 化して記録に残す

「ガードを入れて」という一言の裏には、「どこに」という設計が隠れています。その設計を AI 任せにせず、全経路の列挙から始める——それが、別の入口からの静かな漏れを防ぐ習慣です。

Article title:本番ガードは『全呼び出し経路』に置かないと別の入口から漏れる:多層防御と経路の全列挙
Article author:45395
Release time:2026-06-14

記事へのご質問・ご感想をお聞かせください

フィードバックを送る