ちょっと話題の記事

システムのバグと欠陥の哲学

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

よく訓練されたアップル信者、都元です。うわータイトル大きく出たなオイ感がありますが。本日は珍しく、なんか抽象的なポエムを撒き散らしたいと思います。

残念ながら、本稿は普遍的なものの考え方を示すものではなく、筆者の中で「こう考えると良いのではないか」という提案に過ぎません。中で独自に言葉を定義したりしていますし。

だいぶ色んな人の心を ざわ… ざわ… とさせそうなネタですが。勇気を持って行きます。

誰もいない森で木が倒れたら音はするのか?

私は哲学に詳しいわけでも何でもないので深い話はまったくよくわからないのですが、哲学の問いかけでよくあるものらしいです。

素朴に考えると「いや、倒れたらさすがに音するでしょ」ということで 音がする が回答になると思います。

一方で「音」は「物が動き、こすれ、また、ぶつかって出る空気の震え(=疎密波)が耳に届いて聞こえるもの。」という説明がありました(by Google先生)。確かに空気は震えたかもしれないけど、誰の耳にも届いていないため、定義を満たしていないので 音はしていない が回答になると思います。

結論として、音がするかどうかは「音」の定義によります。

あるシステムに、欠陥は存在するのか?

利用者があるアウトプットを期待してシステムを操作した時、その結果が期待と異なっていました。そのシステムには欠陥が存在するでしょうか? そしてバグは存在するでしょうか?

まぁ、「欠陥」や「バグ」の定義によります。のでこれを定義していきたいと思います。

利用者の【期待】とシステムの【仕様】と【実装】

システムに対して利用者が操作を行う時、それに対する「利用者の期待」が存在します。

期待 (Expectation)
利用者がシステムに対してある操作をした時に、こうなるであろうという想定。

システムが利用者の期待と異なった動きをした時、多くの場合それは欠陥と呼ばれることが多いのですが、状況を細かく整理していくと、期待外れであるからといってシステムの欠陥ではないことがあります。

要するに「利用者の期待」というのは主観的で曖昧なもので、そこを基準とした議論はなかなか厳密になりません。状況を整理するために、さらに「仕様」と「実装」という概念を導入します。

仕様 (Specification)
仕様とは、システムのあるべき姿。こうであるべき、という定義。自然言語で表現する。
実装 (Implementation)
実装とは、システムの仕様を実現することを目標に作られた実体や運用。プログラミングによって表現することが多いが、自然言語で表現する(手順書等)こともある。

システムの【欠陥】と【バグ】

では欠陥とは何でしょうか。本稿では「仕様と実装が食い違った状態」と定義したいと思います。また、バグという言葉も良く使われますが、本稿では「期待・仕様・実装のうち何れか1つ以上が食い違った状態」と定義したいと思います。

つまりバグは欠陥を完全に内包する概念で、欠陥であれば必ずバグとなります。逆に、バグであったとしても必ずしも欠陥ではありません。

そして欠陥は「実装バグ」または「仕様バグ」に分類できます。欠陥は実装または仕様を修正して矛盾のない状態に是正すべきものですが、前者は「fixにあたって実装を修正すべきもの」、後者は「fixにあたって仕様を修正すべきもの」です。

さてここで、皆さんよくご存知の「エレベータ」というシステムを例に考えてみましょう。話を進めるためには本来はエレベータの仕様をきちんと定義しなければならないんですが、多くの人はエレベータの仕様を概ね理解していると思いますし、ガチで定義しだすとそこそこの量になってしまうので多くを省略させてください。(でも、実際のシステムでこれをやっちゃダメなんですよ!? こういう甘いことを言って仕様が曖昧になっていくのです…。この部分は反面教師にしましょう。

1. 正常

今エレベータは1Fにいて、利用者は7Fから1Fに移動したいと思っています。この状況で、▼ボタンを押せばエレベータが7Fまで上昇して扉が開きます。そして、エレベータに乗り込んで中の1Fボタンを押せば、エレベータが1Fまで下降して扉が開き、利用者は1Fに移動できます。

エレベータの仕様を忠実に実装したエレベータの実体があり、そして利用者が上記の通りの操作をした時、結果は利用者の期待通りとなります。当たり前ですが。。。

仕様 実装 期待 外部影響 結果
正常 A→X A→X A→X なし A→X

仕様が「Aという操作をした時にXという動作をするべき」となっており、実装がそれに忠実に「Aという操作をした時にXという動作をする」ように作られています。

このシステムに対して、利用者は「Aという操作をしてXという動作を期待」しました。外部影響(後述)は特筆すべきものはありません。

その結果システムが「Xという動作」をしました。平和ですね。こうありたいものです。

2a. 欠陥 (Defect) ― 恐らく実装バグ

エレベータの実装に仕様との乖離があり、利用者の操作に反して11Fに連れて行かれたとします。これぞ欠陥です。バグの一種でもあります。

仕様 実装 期待 外部影響 結果
欠陥 (実装バグ) A→X A→Y A→X なし A→Y

システム開発者は、これを不具合と認識して是正すべきでしょう。

2b. 欠陥 (Defect) ― 恐らく仕様バグ

これはエレベータの例で例えづらいのですが、仕様側に欠陥があった場合です。これもバグの一種ですね。

仕様 実装 期待 外部影響 結果
欠陥 (仕様バグ) A→Y A→X A→X なし A→X

結果として利用者の期待通りにシステムが動いているので表面的な問題はありませんが、仕様と実装の間には矛盾があり、問題がある状態です。こちらについても是正すべきでしょう。

3. 違反 (Fault) ― 恐らく期待バグ

利用者が、1Fに行きたいにもかかわらず、乗り込んだ後に1Fボタンを押しませんでした。利用者の操作が間違っているパターンで、これを「違反」と呼びます。仕様と実装の間に矛盾は無いので欠陥ではありませんが、バグの一種です。一般的な用語ではありませんが、期待バグと呼べばいいでしょうか。

仕様 実装 期待 外部影響 結果
違反 (期待バグ) A→X A→X B→X なし B→Y

これはまず、利用者の期待を一旦仕切りなおしてもらう必要があるかもしれません。このシステムはそういうものじゃありませんので使い方を改めてください、と。この場合、バグではありますが、システム側に責はありません。

もしくは、そもそもシステム開発者側が「利用者の要求」を解釈し損なっていると捉え、仕様と実装の両者を修正することによって利用者の期待通りの結果を得られるようにする、という可能性も無くはありません。その場合は違反ではなく、実装バグ(2a)と仕様バグ(2b)の合わせ技であるという解釈です。

4. 失敗 (Failure)

エレベータが7Fから1Fに移動中、停電が発生しました。その結果エレベータは中途半端な階で止まってしまい、閉じ込められてしまいました。これも利用者の意図に反した動きですが、システムの欠陥には分類しません。システムは正しく目的を達成しようとしましたが、外部要因によってそれを果たせなかった。これを「失敗」と呼びます。

仕様 実装 期待 外部影響 結果
失敗 A→X A→X A→X 外部異常 A→Y

システムそれ自体に欠陥がなくても、依存する外部システムに起因して、想定外の動きをすることがあります。停電はエレベータシステム自体の欠陥ではなく、どちらかというと給電システム(エレベータでが依存している)に原因がありますね。これを「失敗」と呼びます。

5. 意外 (Unexpectation)

エレベータが7Fから1Fに移動中、地震が発生しました。その結果エレベータは最寄りの3Fに止まってドアが開きました。これも利用者の意図に反した動きですが、システムの欠陥ではありませんよね。むしろ地震発生時には最寄り階にすみやかに停止すべきである、という仕様があるはずです。なので失敗でもありません。

このように、利用者が日頃意識していなくても、仕様の中で明示的にこのような例外ケースを定義することもあります。これを「意外」と呼びたいと思います。

仕様 実装 期待 外部影響 結果
意外 A→X A→X A→X 複雑な外部影響 A→Y

最近筆者が実体験したエレベータの「意外」がもう一つありましたのでご紹介しておきます。

  • 7Fにおいて▼ボタンでエレベータを呼びました。
  • エレベータが来たので乗り込みました。
  • ドアが締まると突然エレベータは上昇を始め、11Fで開きました。
  • ドアが締まると今度は下降を始めました。
  • 7Fで止まり、扉が開きました。
  • ドアが締まり、最終的には1Fにたどり着きました。

何が起こったかわかりますでしょうか。この動きの中では(敢えて記述しませんでしたが)3人の利用者がいたのです。

  • 1Fから7Fに移動したい人(Aさん)
  • 11Fから1Fに移動したい人(Bさん)
  • 7Fから1Fに移動したい人(筆者)

筆者が7Fでエレベータに乗り込んだ際、Aさんとすれ違いました。そして、11FではBさんが乗り込んできました。筆者はAさんとすれ違いでエレベータに乗り込むべきではなかったんです。

これも違反(乗り込むべきでないタイミングで乗り込んでいる)や失敗(外部影響により動きが変わっている)に分類できるとも言えますが、これを「違反」と呼んでしまうのは厳しい気もするし、仕様と実装の間に矛盾はなく、目的達成に失敗したわけでもないので、敢えて別分類としてみました。

命名はいまひとつピンと来ていないのですが、筆者はこれを「意外」と呼ぶことにしました。

まとめ

上に示した表をまとめてみました。

仕様 実装 期待 外部影響 結果
正常 A→X A→X A→X なし A→X
欠陥 (実装バグ) A→X A→Y A→X なし A→Y
欠陥 (仕様バグ) A→Y A→X A→X なし A→X
違反 (期待バグ) A→X A→X B→X なし B→Y
失敗 A→X A→X A→X 外部異常 A→Y
意外 A→X A→X A→X 複雑な外部影響 A→Y

あらためて当初の命題を見てみましょう。

利用者があるアウトプットを期待してシステムを操作した時、その結果が期待と異なっていました。そのシステムには欠陥が存在するでしょうか? そしてバグは存在するでしょうか?

以上の定義上、これはバグではありますが、必ずしも欠陥があるとは限りません。というのが結論です。

そもそも仕様のない実装に、欠陥は存在するのか?

しません。が、仕様が無いこと自体が欠陥かもしれませんw

もし仮に仕様が無い場合。無いものと矛盾することは出来ないので、仕様のない実装に欠陥は存在しません。これは欠陥の無い最強の存在とも考えられますが、利用者に対して何も約束をしない(=価値を提供しない)最も無意味な存在ともいえます。

まとめ

このような考え方は、問題が発生した時に状況を整理するフレームワークとして活用できるのではないかと思っています。

そして仕様を定義することの大切さを感じておりますw そもそも仕様が無ければ実装の存在価値もかなり薄いわけです。そして、仕様がないと、問題が発生した時に何が悪いのか分かりません。

仕様を定義しよう。

参考

What is a bug? 2002 Anthon Pang.

上記サイトは閉鎖してしまったようですが、インターネットアーカイブから参照できます。