【42】完全な実行という誤った考え
頑張れば欠陥の少ないコードが作れるんだと思っていても、恥じる必要はありません。ほとんどの人がそう思っています。しかし残念ながら、それは不可能です。理論的にも不可能なのです。
プログラムロジックのような任意性の高い論理(任意の論理)は一般的に検証困難であり、完全にテストすることは困難もしくは不可能です。最近、英国の 3 人の研究者が建築関係で使われる「れんが」と「はり」のたとえ話を引用しながら、ソフトウェアは検証困難であることを説明しました。「(ソフトウェアには)十分に予測可能な構成要素がなく、プログラムを構成する要素、文、手続き、オブジェクトなどは予測可能な方法では組み立てられない」ためです。
ソフトウェアの構成要素は、レゴのようにぴったりかみ合うものではありません。ソフトウェアは非常にさまざまな方法で組み立てられます。したがって、すべての組み合わせを決定することはできません。これはチューリング完全のそこそこ実用的な定義かもしれません。要するにソフトウェアは複雑なのです。
コードにおける任意の論理を追跡、検証すると聞くと難解に思うかもしれませんが、プログラマの意図を追跡するというもっと単純な作業ならどうでしょう? 確かに私たちはプログラマと話をして、彼らの意図することを尋ねることができます。ところが残念なことに、プログラマの意図というのはコードを書いてから数日のうちに失われてしまいます。要求が変わったり、ドキュメントに矛盾がある場合にはなおさらです。
またプログラマは、ドキュメントのないコードやドキュメントに間違いのあるコードを残して、別の仕事に取りかかります。そうなると、残されたソースコードだけがプログラマの意図を探る最後かつ唯一の手掛かりとなります。結局のところ、プログラマの意図は、変数名やロジックフロー、たまにあるコメントといった手掛かりから、不完全ながらも判明するだけです。
おそらくほぼすべての出荷済みのソフトウェア製品には、必ずどこかにバグが残っています。私たちがソフトウェアにバグを入れてしまう理由には、ひどいものもあれば(言語機能についての無知や細部への注意不足など)と正当なもの(矛盾していたり間違って伝えられた要件など)もあります。さらに、バグはソフトウェアの変更理由にもなります。バグが見つかると、そのバグを修正するためにコードをリファクタリングしますが、その過程でまた新たなバグが入ってしまうのです。
1969 年にメイア・マニー・リーマンは、ソフトウェアがそのライフサイクルにおいて進化することに気づきました。彼はのちに、ソフトウェア開発には複数のフィードバックループが存在し、それらフィードバックループが進化のプロセスに影響を及ぼすことを解明しました。そこには複数の(おそらく矛盾のある)要件や設計判断が入ることも含まれます。
要件や設計の判断、実装詳細に関するプログラマの理解度は、ほかのフィードバックループにも影響を与えます。すなわち、バグの原因は論理的なプロブラミングエラーとは限らないということです。意見の食い違いからもバグは入り込む可能性があります。
「完全な実行」という誤った考えとは、細部に十分注意していれば欠陥の少ないコードが作れるという妄想のことです。これが正しければ、私たちはみんな構造化プログラミングの絶大な支持者になっていたでしょう。私たちはそうではありませんし、それには正当な理由があります。進化のいかなる段階においても、ソフトウェアはバグを抱えており、絶えず変化し、不正確なドキュメントがあるものなのです。
このような見方をすることで、単純かもしれませんが、これまでとは異なるソフトウェアへの取り組みが必要であることがわかります。ソフトウェアの実装、要件、ドキュメントをインクリメンタルにリファクタリングするためのツールやテクニックの開発が求められているのです。