2.15

View in English

2.15 デバッグとトラブルシューティング

概要と動機

デバッグとは、システムがなぜあるべきでないことをするのかを突き止める、規律ある仕事であり、トラブルシューティングは、同じスキルを、時間の圧力のもとで稼働中の本番システムに向けたものです。どちらも、欠陥に適用された科学的方法です。驚くべき振る舞いを観察し、その原因についての仮説を立て、それを確認または反証する実験を設計し、勘ではなく証拠に何を変えるべきかを教えさせます。このやり方なら、デバッグは学べて教えられるエンジニアリングのスキルです。民間伝承としてなされれば、迷信になります。ランダムな行を変え、サーバーを再起動し、祈る。

大きなチームでは、その違いは高くつきます。一つの難しい欠陥が、複数のサービスにわたるエンジニアを巻き込み、オンコールの時間を消費し、リリースを止めえます。各人が直感でデバッグするとき、その労力は複利にならず、他の誰が何を試したか、誰も再現も説明もできません。チームが一つの方法(まず再現し、探索で切り分け、バグを失敗するテストに捉え、それから修正する)を共有するとき、同じ労力が繰り返し可能なプロセスと、育つ回帰スイートに変わります。デバッグは、テスト戦略(2.4章)、ソフトウェア品質(2.11章)、そしてそもそもコードを診断可能にする構築の習慣(2.9章)に密接につながります。

企業や政府の設定では、賭け金が上がります。企業の欠陥はサービスやチームの境界をまたぐので、症状を見る人がその原因を所有する人であることはまれです。政府のシステムは、ほとんどのエンジニアが出会わない制約を加えます。本番にデバッガーを接続できないエアギャップや制限された環境、成果物から診断しなければならない再現可能なビルド、何をなぜ変更したかを記録しなければならない監査証跡。三つすべてで、目標は同じです。当て推量を証拠に置き換えること。

主要原則

  • 理論を立てる前に再現する。 要求に応じて引き起こせないバグは、欠陥ではなく噂です。
  • デバッグは仮説の検証である。 信じていることを述べ、それが間違っていると証明しうる最も安い実験を設計します。
  • まずエラーとスタックトレースを読む。 システムは通常、一行も変更する前に、どこで壊れたかを教えてくれます。
  • 問題空間をスキャンするのではなく、探索する。 上から下に読む代わりに、各ステップで疑わしい領域を半分にします。
  • 最小まで減らす。 本質的なトリガーだけが残るまで、ケースを削ぎ落とします。
  • 一度に一つの変更。 散弾銃的な編集は、どの変更が効いたかを教えたはずの証拠を破壊します。
  • 直す前にバグを失敗するテストに捉える。 修正は、そのテストが緑になり、緑のままであるときにだけ証明されます。
  • 最も近い症状ではなく、根本原因を見つける。 症状を隠すパッチは、欠陥が戻ってくるのを放置します。

推奨事項

何かを変更する前に、欠陥を確実に再現する

最初の仕事は、確実な再現です。要求に応じてバグを引き起こす、一連の手順や自動化されたケース。それがなければ、本物の修正と偶然を区別できません。症状は、制御したことのない理由で現れたり消えたりしうるからです。入力、環境、バージョン、タイミングを特定します。バグが断続的なら、再現が信頼できるようになるまで、それを出現させる隠れた変数(特定のデータレコード、時計の境界、並行するリクエスト)を探します。確実な再現は、デバッグにおける最も価値のある単一の成果物です。その後のすべてが測定可能になるからです。

コードに触れる前に、エラー、ログ、スタックトレースを読む

理論を一つ立てる前に、システムがすでに教えてくれたことを読んでください。スタックトレース(失敗の瞬間の呼び出しの連鎖の記録)は、通常、失敗したファイル、行、順序を名指しします。例外メッセージ、その周りのログ行、スコープ内の値が、何も変更する前に探索を絞り込みます。エンジニアは、トレースバックが一行目で除外した原因について理論を立てて、何時間も無駄にします。エラー出力を最初の証人として扱い、注意深く完全に読み、それから初めて何を調査するかを決めてください。

問題空間の二分探索で切り分ける

コードを上から下にスキャンしてはいけません。探索してください。二分探索を使います。状態がまだ良い点と、すでに悪い点を見つけ、中間点を確認し、繰り返して、そのたびに疑わしい領域を半分にします。これで千行の探索が十の問いになります。回帰がコミットの範囲にわたって現れたとき、同じ考え方を履歴に二分法で適用します。git bisectがコミットの範囲をたどり、各リビジョンを良いか悪いかで印を付けて、欠陥を持ち込んだ正確な変更を名指しします。良い・悪いのテストを自動化すれば、二分法は自力で動きます。

最小の再現可能な例まで減らす

バグを引き起こせるようになったら、縮めます。最小の再現可能な例とは、それでもなお失敗する、最小の入力とコードパスです。さらに取り除くとバグが消えるようになるまで、データ、機能、手順を取り除きます。削減は雑用ではありません。取り除くすべての要素は、除外した原因なので、最小のケースはしばしば欠陥を直接指します。入力が大きかったり構造を持っていたりするときは、デルタデバッグで縮小を自動化します。それは、失敗する入力の塊を体系的に取り除いて、失敗する最小の部分集合を見つけるアルゴリズムです。小さく自己完結した再現は、別のチームに渡せる最良のバグ報告でもあります。

ログで計装し、それから対話型デバッガーを使う

ツールをバグに合わせます。ログと的を絞った計装は、時間にわたる、プロセスをまたぐ、あるいは一時停止できない環境での振る舞いを見る必要があるときに最適です。ブレークポイントを設定し、一行ずつ進み、生きた状態を検査できる対話型デバッガーは、コードをローカルで実行でき、一つの実行を注意深く観察する必要があるときに最適です。計装は、散らばったprint文ではなく、仮説に結びついた意図した実験として加え、バグが解決したら、取り除くか、恒久的な構造化ログに昇格させます。本番では、オブザーバビリティ駆動のデバッグに頼ります。高カーディナリティのイベントと分散トレーシング(9.2章)は、一つのリクエストを多くのサービスにわたって追跡でき、それはデバッガーを接続できない分散システムをデバッグする唯一の方法であることがよくあります。

修正する前に、バグを捉える失敗するテストを書く

修正を書く前に、バグのせいで失敗するテストを書きます。これは三つのことを一度に行います。原因を本当に理解していることを証明し、「修正された」が何を意味するかを正確に定義し、恒久的な番人になります。それから修正し、テストが緑になるのを見ます。そのテストは今や回帰テストの番人としてスイートに加わり、同じ欠陥が気づかれずに戻れなくなります。この実践は、デバッグを直接テスト戦略(2.4章)に結びつけます。解決したすべての難しいバグは、スイートを見つけたときより強くして残し、不安定なテストも同じ扱いを受けます(リトライの注釈ではなく、非決定性を再現してから、それに対して守る)。

根本原因を見つけ、分析を非難なきものに保つ

症状を直すことは、バグを直すことではありません。失敗を真の起源までたどり、各層でなぜかを問い、隠すのではなく取り除ける原因に至ります。本番に届いた欠陥には、インシデント管理(9.3章)の一部として、非難なき根本原因分析を実施します。バグが出荷されて生き延びることを許したシステムとプロセスの条件に焦点を当て、その行を書いた個人には決して焦点を当てません。非難は情報を地下に潜らせ、デバッグは情報で動きます。成果は、修正と、次回その種の欠陥がより早く捉えられるやり方の変更の両方です。

トレードオフ: 長所と短所

アプローチ長所短所
ログと計装本番と分散システムで機能する。時間にわたる振る舞いを捉える雑音、コスト、ログの乱立。タイミングのバグを乱しうる
対話型デバッガー正確で、生きた状態の検査。ローカルのバグに速い制限された、あるいはエアギャップの本番では役に立たない。並行性のバグを隠しうる
まず再現する規律当て推量を測定に変える。失敗するテストを可能にする最初は遅い。本当に引き起こしにくいバグもある
二分探索と二分法見慣れないコードでも速い切り分け信頼できる良い・悪いのテストが必要。バグが相互作用すると難しい
デルタデバッグによる削減巨大な入力をトリガーまで自動的に縮める設定のコスト。失敗が決定的であることを前提とする
今は症状を直す圧力のもとで素早くサービスを復旧する根本原因が戻るのを放置する。負債が積み上がる

中心的な緊張は、速度と確実性です。本番のインシデントでは、サービスを復旧するために、まず出血を止める(ロールバックや症状へのパッチ)必要があるかもしれず、それは正当です。間違いはそこで止まることです。二つの仕事を分けて緊張を解決してください。ユーザーを守るために素早く緩和し、それから再現し、根本原因を見つけ、欠陥を終わったと見なす前に回帰の番人を加えます。フォローアップのない症状の修正は、再び会うことに同意したバグです。

チームで議論すべき問い

  1. 誰かが難しいバグに当たったとき、最初にすることは何で、再現ですか。それとも当て推量ですか。 正直な答えは、チームが共有の方法を持っているのか、私的な民間伝承でいっぱいの部屋なのかを明らかにします。最近の難しい欠陥を声に出して語ってもらってください。確実な再現を最初に得ましたか。それとも、コードを変更してものを再起動し始めましたか。最初に再現するチームは、バグを人から人へ渡せます。再現が一緒に移動するからです。当て推量するチームはできません。すべての試みが再現不能だからです。症状を見る人が、ますますそれを直せる人ではなくなるので、チームが大きくなるにつれてこれはより重要になります。既定が当て推量なら、まず再現することを規範として合意し、きれいな再現を、バグチケットへの入場料にします。

  2. 直したバグは戻ってきますか。そして戻ってきたとき、私たちはそれに気づきますか。 戻ってくる欠陥は、根本原因が取り除かれず、修正がテストで守られなかった欠陥です。直近の四半期のインシデントと再オープンされたチケットを引き出し、先のバグの繰り返しや近い親戚がいくつあったか数えてください。繰り返しの一つ一つが、チームが症状にパッチを当てたか、失敗するテストを飛ばしたか、根本原因分析を早く切り上げすぎた証拠です。直し方はルールです。古い振る舞いで失敗するテストが、新しい振る舞いで合格してスイートに加わるまで、バグは閉じられません。最近繰り返し起きているバグを一つ持ち込み、どんな番人がそれを捉えたかを問ってください。その番人が、あなたが欠いていたものだからです。

  3. 本番システムに触れることが許される方法を考えると、私たちはそもそも本番システムをデバッグできますか。 企業、特に政府の環境では、デバッガーを接続できず、本物のデータで再現できず、監査証跡なしに稼働中のシステムを変更できないことがよくあります。唯一のデバッグ技法がローカルの対話型デバッガーなら、最も難しいバグがいるまさにその場所で盲目です。本番の失敗が実際にどんな証拠を残すかを問ってください。構造化ログ、分散トレース(9.2章)、コアダンプ、再現可能なビルドの成果物。将来のインシデントが診断可能になるよう、既定で何を捕捉しなければならないかを今決めてください。すでに起こった失敗に計装を加えることはできないからです。規制された設定では、同じ跡が監査の義務も満たすことを確認します。

  4. 本番のインシデントが素早い出血止めを強いるとき、その後も根本原因が見つかるようにするにはどうしますか。 インシデントのさなかには、ロールバックや症状へのパッチが、ユーザーを守る正しい最初の動きですが、危険は、サービスが戻った瞬間にチケットが閉じられ、根底にある欠陥が診断されないままになることです。大きなチームでは、ここで負債が見えないまま積み上がります。同じ種類の失敗が、数か月後に別のサービスと別のオンコールのエンジニアで再び現れるからです。直近の重大度1のインシデントをいくつか持ち込んで、それぞれ確認してください。緩和の後に、再現、根本原因分析、回帰の番人が続きましたか。それとも話は「サービス復旧」で終わりましたか。緩和されたインシデントは、根本原因が理解されて守られるまで開いたままにするという明示的なルールに合意し、そのフォローアップを誰が担うかを名指ししてください。企業や政府の設定では、これをインシデント管理のプロセス(9.3章)に結びつけ、インシデント後のレビューが、次の火事が始まると滑り落ちる礼儀ではなく、必須で監査可能なステップになるようにします。

  5. 失敗の事後に、実際にどれだけを再構成でき、既定で何を捕捉するかを決めたのは誰ですか。 すでに起こった失敗に計装を加えることはできないので、あらゆるインシデントの診断可能性は、発することを選んだログ、トレース、メトリクス、ダンプによって、前もって決まっています。相反する考慮はコストと雑音です。高カーディナリティのイベントと完全なトレーシングは無料ではなく、過剰なログは、ストレージを膨らませ、規制された文脈ではデータ保持のリスクも増しながら、シグナルを埋もれさせます。本物の最近のインシデントを持ち込み、それがどんな証拠を残したかを問い、そこから逆算して、捕捉しておけばよかったものと、それを保持するコストを考えてください。どのシグナルを既定でオンにし、どれをサンプリングやオプトインにするかを意図して決め、その決定を偶然ではなく方針となるよう記録します。企業や政府のシステムでは、そのオブザーバビリティの予算の責任者が誰か、そして捕捉された跡が監査、プライバシー、データ所在地の義務も満たすかを加えてください。

  6. デバッグを、教えられ測定可能なスキルとして扱っていますか。それとも、新しいエンジニアは自然に吸収するだけですか。 デバッグは学べるものですが、ほとんどのチームはそれを明示的に教えないので、ジュニアは最も近くにある民間伝承を継承し、まず再現するという方法は、不均一に、あるいはまったく広まりません。緊張は、意図した教え(難しいバグでのペアリング、インシデント後の発見の書き出し、指標の追跡)が、常に他で必要とされているように感じる上級者の時間を要することです。議論に二つの数字を持ち込んでください。繰り返しの欠陥の率と、診断までの時間です。測定できなければ、方法が改善しているのか衰えているのかわからないからです。オンボーディングに本物のデバッグの演習が含まれているか、根本原因の発見が実際にもっと早い検出に反映されているかを検討してください。大きな、あるいは公的な組織では、文書化され測定されたデバッグの実践は、監査人、規制当局、監督機関がますます見ることを期待する、エンジニアリングの厳密さの証拠にもなります。

セクター別の視点

スタートアップ。 少数のエンジニアで余裕もないので、目標は重いプロセスを作ることではなく、バグを再現しやすく、忘れられないものにすることです。git bisect、速いローカルの再現、直したバグごとに失敗するテストを一つ。その習慣は数分しかかからず、出荷しようとしている間に同じ欠陥に再び支払うことを止めるからです。正式なポストモーテムは省いても構いませんが、回帰テストは決して省かないでください。常に負担できるほど小さく、常に保持する価値のある唯一の成果物だからです。

小規模事業者。 専任の信頼性やオブザーバビリティの専門家はおらず、ツールの予算も厳しいでしょうから、スタックがすでに与えるものを好みます。読めるスタックトレース、構造化ログ、買ったフレームワークやホスト型サービスに組み込まれたトレーシング。新しいプラットフォームを評価するときは、それが失敗をどれだけ診断可能にするかを量ってください。何が間違ったかを隠す安いツールは、節約したライセンス料よりはるかに多く、当て推量の時間のコストがかかるからです。まず再現することと、一度に一つ変更することは、無料の規律であり、誰にも余分な時間がないときに最も速く見返りをもたらします。

大企業。 難しいバグはサービスとチームの境界をまたぐので、症状を見る人がその原因を所有することはまれで、共有の方法は、個人のスキルよりも重要です。まず再現する、二分探索による切り分け、修正の前の失敗するテスト、非難なきポストモーテムを、チームを通じて標準化し、一つのリクエストをサービス間で追跡できるよう、分散トレーシング(9.2章)に投資します。デバッグを測定される能力として管理してください。繰り返しの欠陥率と診断までの時間を追跡し、根本原因の発見を早期の検出にフィードバックして、同じ種類の失敗があなたのサービスマップを巡業しないようにします。

政府。 調達規則、制限された環境、公的な説明責任が、そもそもどうデバッグしてよいかを形づくります。本番にデバッガーを接続できなかったり、市民データをノートPCにコピーできなかったりすることがよくあるので、許されたものから診断できるよう設計します。再現可能なビルド、隔離された区画での合成レコード、既定で捕捉される構造化ログとトレース。あらゆる診断のステップとあらゆる変更を監査証跡に記録し、ベンダーに、供給者の言葉に頼るのではなく、失敗を独立して調査できるだけのテレメトリとビルドの再現性を公開するよう求めます。

事例

スタートアップ。 4人のエンジニアのチームは、チェックアウトが一部のユーザーで失敗するのを見続けますが、テストでは決して起きません。当て推量する代わりに、一人のエンジニアが失敗する正確なリクエストのペイロードを再生して確実な再現を捉え、それから無視していたスタックトレースを読み、それが日付解析の呼び出しを指していることがわかります。その週のコミットにわたる素早いgit bisectが、日付ライブラリを切り替えた変更を名指しします。問題のタイムスタンプで失敗するテストを書き、パーサーを直し、テストが緑になるのを見て、スイートに残します。理論を立てる前に再現したので、調査は午後で済み、バグは二度と戻りません。

大企業。 決済プラットフォームが、どのチームにも説明できない断続的なタイムアウトを見ます。症状はチェックアウトに現れますが、原因は三つ先のサービスにあるからです。オンコールのエンジニアが分散トレーシング(9.2章)を使って、失敗する一つのリクエストをサービスの境界をまたいで追跡し、並行する負荷のもとでときどきデッドロックする下流の呼び出しを見つけます。結果がスレッド間の不運なタイミングに依存する、古典的な競合状態です。負荷テストで再現し、失敗する統合テストに捉え、ロックを直し、トレーシングのスパンとアラートを加えて、次の発生が日ではなく分で捉えられるようにする非難なきポストモーテム(9.3章)を実施します。

政府。 給付の機関が、エンジニアが本番にデバッガーを接続できず、市民データをノートPCにコピーできないエアギャップの環境で、ケースシステムを運用しています。照合で計算の欠陥が現れます。チームは、環境が許すものからデバッグします。構造化ログ、隔離されたテスト用の区画に立ち上げられる再現可能なビルド、失敗するケースを再現する合成レコード。あらゆる診断のステップが監査証跡に記録され、修正は証拠としての失敗から合格に変わるテストとともに出荷され、根本原因分析が新しいリリース前のチェックを生みます。再現が合成データを使ったので、市民のレコードが境界を出ることはありませんでした。

ビジネスケース: 動機、ROI、TCO

規律あるデバッグの見返りは、当て推量に費やされなかったエンジニアの時間と、再発しない欠陥で測られます。診断されない断続的なバグは、シニアの時間を何日も消費し、オンコールのエスカレーションを繰り返させえます。まず再現する方法は、それを限りのある、委任可能なタスクに変え、失敗するテストの習慣は、同じ欠陥が来四半期にまたあなたに請求することを止めます。大きな組織全体では、同じバグに二度と支払わないことの複利効果は大きく、リーダーシップがすでに追跡している変更失敗率と平均復旧時間を直接改善します。

総所有コストは、おもに研修とツールで、ささやかです。共有の規約(まず再現する、一度に一つ変更する、修正の前に失敗するテスト)、ツールチェーンにすでに一般的なデバッガーとトレーシング、そして9.2章で述べたオブザーバビリティへの投資が必要です。より大きく隠れたコストは、その代替です。エンジニアが散弾銃的な変更を行い、症状にパッチが当てられて戻り、オンコールの負荷が際限なく増える迷信の文化。オンコールのトイルを減らすだけでも、しばしば投資を正当化し、リーダーシップへの論拠は、習慣と計装への一度きりのコストで、繰り返しのインシデントが減り、復旧が速くなる、と述べるのが最も簡単です。

アンチパターンと落とし穴

  • 散弾銃デバッグ: 一度に多くのことを変更し、修正でさえ原因について何も教えてくれないこと。
  • 再現せずに直す: 要求に応じて引き起こせたことのないバグについて勝利を宣言すること。
  • エラー出力の無視: スタックトレースがすでに除外した原因について理論を立てること。
  • 症状へのパッチ: 根本原因が生き延びて戻ってくる間に、症状を黙らせること。
  • print文の乱立: 仮説に結びついた実験ではなく、雑音を加える、コードに残された散らばったデバッグ出力。
  • 回帰テストを飛ばす: バグは直すが番人を残さず、静かに戻ってこられること。
  • 不安定なテストのリトライ: 根底にある競合状態やハイゼンバグ(観察しようとした瞬間に変わったり消えたりするバグ)をデバッグする代わりに、リトライで非決定性を隠すこと。
  • 非難に基づくポストモーテム: 作者を罰し、デバッグが依存する情報を地下に潜らせること。

成熟度モデル

  • レベル1、開始: デバッグは個人の民間伝承と反応です。エンジニアは当て推量し、散弾銃的な変更を行い、ものを再起動します。バグは症状で直され、再現はまれで、同じ欠陥が再発します。本番はほとんど診断できず、試みが再現不能なので、バグを他の誰かに渡せません。
  • レベル2、発展: 一部のエンジニアは確実に再現し、スタックトレースを読み、デバッガーを使いますが、実践は一貫せず、人によって、チームによって異なります。ログはありますが、雑音が多く構造化されていません。修正は失敗するテストとともに出荷されることもありますが、多くはそうではなく、根本原因分析は誰かが主張したときにだけ行われます。
  • レベル3、標準化: まず再現する、二分探索による切り分け、一度に一つの変更、修正の前の失敗するテストが、組織全体で徹底される文書化されたチームの規範です。二分法とデルタデバッグによる削減は一般的な実践です。本番には構造化ログとトレーシング(9.2章)があり、非難なきポストモーテム(9.3章)が、流出したあらゆる欠陥への標準の対応です。
  • レベル4、管理: デバッグの実践がベースラインに対して測定され、制御されます。繰り返しの欠陥率、診断までの時間、再オープンされたチケット数、回帰テストとともに出荷された修正の割合がチームごとに追跡され、周期的にレビューされます。再現と根本原因の完了は、善意ではなくゲートとして扱われ、ベースラインに対する傾向が、ツール、研修、オブザーバビリティのどこに投資するかを導きます。
  • レベル5、オーケストレーション: デバッグは、品質(2.11章)とインシデント管理(9.3章)に統合された教えられるスキルであり、ループ全体が継続的に適応します。オブザーバビリティが組み込まれているので、ほとんどの本番のバグはデバッガーなしに診断可能で、解決したすべてのバグが回帰スイートを強くし、根本原因の発見が早期の検出にフィードバックされるので、欠陥の種類が再診断されるのではなく予防されます。組織はシステムと失敗モードの進化に応じて労力を再均衡させ、繰り返しの欠陥率は下がり続けます。

議論のためのアイデア

  1. 最近のバグのうち、誰かがコードを変更する前に確実に再現されたものの割合はどれくらいで、その割合はあなたの方法について何を語りますか。
  2. 回帰が現れたとき、チームは二分法に手を伸ばしますか。それとも、誰かが見つけるまでコードを手で読みますか。
  3. 今日、本番システムはどれだけ診断可能で、すでに起こった失敗について何を捕捉しておけばよかったと思いますか。
  4. 修正は一貫して、失敗してから合格するテストとともに出荷されていますか。そうでないなら、その規律はどこで崩れますか。
  5. 不安定なテストはどう扱いますか。非決定性をデバッグしますか。それともリトライで覆い隠しますか。
  6. デバッグは新しいエンジニアに意図して教えられていますか。それとも、民間伝承を自然に吸収するに任されていますか。

要点

  • デバッグは仮説の検証です。確実に再現し、エラーとスタックトレースを読み、それからスキャンではなく二分探索と二分法で切り分けます。
  • 失敗を最小の再現可能な例まで減らし、大きな入力にはデルタデバッグを使います。取り除くすべての要素は除外した原因だからです。
  • ツールをバグに合わせます。本番と分散システムには計装とトレーシング(9.2章)、ローカルの調査には対話型デバッガー。
  • 修正する前にバグを捉える失敗するテストを書き、修正が証明され、欠陥が永久に守られるようにします(2.4章)。
  • 根本原因を見つけて取り除き、非難なきポストモーテム(9.3章)を実施し、デバッグを民間伝承ではなく、学べるスキルとして扱います。
  • 一度に一つ変更します。散弾銃的な変更と症状へのパッチは、証拠を破壊し、バグを招き戻します。

参考文献とさらなる読み物

  • David J. Agans, Debugging: The 9 Indispensable Rules for Finding Even the Most Elusive Software and Hardware Problems
  • Andreas Zeller, Why Programs Fail: A Guide to Systematic Debugging
  • Andreas Zeller and Ralf Hildebrandt, “Simplifying and Isolating Failure-Inducing Input” (the delta debugging algorithm)
  • Brian W. Kernighan and Rob Pike, The Practice of Programming (chapter on debugging)
  • Andrew Hunt and David Thomas, The Pragmatic Programmer (the chapters on debugging and assertions)
  • Steve McConnell, Code Complete: A Practical Handbook of Software Construction (the debugging chapter)
  • John Regehr, “Reducers Are Fuzzers” and related writing on test-case reduction
  • Charity Majors, Liz Fong-Jones, and George Miranda, Observability Engineering (debugging production with high-cardinality telemetry and tracing)
  • Betsy Beyer, Chris Jones, Jennifer Petoff, and Niall Richard Murphy, eds., Site Reliability Engineering (blameless postmortems and production debugging)