間違ったコードは間違って見えるようにする

From The Joel on Software Translation Project

Jump to: navigation, search

Joel Spolsky / 青木靖 訳
2005年5月11日 水曜


私が最初の本当の仕事をはじめたのは1983年9月に遡る。それはオラニムというイスラエルの大きな製パン工場で、16台の飛行機ほどもある巨大なオーブンで、毎晩10万個のパンが作られていた。

はじめて工場に入った時、そのあまりの汚さに信じられない思いだった。オーブンの側面は黄ばんでいるし、機械は錆びていて、そこらじゅうが油だらけだった。

「いつもこんなに汚いの?」と私は聞いてみた。

「なんだって? なんの話をしてるんだ?」とマネージャが答えた。「掃除したばかりだから、今が一番きれいな状態なんだ」

なんてこった。

毎朝の工場の清掃を何ヶ月か続けて、ようやく彼らの言っていたことが理解できるようになった。パン工場では、きれいというのは機械にパン生地が付いてないことを言うのだ。きれいというのは、ゴミ箱に発酵したパン生地が入ってないことを言うのだ。きれいというのは、床にパン生地がついてないことを言うのだ。

きれいというのは、オーブンが白くきれいにペンキが塗られていることをいうのではない。オーブンのペンキ塗りが行われるのは10年に1回とかのもので、毎日することではない。きれいというのは油がついてないということではない。実際機械の多くは定期的に油をさしてやる必要があり、薄いきれいな油の層は通常機械が清掃されたばかりであることを意味していた。

DoughRounder.PNG

パン工場できれいというのがどういうことを指すのかは、学ばないとわからないことなのだ。門外漢にはその場所がきれいなのか汚いのか判断するのは不可能だ。門外漢はパン生地こね機(右の写真にある、パン生地の四角い固まりをボールにする機械)の内側にこびりつきが残ってないか覗いてみようとは決して思わない。門外漢は古いオーブンのパネルが色落ちしているのを気にするが、それはパネルが目立つからだ。しかしパン職人はオーブンの外側のペンキが黄色くなりかけていても、これっぽっちも気にしない。パンの味には影響しないからだ。

パン工場で2ヵ月過ごせば、きれいかどうか「見る」方法を学ぶようになる。

プログラムのコードも同じだ。

あなたが初心者プログラマか、あるいは初めて使う言語のコードを読もうとするとき、どの部分も同じように不可解に見える。そのプログラミング言語を理解するようになるまでは、簡単なシンタックスエラーにさえ気がつかない。

学習の最初のフェーズにおいて、私たちが「コーディングスタイル」と呼んでいるものを認識するようになる。そうして標準的なインデントの付け方に合ってないところや、変数名の大文字/小文字が変なところに気づくようになる。

この時期に典型的なことは「水ぶくれのフジツボみたいだ。一貫したコーディング規則がなきゃだめだ」みたいなことを言って、次の日をチームのためのコーディング規則を書くために使い、そのあと6日間で「唯一正しい括弧付けのスタイル」について議論し、そのあとの3週間を古いコードが「唯一正しい括弧付けのスタイル」に合うよう書き直すのに費やすが、それに気付いたマネージャから全然お金にならないことに時間を使っていることで怒鳴りつけられ、それでソースに手を入れる必要が出た時ついでにフォーマットを直すのでも別に悪くないと思い直し、半分だけ「正しい括弧付けスタイル」という状態になってしまうが、そんなことはすぐに忘れ、そしてすぐまた別な何か、1つの文字列クラスを別な文字列クラスで置き換えるというような、お金になることとは無関係なことに心を奪われるのだ。

その環境でコードを書くことにさらに熟達していくと、もっと別なことに気づくようになる。まったく合法で、コーディング規則上はまったく問題ないのだが、しかし何か不安にさせるようなことにだ。

C言語の例を挙げると:

   char* dest, src;

このコードは正しい。コーディング規則にも合っているかもしれないし、意図した通りのコードなのかもしれない。しかしCのコードを書く経験を積んでいれば、destcharポインタを宣言している一方、srcはただのchar型なことに気付くだろう。それが意図した通りという可能性もあるが、おそらくはそうではない。このコードは少し嫌な臭いがするのだ。

もっと微妙な例としては:

   if (i != 0)
       foo(i);

この場合、コードは100%正しく、ほとんどのコーディング規則を満たし、まずいことは何もないのだが、if文の本体が括弧でくくられていない1行のステートメントになっているのがあなたに不安を抱かせる。それというのも、後で誰かがもう1行追加するかもしれないからだ:

   if (i != 0)
       bar(i);
       foo(i);

そして括弧をつけ忘れる。結果としてfoo(i)は無条件に実行されるようになる! だからあなたは括弧に入っていないブロックを見ると、何かほんの、ごく、ちょっとした汚さを感じるようになり、不安を覚えるのだ。

ここまでで、プログラマの成長の3つの段階を示した。

1. きれいなものと汚いものの区別がつかない。

2. きれいさについて表面的な考えを持っている。そのほとんどはコーディング規則に合っているかどうかというレベルだ。

3. コードのきたなさについて、表面的でない小さな兆候も嗅ぎつけるようになり、気になってコードを直すようになる。

これよりもさらに高いレベルがあり、それこそ私が本当に話したいと思っているものだ。

4. コードを直すために汚いコードに対する嗅覚を生かせるような仕組みを、最初から作り込むようになる。

これこそ本当のアートだ。エラーが画面から飛び出して見えるような規則を考案することによって、頑強なコードを作り出すのだ。

では、小さな例で見ていくことにしよう。そのあと頑強なコードのための規則を考案するのに使える一般的なルールを示す。最後に、ある種の(人々が乗り物酔いを感じる類のものではない)ハンガリアン記法の擁護を行う。そしてある状況における例外の使用を批判する(ただしこれはおそらく普段あなたが直面しているような状況ではない)。

あなたがハンガリアン記法は悪であり、例外はチョコレートミルクセーキ以来の最良の発明であると信じて疑わず、他の意見には耳を傾けるつもりがないのなら、結構、かわりにローリーのサイトに行って、すばらしいコミックを読むといい。たぶんあなたは大して損することにはならないだろう。実際このあとすぐ、私はあなたを居眠りさせるようなコード例を示すつもりだ。だからあなたは怒りはじめる前に眠り込んでしまうだろう。つまり、私のプランは、あなたをすっかり眠り込ませ、その間にハンガリアン=良い、例外=悪い、というアイデアをもぐり込ませてしまおうというものなのだ。あなたは眠くって抵抗もできないだろう。


Umbria.JPG

じゃあ例を見よう。あなたはWebベースのアプリケーションを作っているものとしよう。若い人たちの間で最近流行っているみたいだから。

クロスサイトスクリプティング、別名XSSと呼ばれているセキュリティ脆弱性がある。あまり詳しい説明はしないが、Webアプリケーションを作るときにはユーザがフォームで入力した文字列をそのまま返してはいけない、ということをとりあえず知っておいてもらえればいい。

たとえばテキストボックスを出して「あなたの名前は?」と質問するページがあって、送信すると別なページに「こんにちは、エルマー!」と表示されるとする(ユーザの名前がエルマーだとしてだ)。その場合、セキュリティ脆弱性があって、それはユーザが「エルマー」の代りにあらゆる奇妙なHTMLやJavaScriptを入力でき、その奇妙なJavaScriptコードにはいろいろな嫌らしいことをさせることができて、その嫌らしいことをそのサイトがやっているように見えるのだ。それで彼らはクッキーを読み取ってDr.Evilのサイトに送信するといったことができるようになる。

擬似コードを書いてみよう。

   s = Request("name")

このコードで入力(POST引数)をHTMLフォームから読み込めるとしよう。あなたが以下のように書いたとすると、あなたのサイトはXSS攻撃に対して脆弱性を持つことになる:

   Write "Hello, " & Request("name")

これだけのことでそうなるのだ。

コピーした文字列をHTMLに書き出す前にエンコードする必要がある。エンコードというのは、"を"で置き換えたり、>を>で置き換えたりすることをいう。

   Write "Hello, " & Encode(Request("name"))

こう書くのであれば安全だ。

ユーザに由来する文字列はすべて安全ではない。安全でない文字列をエンコードせずに出力してはいけない。

あなたがこの間違いを犯したときに、コードがただ間違ってるように見えるコーディング規則を作ってみよう。間違ったコードが間違って見えるなら、そのコードをいじっている人やレビューする人の目にとまる可能性がある。


解決法1

1つの解法は、ユーザから来た文字列をすべて、即座にエンコーディングするというものだ。

   s = Encode(Request("name"))

この規則では、Encodeで囲まれていないRequestがあれば、それは誤りだということになる。

あなたの目は、規則に反している裸のRequestがないか探すようになるだろう。

この規則に従っていればXSSバグがなくなるという点では、この規則は上手く機能する。しかしこれは最適なアーキテクチャというわけではない。たとえばあなたはユーザの入力文字列をどっかのデータベースに格納したいと思うかもしれず、データベースに入れる文字列をHTMLエンコードするというのは意味がない。その文字列はHTMLページ以外の所に行くわけで、クレジットカード処理アプリケーションがHTMLエンコードされた文字列を食わされたら混乱することだろう。ほとんどのWebアプリケーションは、内部的な文字列はエンコードせず、HTMLページに出力する直前にエンコードするという原則で開発されている。これはたぶん正しいアーキテクチャだ。

私たちは安全でない形のものをしばらく扱わなければならないのだ。

OK、もう一度トライしてみよう。


解決法2

文字列を出力するときには必ずエンコードする、というコーディング規則にしたらどうだろう?

   s = Request("name")
   
   // ずっと後で
   Write Encode(s)

そうすると、Encodeのない裸のWriteを見たら、何かが間違っているとわかる。

しかしこれはあまりうまくいかない・・・ときどきちょっとしたHTMLを使う必要があり、それはエンコードするわけにいかないのだ。

   If mode = "linebreak" Then prefix = "<br>"
   
   // ずっと後で
   Write prefix

こうやるのは「出力するときにエンコードする」という規則の元では間違っているように見える。

   Write Encode(prefix)

しかしこうしてしまうと、"<br>"は改行のつもりなのに、&lt;br&gt;とエンコードされ、ユーザに文字通りの< b r >を見せることになる。これもまた正しくない。

だから文字列を読み込んだ時にエンコードできない場合がときどきあり、文字列を出力するときにエンコードできない場合もときどきあって、これらの案はどちらもうまく行かないのだ。そして規則がないと、次のようなことをするリスクが残る:

   s = Request("name")
   
   ...何ページも後...
   name = s
   
   ...何ページも後...
    recordset("name") = name // データベースの"name"カラムにnameを格納する
   
   ...何日も後...
   theName = recordset("name")
   
   ...何ページも、あるいは何ヶ月も後...
   Write theName

文字列をエンコードしなきゃいけないことを果たして覚えているだろうか? バグを見つけるために一カ所見ればすむ場所というのはどこにもない。臭う場所がどこにもないのだ。こういうコードがたくさんあれば、出力される文字列のそれぞれがエンコードされているか確認するために、その起源をたどっていくという膨大な作業が必要になる。


本当の解決法

では上手く機能するコーディング規則を提案することにしよう。必要なルールは1つだけだ。

ユーザから来た文字列は(安全でない—Unsafe—文字列という意味で) プレフィックス"us"をもつ名前の変数(あるいはデータベースのカラム)に格納する。HTMLエンコードされた文字列や、素性の安全なことのわかっている文字列は(安全—Safe—な文字列という意味で)プレフィックス"s"を持つ名前の変数に格納する。

上に挙げたコードを書き直してみよう。何も変えず、ただ変数名だけを、新しい規則に合うよう変更する。

   us = Request("name")
   
   ...何ページも後...
   usName = us
   
   ...何ページも後...
   recordset("usName") = usName
   
   ...何日も後...
   sName = Encode(recordset("usName"))
   
   ...何ページも、あるいは何ヶ月も後...
   Write sName

新しい規則で気付いてほしいのは、この規則にしたがっていれば、安全でない文字列について間違いを犯したとき、どこか1行のコードでその間違いを見つけられるということだ。

   s = Request("name")

これは理論的に間違っている。sで始まる名前の変数にRequestの結果を代入しており、これは規則に反しているからだ。Resultの結果は安全でなく、したがって常に"us"で始まる名前の変数に格納する必要がある。

   us = Request("name")

これは常に正しい。

   usName = us

これは常に正しい。

   sName = us

これは確実に間違っている。

   sName = Encode(us)

これは確実に正しい。

   Write usName

これは確実に間違っている。

   Write sName

これは正しい。

   Write Encode(usName)

同様にこれも正しい。

すべてのコード行が、その行だけ見てチェックすることができ、そして各行がすべて正しいなら、コードの全体も正しいことになる。

このコーディング規則を使っていれば、Write usXXXのようなコードを見ると間違いだと気付くようになり、そしてどう修正すればいいのかがすぐにわかる。はじめはコードの間違いを見つけるのが難しく感じられるかもしれないが、これを3週間も続けていれば、目が慣れるようになる。ちょうど巨大なパン工場の見方を覚えた職人が即座に言うように。「まったく、こね機ん中誰も掃除してねぇじゃねぇか! ここじゃ一体どんな運用してんだよ?」

このルールをもう少し拡張して、Request関数やEncode関数をリネームして(あるいはラップして)、UsRequestSEncodeという名前にすることもできる・・・言い換えると、安全でない文字列を返す関数や安全な文字列を返す関数に、変数と同様、それぞれUsSで始まる名前を付けるのだ。コード例で見てみよう。

   us = UsRequest("name")
   usName = us
   recordset("usName") = usName
   sName = SEncode(recordset("usName"))
   Write sName

ここで何をしているかわかる? こうしておけば、等号の両端が同じプレフィックスで始まっているか見てコードに間違いがないか確認できる。

   us = UsRequest("name") // 正しい。どちらもUSで始まっている
   s = UsRequest("name") // バグ
   usName = us // 正しい
   sName = us // 確実に間違い
   sName = SEncode(us) // 確実に正しい

さらに一歩進めて、WriteWriteSに、SEncodeSFromUsにリネームしてみよう。

   us = UsRequest("name")
   usName = us
   recordset("usName") = usName
   sName = SFromUs(recordset("usName"))
   WriteS sName

こうすれば間違いがさらに見つけやすくなる。あなたの目には臭うコードが「見える」ようになり、それはあなたが、コードを書いたり読んだりしているときに奇妙なセキュリティバグを見つける助けになる。

間違ったコードを間違って見えるようにするのはいいことだが、これがあらゆるセキュリティ問題に対する最良の解法とは限らない。起こりうるあらゆるバグやミスをこれで見つけられるわけではなく、それはあなたがコードをすべては読まないかもしれないからだ。しかし何もしないよりずっといいのは確かだ。そして私は間違ったコードが少なくとも間違って見えるコーディング規則を持ちたいと思う。プログラマの目がコード行の上をたどるたびに、バグがチェックされ、回避されるという、追加的な利益がすぐに得られるのだ。


一般的なルール

間違ったコードが間違って見えるためには、関連するものが画面上で近くにある必要がある。コードが正しいか確認しようと文字列を見るとき、どれでも文字列を見れば、それが安全か安全でないかがわかる必要がある。その情報が別なファイルや、スクロールしなければ見えない別なページにあっては困る。その場にあるのを見ることができる必要があり、それには変数命名規則が必要なのだ。

関係するものを隣り合わせにしておくことでコードを改善できる例はたくさんある。コーディング規則の多くは以下のような規則を含んでいる:

  • 関数を短くする。
  • 変数は使う場所のできるだけ近くで宣言する。
  • 自分のプログラミング言語を作ろうとしてマクロを使わない。
  • gotoを使わない。
  • 閉じる括弧は、対応する開く括弧から1画面以上離さない。

これらのルールに共通しているのは、1行のコードが実際にすることに関連した情報を可能な限り物理的に近づけるということだ。そうすることによって、あなたの目玉が何が起きているのか理解できる可能性が高くなる。

一般的に言って、私はものごとを隠してしまうような言語機能には恐れを感じる。次のコード

   i = j * 5;

・・・を見れば、少なくともCであれば、j の値が5倍されて結果が i に格納されるということがわかる。

しかしC++の場合では、同じスニペットから分かることは何もない。まったく。C++で本当に起こることが何か知るためには、ぜんぜん違う場所で宣言されているかもしれない i と j の型を見つける必要がある。それはjがoperator*をオーバロードしている型の変数かもしれず、かけ算したときにはなはだ気の効いたことをやってくれるかもしれないからだ。そして i はoperator=をオーバロードしている型の変数かもしれず、また型が異なるために暗黙の型変換関数が呼ばれることになるかもしれない。そして何が起るか知るためには変数の型をチェックするだけでなく、その型を実装しているコードを見つける必要があり、そして,神よ救いたまえ、どこかで継承が行われていれば、コードが実際どこにあるのか自分でクラス階層をたどる必要があり、そしてどこかにポリモーフィズムがあれば、あなたは本当に難儀することになり、それは i と j の型がどう宣言されているか知るだけでは不十分で、実行のその時点における型が何か知る必要があるからであり、それにはどれくらいの量のコードを調べなければならないかわからず、停止性判定問題のおかげで、あなたは決してすべてをチェックしたか本当に確信することはできないのだ。(やれやれ!)

C++におけるi=j*5というコードは任意性が高く、その分コードを見るだけで問題を見つけ出せる能力は下がることになる。

これらの機能は問題となることが想定されたものではもちろんない。あなたが賢い中学生みたいにoperator*をオーバライドするとき、それは素敵な漏れのない抽象化を提供することを意図している。j がUnicode文字列型ならUnicode文字列の整数倍が繁体中国語から簡体中国語への変換だというのは優れた抽象化に違いない。そうだよね?

問題は、漏れのない抽象化は存在しないということだ。この点については「漏れのある抽象化の法則」で詳しく議論したので、ここでは繰り返さない。

スコット・メイヤーズは彼のキャリアのすべてを、C++で失敗したりしくじったりするためのあらゆる方法を示すことに費やしている。(ところで、スコットの本Effective C++の第3版が出たところだが、この本は前の版から完全に書き直されている。すぐ手に入れるといい!)

OK。

話がそれてしまった。ここまでの話を要約しておいた方が良さそうだ。

間違ったコードが間違って見えるコーディング規則を探すこと。関連する情報を画面上でひとまとまりにして見られるように配置することで、ある種の問題を容易に見つけて修正できるようになる。

私はハンガリーです

Lugnano.JPG

では、評判の悪いハンガリアン記法の話をすることにしよう。

ハンガリアン記法はMicrosoftのプログラマ、チャールズ・シモニイにより考案された。シモニイがMicrosoftで手がけた大きなプロジェクトの1つにWordがある。実際彼はXerox PARCで、Bravoと呼ばれる世界最初のWYSIWYGワードプロセッサを構築するプロジェクトの指揮をしていたのだ。

WYSIWYGワープロにはスクロールする画面があり、すべての座標はウィンドウに対する相対座標なのかページに対する相対座標なのか解釈する必要があり、どちらであるかによって話は大きく違うので、取り違えないようにすることが重要だ。

私の推測ではこれが、ハンガリアン記法と呼ばれるようになるものをシモニイが使い始めた理由の1つなのだと思う。それがハンガリー語のように見え、またシモニイがハンガリー人であったことから、この名前が使われるようになった。シモニイ版のハンガリアン記法では、すべての変数に、それが保持するものの種類を示すタグを小文字で付ける。


Hungarian jp.png


私はここで種類(kind)という言葉を意図して使った。シモニイはタイプ(type)という語を不用意に使い、それが後の世代のプログラマたちの誤解の元となった。

シモニイの論文を良く読めば、彼が書いているのは、私が上の例で使った命名規則と同種のものであるのがわかるだろう。usは「安全でない文字列」を表し、sは「安全な文字列」を表すが、どちらも型(type)としてはstringだ。間違って代入してもコンパイラは助けにならないし、インテリセンスは何も教えてくれない。しかしこれらは意味論的には異なっており、違ったように解釈し、違ったように扱う必要がある。そして一方を他方に代入するときには変換関数を呼ぶ必要があり、そうしないと実行時バグを目にすることになる。運が良ければね。

シモニイのハンガリアン記法のオリジナルのアイデアは、Microsoft内部ではアプリケーションハンガリアンと呼ばれ、それはアプリケーション部門で、すなわちWordとExcelで使われていたためだ。Excelのソースコードにはrwcolというのがたくさん出てくるが、これは行(row)とカラム(column)を表している。どちらも整数だが、これらの一方を他方に代入するのは意味がない。Wordでは、聞いたところによると、xlxwというのがたくさん出てくる。xlは「レイアウトに対する相対水平座標」、xwは「ウィンドウに対する相対水平座標」を意味する。どちらもintだが、交換可能ではない。ExcelでもWordでも「バイト数」を意味するcbがたくさん出てくる。これもまたintだが、変数名を見るだけで、ずっと多くのことを知ることができる。その変数はバイト数であり、すなわちバッファサイズだ。そしてxl = cbのようなコードを見たなら、まずいコードを知らせる警鐘が鳴り響き、コードの間違いは明らかだ。それはxlcbがどちらもint型であるにせよ、ピクセル単位での水平オフセットにバイト数をセットするというのはまったく狂ったことだからだ。

アプリケーションハンガリアンでは、プレフィックスは変数名と同様に関数名にも使われる。私は、ほんとのこと言ってWordのソースは見たことがないのだが、垂直ウィンドウ座標を垂直レイアウト座標に変換するYlFromYwという名前の関数があるだろうことには賭けてもいい。アプリケーションハンガリアンでは、前の例でEncodeSFromUsにリネームしたように、よく見られるTypeToTypeという形ではなく、TypeFromTypeという記法を使い、関数名が戻り値の型で始まるようにしている。実際、本来のアプリケーションハンガリアンでは、Encode関数はSFromUsという名前にしなければならないのだ。アプリケーションハンガリアンではこの関数の名前について選択肢はない。これはいいことであり、記憶しなければならないことが1つ少なくなり、Encodeという名前がどんな種類のエンコーディングを表しているのか思いまどう必要もない。ずっと正確に表せるのだ。

アプリケーションハンガリアンは非常に価値があり、ことにコンパイラがあまり型について有用な情報を与えてくれなかったCプログラミングの時代にはそうだった。

しかしその後まずいことが起きた。

ハンガリアン記法の暗黒面が支配権を得たのだ。

それがなぜなのか、どういう経緯でそうなったのか誰も知らないのだが、どうやらWindowsチームのドキュメントライターたちが、不用意にシステムハンガリアンとして知られるようになるものを作り出したということらしい。

誰かが、どこかでシモニイの論文を読み、「タイプ」という言葉が使われていたため、それがクラスのような、タイプシステムや、コンパイラのタイプチェックというときのタイプ(型)を意味するのだと考えた。これはシモニイの意図していたことではない。シモニイは「タイプ」が正確に何を意味するのか注意深く説明しているが、それも救いにはならなかった。ダメージは為されてしまった。

アプリケーションハンガリアンは有用で意味のあるプレフィックスを使う。配列インデックスを表す"ix"や、個数(count)を意味する"c"、2つの数の差(difference)を意味する"d"などだ(たとえば"dx"は幅を表す)。

システムハンガリアンはlongを意味する"l"、unsigned longを意味する"ul"、double word(これは、実際は、その、unsigned longと同じものだ)を意味する"dw"のような、あまり役に立たないプレフィックスを使う。システムハンガリアンでは、プレフィックスが教えてくれるのは変数のデータ型だけだ。

ちょっとした誤解ではあるが、シモニイの意図とやり方を完全に捉え損ねている。込み入ってわかりにくい学術的な文章を書くと、結局誰も理解できず、アイデアは誤解され、誤解されたアイデアはあなたのアイデアでないにもかかわらず、あなたがあざ笑われることになるのだ。システムハンガリアンではたくさんの"dwFoo"が出てきて、それは「double word型のfoo」を意味しているのだが、ばからしい、その変数がdouble word型であるというのは有用なことをほとんど何も教えてくれない。だから人々がシステムハンガリアンに反対するのも無理のないことなのだ。

システムハンガリアンは広く普及し、Windowsプログラミングのドキュメンテーションにおける標準となった。これはWindowsプログラミングのバイブルである、チャールズ・ペゾルドの「プログラミングWindows」のような本によって広められた。そして急速にシステムハンガリアンがハンガリアン記法の支配的な形態となり、Microsoft内部においてさえ、WordチームとExcelチームを別にすると、彼らがいかに大きな間違いをしたか理解しているプログラマはごくわずかしかいなかった。

そして大きな反乱が起きた。ハンガリアン記法をそもそものはじめから理解していないプログラマたちは、自分たちの使っているハンガリアン記法の誤解されたサブセットがはなはだ煩わしくほとんど役立たずであることに気づき、反旗を翻したのだ。システムハンガリアンにも、バグを見つけやすくする特質がなくはない。少なくとも、システムハンガリアンを使うなら、変数の型がその使われているその場でわかる。しかしそれはアプリケーションハンガリアンの有用性には遠く及ばない。

大反乱は.NETの最初のリリースでピークを迎えた。Microsoftはついには人々に言い始めたのだ。「ハンガリアン記法は推奨しない」。これは喜びをもって迎えられた。Microsoftはその理由を説明すらしなかったと思う。彼らはただドキュメントの命名ガイドラインの部分をたどって、それぞれの項目に「ハンガリアン記法は使わないこと」と書き込んでいった。ハンガリアン記法ははなはだ人気がなかったので、誰も苦情を言う人はおらず、ExcelチームとWordチームを別にすると、強い型付けとインテリセンスの時代には不要と思える面倒なだけの命名規則にもはや従わなくていいことに、みんなほっとしていた。

しかしアプリケーションハンガリアンには依然としてものすごい価値がある。コードのコロケーションを増し、コードは読みやすく、書きやすく、デバッグしやすく、メンテナンスしやすくなり、そして最も重要なのは、間違ったコードが間違って見えるようになることだ。

終わる前に、もうひとつ約束していることがあった。もう一度例外を攻撃するということだ。前にやったときには、すごいトラブルに見舞われた。Joel on Softwareのホームページに即席のコメントで例外が嫌いだと書いた。例外は実質的に見えないgotoであり、目に見えるgotoよりいっそう悪いという議論をしたのだ。もちろん何百万という人々が私ののど元に飛びかかってきた。私の擁護に立ち上がった唯一の人間は、もちろんレイモンド・チェンで、彼は世界最高のプログラマなわけだから、それは意味のあることなはずだ。そうだよね?

このアーティクルの文脈における例外とはこういうことだ。あなたの目は、見えるものがある限り、間違ったものが見えるようになり、それがバグを抑止する。コードを本当に頑健なものにするためには、コードレビューしたとき、コロケーションを可能にするコーディング規則が必要になる。言い換えると、コードが何をやっているかについて目の前にある情報が多いほど、間違いを見つける上でいい仕事ができるのだ。次のようなコードがあるとき

   dosomething();
   cleanup();

・・・あなたの目はこれのどこに問題があるのか教えてくれるだろうか? いつでもクリーンアップする必要がある! しかしdosomethingが例外を投げる可能性により、cleanupは呼ばれないかもしれない。それはfinallyかなにかを使って容易に修正できるが、それは私の論点ではない。cleanupが間違いなく呼ばれることを確認できる唯一の方法は、dosomethingのコールツリーをすべて調べて、何か、どこかに、例外を投げるかもしれないものがないかチェックし、それによって問題が起きないか確認するということで、チェック例外によりいくぶん骨折りを軽減できるにしても、本当の問題は、例外がコロケーションを殺してしまうということだ。コードが正しいことをしているかという質問に答えるためには、どこか別なところを見なければならず、あなたの目の持つまずいコードを見つけ出す能力が生かせないことになる。そこには見えるものがないからだ。

たくさんのデータをかき集めてきて1日に1度プリントアウトする小さなスクリプトを書いているのであれば、例外は大変結構なものだ。起こりうるあらゆるまずいことをすべて無視し、古き良きtry/catchを使ってプログラムに片を付け、起ったまずい問題はメールで通知するというのは、私の喜んでやりたいことだ。例外はやっつけ仕事や、スクリプトや、ミッションクリティカルでも生命維持にもかかわらないコードにはいいものだ。しかしオペレーティングシステムや、原子力発電所や、心臓の手術に使う高速回転丸鋸をコントロールするソフトウェアに例外を使うのは、はなはだ危険なことだ。

みんなが私のことを、例外をちゃんと理解できず、例外を受け入れればずっと人生が良くなることが分からないヘボなプログラマだと思うのはわかっている。しかし、そのために自分の心臓に例外を入れるというのはまずすぎる。本当に信頼性のあるコードを書く方法は、人の典型的な弱さを考慮に入れるシンプルなツールを使うことであって、副作用のあることや、プログラマの無謬性を前提とする漏れのある抽象化を隠している複雑なツールを使うことではない。


読書案内

あなたが依然例外に献身的なら、レイモンド・チェンのエッセイCleaner, more elegant, and harder to recognizeを読むといい。「例外ベースのまずいコードと、例外ベースのまずくないコードを見分けるのは、はなはだ難しい・・・例外はあまりに難しく、私は例外を扱えるほどに頭が良くはないのだ」

レイモンドのマクロ死についての議論A rant against flow control macrosは、必要な情報がすべて一カ所でわかるようにしそこねているコードはメンテナンス不能であることのもうひとつのケースを示している。「(マクロを)使っているコードを見るとき、ヘッダファイルを掘削してそれが何をやっているか理解する必要がある」

ハンガリアン記法の歴史的な背景については、手始めにシモニイのオリジナルの論文Hungarian Notationを読むといい。ダグ・クランダーがハンガリアン記法をExcelチームに紹介した文章はもう少し明快に書かれている。ハンガリアン記法とそれがドキュメントライターによっていかに損なわれたかについての話は、ラリー・オスターマンの投稿(特にスコット・ラドウィグのコメント)か、リック・ショートの投稿を読むといい。


(オリジナル: Making Wrong Code Look Wrong)

戻る

Personal tools