プログラムでときどき妙なエラーが出るという報告を受け、原因を探そうとデバッガを起動すると、うそのようにエラーが消えてしまうことがあります。確認用の出力を1行入れただけで問題が止まり、その行を消すとまた現れることもあります。開発者たちは、こうしてのぞき込んだ瞬間に隠れてしまうバグに、物理学者の名前を借りたあだ名を付けました。それが「ハイゼンバグ(Heisenbug)」です。
ハイゼンバグとは、簡単に言えば、原因を調べようとすると消えたり振る舞いが変わったりするソフトウェアのバグを意味します。観察する行為そのものが対象の状態を変えるという観察者効果になぞらえた言葉で、再現や追跡が特に難しいバグを指すときに使います。
本記事では、ハイゼンバグという名前の由来から、バグが隠れる理由、一緒に使われるバグの呼び名、そしてハイゼンバグを捕まえる方法まで、順に見ていきます。
見ようとすると隠れるバグ、ハイゼンバグ
ハイゼンバグという名前の由来

このあだ名は、物理学の有名な原理とプログラマーたちの冗談が出会って生まれました。
(1) 物理学者の名前を借りた言葉遊び
ハイゼンバグは、不確定性原理を提唱したドイツの物理学者ヴェルナー・ハイゼンベルク(Werner Heisenberg)の名前に、虫を意味するバグ(bug)を重ねた言葉遊びです。観察しようとした瞬間に対象が変わってしまうという観察者効果を連想させる意図が込められています。
(2) 記録に初めて現れた時期
知られている最も古い記録は、1983年の米国計算機学会(ACM)の出版物です。1985年にはデータベース研究者のジム・グレイ(Jim Gray)がソフトウェア障害を扱った論文でこの言葉を使ったため、彼が作った言葉だと誤って伝えられたこともあります。
(3) 電子工学の古い隣人
電子工学には、似た現象を指すプローブ効果(probe effect)という言葉が先にありました。測定用のプローブを回路に当てるだけで、装置の動作が変わってしまう現象です。
ただし、この名前には批判もあります。不確定性原理と観察者効果は別の概念なのに、あだ名が両者をひとつのように混ぜてしまっているという指摘です。そのため、ハイゼンバグは正確な科学用語というより、開発現場の気の利いた隠語として受け止めるのがよいでしょう。
ハイゼンバグはなぜ隠れるのか

バグが本当に人の顔色をうかがっているわけではありません。原因を調べるための道具や方法が、プログラムの実行条件を少しずつ変えてしまうからです。
(1) 実行時間が変わる
デバッガで1行ずつ実行すると、プログラムは普段よりずっと遅くなります。複数の処理が同時に動くプログラムでは、処理の順序が少しずれるだけで起きる競合状態(race condition)がよくありますが、速度が変わるとそのずれが起きなくなることがあります。
(2) メモリ配置が変わる
確認用の出力文を入れたり、デバッグ用にビルドし直したりすると、変数が置かれるメモリ上の位置が変わります。初期化していない変数や、見当違いの場所を指すポインタが原因なら、位置が変わった瞬間に症状も一緒に変わります。
(3) 最適化の有無が変わる
性能のために最適化したプログラムでだけエラーが出て、デバッグのために最適化を切ったプログラムでは問題なく動くこともよくあります。たとえば計算値をレジスタに置くときとメモリに移すときで精度が異なり、浮動小数点数の比較結果が変わることがあります。
結局のところ、ハイゼンバグとは、別に実在する本物の欠陥が、実行条件によって表に出たり隠れたりしている姿です。症状が消えたからといって原因まで消えたわけではない、という点が肝心です。

ハイゼンバグと一緒に使われるバグの呼び名

ハイゼンバグが広く使われるようになると、科学者の名前を借りたバグの呼び名がいくつも生まれました。
- ボーアバグ(Bohrbug):物理学者ニールス・ボーアの原子模型のように決まった規則に従い、同じ条件ならいつも同じように現れるバグです。再現しやすいため、比較的捕まえやすいバグです。
- マンデルバグ(Mandelbug):原因があまりに複雑で、振る舞いが混沌として見えるバグです。フラクタルを研究した数学者ブノワ・マンデルブロの名前に由来します。
- シュレーディンバグ(Schrödinbug):コードを読んでいた開発者が、そもそも動くはずのないコードだったと気づいたときから、実際に問題を起こし始めるバグです。
- ヒッグスバグソン(Higgs-bugson):ログやユーザーからの報告を見ると確かに存在しそうなのに、開発環境では再現が難しいバグです。
これらの呼び名の多くは冗談から始まりましたが、バグを再現できるかどうかや、原因がどれほど複雑かを短く言い分けるのに役立ちます。
ハイゼンバグを捕まえる方法

ハイゼンバグは厄介ですが、アプローチを変えれば十分に捕まえられます。
(1) 実行を止めずに記録を残す
デバッガでプログラムを止める代わりに、実行の流れに影響しにくい軽い記録を残し、問題が起きた後で調べます。観察が与える影響を減らすことが第一の原則です。
(2) 同じ条件をそのまま再現する
問題が起きたビルド設定、入力値、実行環境をできるだけ同じにそろえます。実行過程を記録しておき、そのとおりに再生できるツールを使う方法もあります。
(3) 原因の候補を絞り込む
初期化していない変数、誤ったポインタ、処理間の順序の問題など、ハイゼンバグをよく引き起こす原因から順に点検します。コード検査ツールやメモリ検査ツールが、この作業を大きく助けてくれます。
(4) 消えたからといって終わりにしない
コードを1行変えたら症状が止まった場合、バグが直ったのか、しばらく隠れているだけなのかを見極める必要があります。原因を説明できたとき、はじめて解決したと言えます。
ハイゼンバグは、観察する道具さえもシステムの一部であることを開発者に気づかせてくれます。バグが見えないときでも、実行条件が何を変えたのかを問う姿勢が必要です。
症状が消えた瞬間に満足せず、その理由まで最後まで追いかけること、それこそが信頼できるソフトウェア開発の第一歩です。