コードは方法を語り、コメントは理由を語る

私はコードにコメントを書く際の哲学について書いた投稿の中で、必要のないコメントこそが、最も優れたコメントだと書きました。この点についてもう少し説明させてください。まず、コードはコメントに頼らなくても理解できるようにできるだけ簡潔にする必要があります。コードだけではどうしても分かりにくい箇所のみ、コメントを書き込むようにしましょう。

これを念頭に置いて作業することで、読者を常に意識しながら、コードが書けるようになります。1985年に出版された名著『計算機プログラムの構造と解釈』の、「第一版への前文」にも、この点について単刀直入に書かれています。

プログラムは人が読めるように、そしてコンピュータがついでに実行できるように書くべきである。

Donald E. Knuthも1984年に出版したエッセー『文芸的プログラミング』(PDF)の中で、同様のことを書いています。

これまでのプログラム構築のあり方を変えてみましょう。コンピュータに指示を出すのではなく、コンピュータにしてほしいことを人間に説明することに注力するのです。

文芸的プログラミングの実践者は読者への見せ方を一番に考える、いわば、エッセイストと言えるでしょう。彼らはまるで作家のようにシソーラスを片手に、変数の名前を注意深く選び、それぞれの変数の意図するところを伝え、分かりやすいプログラムを仕上げようと努めます。なぜなら文芸的プログラミングの概念とは、互いに補強しあうフォーマルな手法、インフォーマルな手法を取り混ぜながら、人間にとって最もわかりやすい秩序に従ったプログラムを導入することだからです。

自分ではない他のプログラマが最初に確認し、実際のコンパイラ処理を実行するようなプログラムを書く場合、より多くのコメントで補足しておきたくなるものですが、実際はそのほとんどが不要なものです。こちらのブログでは、コメントをちょっとした補足として使うという好例が紹介されています。

これは、十分な資金を元に数年に渡って本番環境で展開され続けている、あるクローズドソースシステムのコード・スニペットです。

1
2
3
4
5
6
7
8
9
float _x = abs(x - deviceInfo->position.x) / scale;
int directionCode;
if (0 < _x & x != deviceInfo->position.x) {
if (0 > x - deviceInfo->position.x) {
directionCode = 0x04 /*left*/;
} else if (0 < x - deviceInfo->position.x) {
directionCode = 0x02 /*right*/;
}
}

そしてこちらが、もっと読みやすいコードで書かれたバグフィックス版です。

1
2
3
4
5
static const int DIRECTIONCODE_RIGHT = 0x02;
static const int DIRECTIONCODE_LEFT = 0x04;
static const int DIRECTIONCODE_NONE = 0x00;
int oldX = deviceInfo->position.x;
int directionCode = (x > oldX) ? DIRECTIONCODE_RIGHT : (x > oldX) ? DIRECTIONCODE_LEFT : DIRECTIONCODE_NONE;

コメントが多いからといって読みやすいコードになっている訳ではないことに注意してください。この例では全く違っていますよね。すでにお気づきかと思いますが、最初にあげたスニペットにあるコメントは、むしろコードをよりぐちゃぐちゃにしています。意味が付与されているシンボル名を代わりに使える場合は特に、コメントをほとんどつけなくても読みやすいコードになることもあります。

コメントを書く必要がなくなるように、コードをリファクタリングしたりシンプルにしたりするチャンスはいくらでもあります。とはいえ、コードの中だけですべてを説明するにはどうしても限界があります。

どんなにシンプルかつ簡潔な分かりやすいコードだったとしても、それを完全に自己文書化することは不可能です。コメントがコードそのものに取って代わることは決してありません。Jef Raskinの記事を確認してみてください。

プログラムが書かれた経緯や、メソッドを選択する際の根拠をコードから判断することはできません。なぜそのようなアプローチを取ったのかという理由を、コードが教えてくれることはないのです。そのため、下記のようなコメントは必要になります。

1
/* データ集合の関係において、二分探索法は文字列検索アルゴリズム(BM法)よりも遅いということが分かっています。そのため、この問題は文字列の検索手法に影響がなさそうでしたが、より複雑でありながらも早いメソッドを使っています。 */

あるデベロッパにとっては、完璧かつ明確なものであっても、背景を知らない他のデベロッパにとっては、理解し難いものです。コメントに関するアドバイスを熟考してみてください。

以下が文字列を反転させるコードだということは、すでにご存じかと思います。

1
$string = join('',reverse(split('',$string)));

では、次のコードをPerlファイルに書き込むのは、どのくらい難しいでしょうか。

1
# Reverse the string

実際は、全く難しくありません。プログラムがどう動くのかを伝えるのがコードで、なぜそれが動くのかを伝えるのがコメントです。どちらの場合でも、同僚のデベロッパを惑わすようなことはしないでください。

##著者: Jeff Atwood
インドア派。Stack Exchange and Discourseの共同創設者。お断り: 自分でも何を話しているのかよく分かっていません。私についてはhttp://twitter.com/codinghorrorをチェックしてください。