プログラマーの理想と現実

第2回 一発完動プログラミング

2014.09.10

この記事は、『UNIX Magazine』2003年9月号(2003年8月18日発売)に掲載された同名記事の初稿(著者から編集部に提出したもの)を元に、Web掲載用に一部を修正したものです。10年以上前に執筆したものなので、現在のUNIXを取り巻く環境とは色々と異なることがありますが、プログラミングに対する心構えとしては現在でも通用するものと思い、再掲してみることにしました。

・・・・・・・・・・

デバイスドライバの開発においては、一発完動プログラミング の実践が本質的に重要であり、それがまたデバッグ段階での作業量にも大きく関係してくることは、前回述べた通りである。多くのプログラマが一発完動プログラミングを習得していてくれれば良いのだが、少なくとも筆者の見る限り、一発完動プログラミングを習得しているプログラマの割合は、年々どんどん減っているように思える。

一発完動プログラミングが習得されないのは何故か?

一発完動プログラミングを習得しているプログラマの減少の度合いは、プログラム開発環境の充実度に比例しているように感じられる。開発環境が充実するに従い、少なくとも、ごく普通のユーザプロセスの開発では、一発完動プログラミングを実践せずとも、動作するものが得られるようになって来ているということである。

筆者が職業プログラマをはじめた約20年前と、現在のプログラム開発環境を比べて、大きく異なっている事項を挙げてみよう。これらの違いにより、一発完動プログラミングの習得が妨げられているように思えるのだ。

プロセッサの処理速度向上

20年前は、たとえば高々数千行のC言語プログラムをコンパイルするにも10~20 分かかっていた。もし、単純なタイプミスや型宣言の間違いなどがあり、コンパイル終了直前でエラーになったとしたら、ほんの数文字の修正で、また10~ 20分待たねばならない。アルゴリズム的な誤りについても同様である。単純な誤りであれば、コンパイル完了後、ちょっとした動作確認で誤りに気が付く。そしてまた10~20分待たねばならない。これは時間的に大きな無駄である。結果として、「一回のコンパイルで絶対に通してやる」という意識をもって、注意深く、誤り無くコードを書くことを心掛けるようになった。

翻って、最近ではプロセッサ性能の極端な向上により、コンパイル時間は非常に短くなっている。数千行のC言語プログラムでも1~2秒で済む。タイプミスや型宣言の間違いなど、コンパイラがすぐ教えてくれるし、修正して再度コンパイルしても時間の無駄などないに等しい。アルゴリズムの誤りがあり、それを発見したとしても、「この段階で確実に直す!」という意識は無くても構わない。幾つか試してみてうまく行くものを探せば良いのだ。

処理系の安定性向上

20年前は処理系にバグが多かった。システムコールも、標準ライブラリも、コンパイラも、デバッガも、実に多くのバグを抱えていた。自分が書いたプログラムが想定どおりに動かなかった時、自分のプログラムが誤っているのか、標準ライブラリのバグを踏んだのか、コンパイラが発狂して正しいオブジェクトを吐かなかったのか、そう簡単には判断できないのだ。

こういった状況下で動作不良の原因を突き止めるには、「自分の書いたコードは正しい!」という確固たる自信が重要になってくる。「正直に、心の底から自分のコードは正しい!」と思えるなら、バグは処理系にあると想定して調べる。「正直なところ、自分のコードは不安である......」と感じるならば、まず自分のコードを見直す。つまり、常に「自分の書いたコードは正しいのか?」と自問しながらプログラミングする必要があったのだ。

翻って、最近の処理系では、システムコールも、標準ライブラリも、コンパイラも、デバッガも、実にバグが少なく安定している。自分が書いたプログラムが想定どおりに動かなかったならば、ほぼ間違いなく自分の誤りである。自分の書いたコードに自信を持っていないと後で損をする、という状況に遭遇しないので、「自分のコードは正しいのか?」などと自問しないだろう。

デバッグ環境の充実

20年前にはソースコードレベルデバッガなど存在しなかった。オブジェクトレベルのシンボリックデバッガである adb が使えるのみ。たとえばSIGSEGVでcore を吐いたとしよう。adbをcoreファイルとともに起動すれば、SIGSEGVを引き起こしたマシン命令と、アクセスしようとしていたレジスタやメモリアドレスがわかる。しかしそれは一体自分の書いたC言語のどの行で、どの変数なのか?

これを知るには、関数のスタックフレームとレジスタ割り付けを追っかけ、マシン命令列を逆コンパイルしてC言語の文を特定するという、かなり面倒な解析を行わねばならず、時間が掛かる。まあ、慣れてしまえばそれほど苦では無いが、それでもソースコードレベルデバッガに教えてもらうよりは格段に時間が必要である。だから、最初からコードをきちんと書けば避けられるような、単純だけれども見つけにくいバグでadbを使ったりはしたくない。最初からきちんとコードを書くことを心掛ける方がずっと楽だと考えるわけである。

デバッガが使いにくいならば、俗称 printfデバッグ を多用したのか、というと、そういうわけでもない。なぜなら、前項で示したように、コンパイラやライブラリのバグが多く、printfを混ぜることによって動作状況が変わってしまうことなどザラだったからだ。ここにprintfを入れると動くようになる、ここの printfを取ると動かなくなるなど。

printfデバッグでは、観測対象であるプログラムにprintfという観測プローブを突っ込むわけであるから、観測によって対象が乱されるのである。近代科学の教えるところによると、科学的観測とは、観測対象を乱さず、乱すとしても理論的に無視できる範囲で留めることにより成立する。プログラミングも科学なのだから、この原則には従わねばならない。printfデバッグは簡便で強力だが、この原則からするとあまり科学的ではないのだ。

さて、翻って最近のデバッグ環境を見ると、ソースコードレベルデバッガは使えて当然だし、printfデバッグだってコンパイラやライブラリの安定性が格段に増したことで、観測対象を乱す度合いは相当に低くなっている。書いたコードに誤りがあったとしても、それを容易に見つけ出す手段が色々と提供されているわけである。このような状況では、バグを見つけるのが大変だから最初からきちんと書こう、という意識は低くなってもやむを得ないと言える。

他にも、細かい違いは幾つもあるが、大きな要素は上記の3点くらいであろうか。要するに、20年前のプログラマにとって、「一発完動プログラミング」は仕事を遂行する上で必須の技量だったのだ。20年前の環境でしばらくプログラミングを行っていれば、一発完動プログラミングに習熟せざるを得なかったわけだ。

一発完動プログラミングは必要なのか?

ここまでの流れを素直に読むと、本稿の主旨を次のように解釈する読者もいるかもしれない。

  • 主張1 一発完動プログラミングは重要である。
  • 主張2 一発完動プログラミングを習得していないプログラマが多くなってきて問題である。
  • 主張3 一発完動プログラミングが習得できない原因は開発環境が進歩したせいである。
  • 主張4 現在のぬるま湯的開発環境に浸っていてはダメだ。20年前に戻すべきだ。

これらの主張は正しいと言えるのであろうか?

プログラミング環境の進歩

概して、工学における進歩というものは、専門的な知識や経験を持たない者が、より専門的な仕事を成し遂げられる方向に進むものである。自動車を例に取ろう。30年前の自動車は、エンジンがかからなかったり、オーバーヒートしたり、色々とトラブルが起きた。この時代においては、まともに自動車を運転・運用できるのは、エンジンなどの整備方法を習得している者のみであった。

しかし現在では、エンジンの整備方法など知らなくても、自動車を安全に運転・運用することは可能である。自動車がこのように進歩したおかげで、専門的な知識を持たない者にも、便利な移動手段が利用できるようになり、社会的には大成功というわけである。その代償として、自動車の構造はより複雑怪奇になり、生半可な知識では全く歯が立たないブラックボックスと化した。

プログラミングにおいても、自動車の進歩と同様の事柄が起きていると考るべきであろう。20年前、プログラミングという仕事は一部の専門的な知識と経験を持つ者のみが携わるものであった。計算機を用いて何らかの問題を解こうと欲するならば、専門家に依頼するか、自らが専門家になるしかなかった。

しかし、20年間の進歩により、プログラミングに必要とされる専門知識や経験は相当に少なくなった。多くの典型的な問題については、パッケージソフトウェアのような形で既に解決策が用意されているし、扱いの簡単なスクリプト言語やGUIを用いる補助ツールなどが充実し、専門家でなくても十分にプログラミングを行えるようになったのである。一発完動プログラミングが出来なくとも役に立つプログラムを作ることは可能だし、社会的には大成功というわけである。その代償として、一発完動プログラミングを習得できる機会は激減し、専門家になることがより難しくなったのである。

計算機屋として、この進歩は歓迎こそすれ、決して批判すべき類ものではない。前述の主張1と主張3は正しいとしても、主張2と主張4は正しいとは言えない。プログラムを書く者の母集団が大幅に増えたわけだから、一発完動プログラミングを習得していないプログラマが増えるのは当たり前である。これを問題視すること自体が間違っている。であれば、開発環境を20年前に戻すなどナンセンスである。問題が存在しないのだから、その解決策には何の意味もない。

一発完動プログラミングの習得

プログラムを書く者は誰であれ、一発完動プログラミングを習得しているべきだ、などと言うつもりもないし、現在の便利な開発環境はすべて、プログラマの技量を衰退させる悪の環境だ、などと言うつもりもない。要は、「自分はどこに居るのか?どこに居たいのか?」という、極めて個人的な問題なのだ。

自動車をさらに進歩させるためには、相当に高度な専門知識と経験を有する技術者が、それなりの数必要であることは疑う余地もない。プログラミングを進歩させるためには、相当に高度な専門知識と経験を有する技術者が、それなりの数必要であることも、同様に間違いない。自分が、その「相当に高度な専門知識と経験を有する技術者」でありたいのか、ということが問われるだけである。

一発完動プログラミングは、個々のプログラマが「相当に高度な専門知識と経験を有する技術者」になりたいと思い、研鑽を積み、階段を登って行く途中で習得すれば良いことなのである。

一発完動プログラミングをどうやって習得するか

さて、幸運にも(「不幸にも」かもしれないが......)、あなたが「相当に高度な専門知識と経験を有する技術者」になろうと心に決めたとする。上述の主張3からすると、現在の進歩した開発環境を利用していては、一発完動プログラミングを自動的に習得できるような機会はかなり少ないものと想像できる。では主張4を採用して20年前の環境のみを使ってプログラミングをするのか?それはいくら何でも、あんまりである。目の前に便利なものがあるのに、それを使わずに仕事をするなど、精神的苦痛は計り知れないものとなろう。

そこまでストイックに考えずとも、我々の回りには一発完動プログラミングの習得を強く促す状況はかなりある。ポイントは、遅いプロセッサ、不安定な処理系、不親切なデバッグ環境、である。こういった条件を満足するプログラミングを(公私ともに)進んでこなしてゆけば、いずれ一発完動プログラミングも習得できるであろう。いくつか例を挙げてみよう。

デバイスドライバの開発

まず第一に、当然と言えば当然ではあるが、デバイスドライバの開発である。プロセッサはそこそこ速いし、処理系も安定しているものが多いが、デバッグ環境は相当に悪い。前回述べたように、ソースレベルデバッグなど出来なくて当然、デバッガさえないこともある。printfデバッグは観測対象を大幅に乱すので多用はできない。便利な開発環境に慣れていると、「こんなデバッグ環境でバグなんか取れるかぁ~!」と立腹したくなるとは思う。が、そこをこらえて「こんなデバッグ環境でも自分にはなぁ~んの問題もない。何故なら自分は優秀だからだぁ~!」と、自己に暗示をかけつつ、やり抜く覚悟が必要である。

フリーソフトウェア関連の開発

次に、フリーソフトウェアが関係する開発である。特に、フリーの言語処理系でプログラムを開発する、フリーのライブラリを使ってプログラムを開発する、といった案件では、ハズレたときの処理系やライブラリの不安定さは、かなりのものがある。自分が書いたプログラムの誤りか、言語処理系のバグか、はたまたライブラリのバグか、スリリングな開発が堪能できる。安定した処理系に慣れていると、「こんな処理系で開発できるかぁ~!」と投げ出したくなるとは思う。が、そこをグッと堪えて「こんな処理系でも自分にはなぁ~んの問題もない。処理系やライブラリのバグくらい、いくらでも取ってやる。何故なら自分は優秀だからだぁ~!」と、自分を鼓舞しつつ、やり抜く覚悟が必要である。

組み込み系ソフトウェアの開発

続いて、組み込み系ソフトウェアの開発である。組み込み系ではクロス開発が普通であるから、コンパイラは高速であるが、実機のプロセッサは遅い場合もある。洗練されたプログラミングを行わないと、遅くて使い物にならないプログラムになってしまうこともある。処理系は、動作自体は安定しているものが多いが、プログラムの些細なミスが処理系を引きずって死ぬことが多いので、開発段階のことを考えると処理系は不安定である。デバッグ環境は、デバイスドライバ開発とさほど変わらない。処理系の不安定さ、デバッグ環境の悪さから「こんな環境で開発なんかできるかぁ~!」と悪態をつきたくなるとは思う。が、そこをじっと我慢して「こんな環境でも自分にはなぁ~んの問題もない。何故なら自分は優秀だからだぁ~!」と、自信過剰に陥ってでも、やり抜く覚悟が必要である。

その他、プログラムの異機種への移植などでも、摩訶不思議な問題が生じることはあるし、プログラムが巨大になってくれば、全体像が把握できずに頭脳破綻を起こすこともある。こういった、やりにくい、困難と思われる、尋常ではない状況に対して、「それは環境が悪いのが原因であって、解決できないのは自分の責任ではない」という態度で望んでいては、真の勇者(?)にはなれない、ということなのである。

注釈

  • 初稿に由来する表現の差異、ならびにWeb掲載に伴う修正のため、雑誌掲載の内容とは一部文章等が異なることがあります。
  • 転載許諾を頂いた株式会社KADOKAWAアスキー・メディアワークスブランドカンパニーならびに旧『UNIX Magazine』編集部の皆様に深く御礼を申し上げます。

著者プロフィール

tom

当社設立直後に入社して約30 年、UNIX の移植、日本語化、デバイスドライバ開発、周辺機器ファームウェア開発などに継続的に携わり、現在も現役でUNIX 系OS の移植、改造などの開発業務を行う。社内でもっともプログラムを書いている人の一人。代表取締役社長。

記事一覧Index