Skip to content
Published on

デバッグはなぜ置き換えにくいのか — 消去の技術と仮説の一文

シェア
Authors

直ったで止まる人と、なぜを問う人

バグが一つあります。二人が取りかかります。

一人は怪しいところを直して再読み込みします。だめなら別のところを直して再読み込みします。何度か繰り返すうち、ある瞬間に症状が消えます。そこでコミットして次へ進みます。

もう一人は症状が消えた地点で止まりません。今何を変えたかを辿り直し、その変更がなぜ症状を消したのか説明がつくかを確かめます。説明できなければ、まだ終わっていないと数えます。戻して症状が再び出るかも見ます。

二人の差はこのバグではほとんど表に出ません。出るのは三か月後、同じ原因が別の顔で戻ってきたときです。

デバッグは生成ではなく消去です

第1回で、値が残る技術とは検証が高いままの技術だと書きました。デバッグはその定義にほぼ完全に当てはまります。作業の向きがコードを書くのと逆だからです。

コードを書く仕事は、可能なものの中から一つを作り出すことです。作ってしまえば存在そのものが成果物です。デバッグは逆に、可能な原因を一つ残るまで消していく仕事です。消すことには成果物が残りません。残るのは排除した仮説の一覧だけで、その一覧はたいていどこにも記録されません。

そしてデバッグに必要な入力はコードの中にありません。いつからそうなのか、最近何がリリースされたか、この症状は特定の顧客だけに出るのか、三週間前に誰がどの設定に触ったか。これは検索できるテキストではなく、組織に散らばった状態です。この情報を集めることが、すでに作業の半分です。

仮説は反証できる一文であること

デバッグを学んだ人と、ただ長くやってきた人を分ける地点がここです。仮説の形です。

「ネットワークの問題っぽい」は仮説ではありません。外れたとき何が見えるかが決まっていないので、どんな結果が出てもこの文は生き残ります。生き残る推測は絞ってくれません。

使える仮説はこういう形です。「リクエストがゲートウェイからアプリケーションへ渡る区間で落ちている。であればゲートウェイのアクセスログには5秒のタイムアウトが記録され、アプリケーションのアクセスログにはこのリクエストIDが一切ないはずだ」。

違いは後半の文にあります。確かめるものが指定されていて、予想と違えば仮説が死にます。死ねる仮説だけが候補を減らします。

例 — 変更する前に書く三行

仮説:   リクエストはゲートウェイとアプリケーションの間で落ちている
正なら: ゲートウェイのログに5秒タイムアウト、アプリのログに該当IDなし
誤なら: アプリのログにリクエストが届き、処理中に失敗した痕跡がある

観測結果:
次の仮説:

この三行を書くのに一分かかります。その一分が惜しく見えますが、書かないと三十分後に、自分がすでに排除したものをもう一度確かめています。複数人が入った障害では効果がさらに大きい。排除済みの一覧が共有されないと、三人が同じものを三回確かめます。

システムを層に切る

仮説を絞る最も信頼できる手順は二分法です。コミット履歴を二分するのと原理は同じで、対象がコミットではなく層になります。

まずリクエストやデータが通る層を順に書きます。クライアント、DNS、ロードバランサ、ゲートウェイ、アプリケーション、キャッシュ、データベース、外部連携。そしてちょうど真ん中を選んで問います。ここまでは正常か。

肝心なのは、正常かどうかを判定できる地点を選ぶことです。判定できない地点で切ると半分を消せません。だから良いシステムとは切れる地点が多いシステムであり、オブザーバビリティという言葉が実質的に指すのもこれです。

一層ずつ順に見る方式と比べると差は大きい。八つの層を順に見れば平均四回、半分ずつ切れば三回で終わります。層が二十になれば十回対五回になります。そして実際の障害では一回の確認に数分かかるので、この差はそのまま復旧時間です。

再現しない問題の扱い方

最も難しいのは再現しない問題です。一日に二回ほど起き、見ようとすると起きません。

ここでよくある失敗は再現を目標にすることです。何日も再現に費やして作れずに終わります。向きを変えたほうがよい。目標を再現ではなく次回の観測へ移すことです。

手順はこうです。まず、この症状が起きたときどんな証拠があれば原因を特定できるかを一覧にします。次に、その証拠が今残っているかを確かめます。たいていは残っていません。ならば残るように計測を仕込んで待ちます。次に起きたとき、証拠が手に入ります。

この進め方の利点は、失敗しても残るものがあることです。仕込んだ計測はこのバグでなくても次のバグで使えます。一方、再現の試みは失敗すると何も残しません。

注意もあります。タイミングや競合が原因の問題では、計測を入れる行為そのものがタイミングを変えて症状を隠します。その場合は実行経路に割り込まない観測を選びます。すでに出ているログの時刻を突き合わせる、標本抽出で負荷を下げる、事後に状態をダンプする、といった方向です。

なぜ年数と差が比例しないのか

デバッグは経験を積めば自然に伸びそうですが、実際にはそうなりません。理由が三つあります。

一つめは、明示的に教える場所がほとんどないことです。言語やフレームワークは習いますが、絞る手順は肩越しに覚えます。手順を持った人が隣にいなければ、十年やっても推測と変更の反復に留まります。

二つめは、フィードバックの掛かり方が間違っていることです。症状が消えれば報酬が来ます。なぜ消えたか分からない状態も同じように報酬を受けます。だから運で直った経験が実力と取り違えられます。

三つめは、良いデバッグは痕跡を残さないことです。三十分で正確に絞って直した人のコミットと、三時間さまよって偶然直した人のコミットは同じに見えます。だから組織はこの能力を見つけにくく、見つけられないものは育ちません。

これへの対応は痕跡を残すことです。排除した仮説と確認方法を短くでも課題に書けば、その記録が自分の訓練になると同時にチームの資産になります。

手を動かす

今週出会うバグを一つ選び、コードを直す前に上の三行を書いてみてください。仮説、正なら見えるもの、誤なら見えるもの。三行目が書けないなら、それはまだ仮説ではなく感覚です。

  • Linux ターミナル シミュレータ — ログ・プロセス・ネットワーク状態を手で確かめる練習を安全に反復できます。
  • FDEエンジニア育成RPG — 限られた時間とアクセス権の中で原因を絞り診断を確定するミッションが31あります。ドメインのレベルが上がると同じログが違って読めるように設計されています。

この助言が通じない場合もあります。サービスが今落ちている間は、診断より緩和が先です。ロールバックするかトラフィックを回して出血を止めてから絞ってください。仮説をきれいに書くのは、利用者が待っていないときにやることです。

続けて読む

高いまま残る技術シリーズ