日々常々

ふつうのプログラマがあたりまえにしたいこと。

コメントについて考えてみた

やきに駆動2.0で話した内容です。

コメント書いてますか?

コードにコメントを書くことは、犬の散歩で糞を持ち帰ることくらい常識的なマナーです。皆さんコードにコメントは書いていらっしゃるでしょうか?
書いてます?書いてます。はい。そうですよね。……なんで?


コメントを書く理由を考えたことはあるでしょうか。コメントを書くと、コストがかかります。コメントを書く程度のコストは無視できるものかもしれませんが、書いたコメントを残すと、残っている限りずっとコストをかけ続けることになります。なので、コメントにはリターンが求められて然るべきです。理由のないコメントは、未来の混乱を招く優れた方法です。

コメントを書く動機

コメントを書きたいと思う動機は様々あります。コードがわかり辛いところに説明を書いておく。実装の仕方がわからなくてコメントで書いて次に進んでおく。なんとなくあった方がいい気がするから書く。コードを書く前に下書きのようにコメントを書く*1。……他にも色々あると思います。
コメントはコンピュータには無視されます*2ので、コードとは無関係のことを書くこともできます。変な言葉を書いたからって動かなくなることはありませんし、コードの中で一番プログラマ個人の裁量に任せられているところかもしれません。
そんなコメントなので、目的を意識しておかないと簡単にカオスな状態になってしまいます。

コメントの目的

コメントの目的はなんでしょう。これに対する私の考えは次の二つに集約されます。

  • コードの理解を助けるため
  • コードに書けないことを伝えるため

それぞれ掘り下げていきます。

コードの理解を助けるため

コードを読む際に、そのコードを読むのを助ける……ガイドのようなものです。
全くコメントのない長ったらしい記号の羅列のようなコード、私はそうそう読む気は起こりません。所々にいい感じにコメントがあれば、その単位で読んでいくこともできるものです。コメントの無いコードとコメントのあるコードでは、他の条件が同じなら、コメントのあるコードの方が読みやすいに決まっています。

コードに書けないことを伝えるため

なぜそのように書いたかなど、コードに表現できない理由を書く。設計意図や存在理由などは、コードでは表現し辛い場合も多くあります。ある程度意思を込めることはできますが、推測するにも限界があります。たとえばコードに書かなかったこと、つまり取捨選択により捨てたものは、コードで表現することは不可能です。選択の余地がある場合、代替案をコメントで残すのは良いことかも知れません。*3*4

未来のために書く

コメントの目的を一言で言うなら「未来へのメッセージ」です。
未来――それも自分がその場の居ない時――に読まれ、意味が通じ、意図通りの効果を発揮しなければ、コメントにかけたコストは水の泡です。大掃除の時にすら捨てられず、引っ越す時に苦労する使わない荷物のようなものです。そんなコメントなら書かない方がいいに決まっています。
とは言え、未来のことを正確に予測することはできません。せいぜい未来に役立ちそうだと予想するのが精一杯です。どうせ書くなら、その精一杯を費やしたいと思うじゃないですか。

コメントに書く内容

未来のためにどんな内容を残すと良いかを考えてみます。コードに書かれるコメントを大きくわけると二種類になります。

  • ドキュメントコメント
  • インラインコメント

どう言う名前で呼ばれるのが正確かは正直わからないので、ニュアンスで読み取ってください。

ドキュメントコメント

このコメントが読まれる条件を想像すると、そのメソッドやクラスを使用したい時でしょう。IDEの補完時に同時に表示されたりします。このコメントに書かれていて嬉しいことは「何が達成できるか」です。どのように使うと、どうなるかをさくっと把握できるのがベストです。
このコメントを読む人がコードを読むことはそれほど考えなくていいです。
なぜならば、ドキュメントコメントはコードとは独立して存在できることもあり、コードを読まずに内容を把握したい人が最大集団だと考えられるからです。実装の詳細を記述する必要性はそれほど高くありません。実装に触れるにしても概要に留め、詳細はコードに任せるのが良いでしょう。

インラインコメント

このコメントが読まれる条件も同様に想像すると、そのコードを読むかメンテナンスする時でしょう。コメントは間違いなくコードと同時に読まれます。ならばここに書くのは「意図や存在理由」でしょう。そのコードで何をしたいのか、どうしてそのコードになったのか。時には言い訳のようなものになるかもしれません。それでも他に伝える術が無い、もしくはコードのすぐ傍にあるのが好ましいと判断したならば、それはこのコメントで書かれるべき内容です。
インラインコメントは最も強力なコメントです。必ずコードと同じコンテキストで読まれるにも拘らず、チェックもされず、出力もされません。インラインコメントの数だけ、コードを読む量は増えます。もし役に立たないインラインコメントが目につけば、読む人はコメントを無視するようになるでしょう。この時点で読み手にはコメントによる恩恵より、コメントを無視するための余計なコストを払うことになります。書く時にもコストをかけ、読む時にもコストをかける。最悪です。
インラインコメントの自由度は高く、ほぼプログラマの裁量に依存します。何でも書けます。こういう場は得てして行き場の無いものの押しつけ先になりますが、そう言う場所は必要ですし、否定もしませんし、コードの近くにあるのはありがたいと思います。だからこそ、ここに何を書くかは強く意識するべきでしょう。
設計上の判断、代替案、プログラミング言語の制約、もしかしたら力不足かもしれなし。それらコードに伝えたい意図が込められない場合、ここに伝わるように記しておくのは良い未来へのメッセージになるはずです。
コードを読む時に同時に読まれるこのコメントは、コードのメンテナンスを助けることに特化してこそ、その価値が発揮されると言えます。逆に言えば、それ以外のことに使われるべきではありません。

「いいコードには多くのコメントがある」

こういう言葉を聞いたことがありませんか?
これ自体は真だと思っています。コメントがあることで、情報量は増えます。コードから得られる情報が多く、他の条件が同じならば、コメントのないコードよりもあるコードのほうが読みやすいです。人が読めるように書かれたコードならば、読みやすい方が良いコードでしょう。
ここでもう一つの言葉があります。「見苦しいコードには多くのコメントが必要になる」です。これも真です。見苦しいコードを読みながらコメントを足した経験のある方は多いでしょう。見苦しいコードにコメントを足すと、多少読めるようになったりします。大量のコメントで丁寧に覆い隠せば、それなりに見れたものになるかもしれません。つまり大量のコメントにより、見苦しいコードが良いコードになった……そんわけありませんよね?
これは単にコメントで覆い隠しただけです。読む時に見苦しいコードは読まずコメントだけを読んでいるだけです。コメントによりコードが読みやすくなったわけではありません。違うんです。そのコメントを書いた後に一切合切の記憶を失えば、コメントによりコードが読みやすくなったなんて思わないはずです。よっぽどのお人好しでもなければ、見苦しいコードとコメントを見比べ、合っているか検証することになるでしょう。そのコードは見苦しいままのはずです。

  • 見苦しいコードには多くのコメントが必要になる - 達人プログラマー

コメントの良し悪し

コメントには良いコメントと悪いコメントがあります。全てのコメントが純粋に良いものではありません。多すぎるコメントは、コメントが全く無いのと同程度には害悪になります。

良いコメントとは?

良いコメントは、正しいことが書いていて、書いてあることが明瞭であり、コードに対して新たな情報を追加してくれるものです。そして、必要のない場所には存在しないのも良いコメントと言えます。「そこにコメントが無いこと」が時として価値を生みます。

悪いコメントとは?

良いコメントが出た後なので、悪いコメントを考えるのは簡単でしょう。嘘だったり、矛盾があったり、読むのが面倒なほど大量にあったり、コードと同じことを繰り返しているに過ぎないものが、悪いコメントと言えるでしょう。悪いコードを誤魔化し覆い隠すコメントはコード自体の質は引き上げるかもしれませんが、コメントで妥協してしまうためコードを改善する機会を奪ってしまいます。

ウンコメントカタログ

どうせ書くなら良いものを。良いものを書くためには、良いものを知ることも必要ですが、ぼんやり悪いコメントを書かないためには、それを知っておく必要があります。なので悪いコメントの一例を考えてみます。

履歴コメント
// 2012/11/01 irof 修正開始
// oldMethod();
newMethod();
// 2012/11/01 irof 修正終了

修正履歴がコメント化したもの。場合によってはコメントのコメントなどが生まれます。VCSがある今、履歴コメントにもはや存在価値はありません。

日本語訳コメント
public boolean isInStock() {
    // カウントが0より大きかったらtrueを返す
    return count > 0;
}

コードで書いていることで明らかなこと、わざわざ日本語で書き直すコメントは不要です。このコメントは情報を追加しません。
これを記述すると、コードのメンテナンス時に同時に修正する必要が出てきます。単純にメンテナンスコストが2倍になります。その上、コメントの誤りは即座にフィードバックされませんし、コメントが置き去りになることは不可避です。そのためこのコメントはそのうちに後述する嘘コメントになる可能性を秘め、百害あって一利無しです。

多すぎるコメント
// ジュースストックから指定したジュースのラックを取得する。
// 取得したラックはrackに格納し、後続処理で使用する。
Juice rack = juiceStock.getRack(juice);
// ラックに在庫がある場合のみ処理する
if (rack.isInStock()) {
    // ラックからジュースを取り出す
    // ジュースが取り出せない場合は例外となるが、
    // 先に在庫を確認しているため通常は起こらない
    rack.remove();
    // ジュースが取り出せたら購入できるので、
    // ジュースの単価を購入処理に渡して処理する
    moneyFlow.purchase(juice.getPrice());
}

扱いが難しいコメントの一つですが、コメントがコードよりも多い場合は悪いコメントの可能性が高いです。
コードを読めばわかることも書いていますが、間違っていないため読む価値があるように思われ、コメントがあることで逆にコードを読む速度が低下してしまいます。コメントは必要な場所に必要なだけが鉄則です。不要な場所に丁寧に書かれたコメントは、コードの密度を希薄にし、水で薄めたジュースのようにしてしまいます。

嘘コメント
// 誕生日が来たので年齢を加算して返す
return age++;

コメントとコードが不整合な、明らかに悪いにも拘らず、扱いが厄介なコメントです。
嘘コメントを信じてしまうとバグりますし、コメントの誤りに気づいたとしてもそこからが大変です。
コメントに従ってコードを直せば良いのでしょうか?それともコードに従ってコメントを直せば良いのでしょうか?この手の誤ったコメントが付けられたコードは、その影響個所で本来やるべきだったことをしている*5可能性が出てきます。もしコードが既に運用されているならば、その可能性は飛躍的に高まることでしょう。そうなるとメンテナンスコストは跳ね上がってしまいます。

設計書マッピングコメント
// (3) 処理を継続する

// 1.2: 1.1で受け取った入力値を変換する
String output = null;
if (number % NUMBER_FIZZBUZZ == 0) {
    // (1) 15の倍数の場合、FizzBuzzに変換する
    output = "FizzBuzz";
} else if (number % NUMBER_FIZZ == 0) {
    // (2) 3の倍数の場合、Fizzに変換する
    output = "Fizz";

とてもとても厄介なのが、この設計書マッピングコメント。何が厄介かというと、中途半端な価値があります。設計書と対応しているこのコメントは、コードを書く際は下敷きコメントとして未熟なコーダーを導いてくれますし、書き終えた後のメンテナンス時も設計書との対応が明らかなため、メンテナンスを助けることも無くはありません。
ですが、コメントはコードに置き去られることを思い出してください。コメントは修正されにくいです。設計書の番号が変わることもあるでしょう。0番や枝番が登場して変わらない設計書もありますが、変わることもあります。その際に全てをきちんとメンテしきることは現実的ではありません。そんなことをして良いのは、自動的に更新される場合だけです。
設計書マッピングコメントは、設計書に自然言語で記された構造でプログラムを固定してしまいます。そのプログラミング言語に余計な制約を与えることになり、残念なコードを生み出す優れた方法です。

放置されたテンプレートコメント
/**
 * @author Administrator
 *
 * この生成されたコメントの挿入されるテンプレートを変更するため
 * ウィンドウ > 設定 > Java > コード生成 > コードとコメント
 */
public class Hoge {

ドキュメントコメントはテンプレートから自動生成することも多いです。時にそれが放置されます。
コメントがあるとまずコメントから読むものです。コメントはコードよりも、より人が読むことに焦点が当てられているはずなので、コードよりは読みやすいはずです。ですが、そこに書かれているのがただのテンプレートならば、得られるのは落胆のみです。多すぎるコメントと同様、この手のコメントに溢れていると、コメントは読まれなくなります。
特定のコメントを記述するルールとなっているプロジェクトもありますが、それ以外のコメントを書かなくていい理由にはなりません。定められたコメントしか書いていないことに気づいたならば、見直す機会かも知れません。

不安コメント
// 4で割り切れる年は閏年、ただし100で割り切れる年は平年、ただし400で割り切れる年は閏年
if (((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0) {
    ...
} else {
    ...
}

不安な箇所にコメントを書くことは良いことです。
もしコードが間違っていたら、コメントに従って直せば良いですから……ん?と思った方は正解。そんな事には使えません。少なくとも、自分が書いたコメント以外は。コメントが合っているかなんて誰にもわかりませんし、コメントが置き去りになることは不可避です。誤ったコメントも残念ながら世界中に大量に存在しています。コメントの正しさを疑わなくていい、なんて言うお花畑はどこにも無いでしょう。
コードを書いた人が不安だったと言うことは、未来に重要な情報になるかもしれません。ですが一方で、もしその不安さの由来が「コードの挙動」ならば、より誤解無いコードに書き換えたり、テストするなど、その不安は払拭しておくべきでしょう。
不安の由来が挙動ではなく仕様ならばコメントに書くよりもっととりうる手段はあるはずです。将来の変更に耐えうるかなど、ウィークポイントなどが見えている場合は、その不安は記述すると良いと思います。
反面、単なる不安コメントが残っていることは、そのコードは注意するべきであると言う警笛にもなります。将来役に立たないと言い切ることは難しいです。

領域コメント
// 事前処理
(前準備のコードをダラダラと)

// 処理本体
(複雑な分岐や深いネストを含むコードがどかんと)

// 事後処理
(後片付けのコードをダラダラと)

コード内に「ここからここまでは何をしているか」を書いているコメントです。これを悪いと言ってしまって良いのかは難しいのですが、このようなコメントが必要になる状況はあまり好ましくありません。
この手のコメントを必要とする場合、メソッドは長くなりがちです。数行のメソッドならば書かない、つまり「長くなったから仕方なく書いている」あたりに、このコメントの好ましくなさが現れているのではない気がします。
と言いつつ、あってもまず害は無いでしょうし、別にあっても良いかなとかなんとか……。

一歩引いて考える

この辺りで一度立ち止まって、コメントを書きたくなる瞬間を考えてみます。
私がコメントを書きたくなるのは、先に挙げた動機の通りなのですが、その中で一番重要だと思うのは「なんとなく」です。なんとなくを無視してはいけません。なんとなくはコードからの言葉にならないメッセージであり、それに従って書かれたコメントは、言葉になり辛い思いが言語化されたものです。コメントは曖昧なものの押しつけ先になりがちですが、曖昧なそれを表現できる場でもあるので、有効活用するべきです。
コメントを書きたいと思ったら、書く。書いてから考える。このコメントは良いコメントか?悪いコメントか?衝動に任せて書かれたコメントには悪いコメントも多く含まれ、悪いコメントは悪いコードに付けられることが多いです。つまり、悪いコメントはより良いコードへの招待状なのです。

コメントで悪いコードは良くならない

  • コメントで、ダメなコードを取り繕うことはできない。 - CleanCode
  • 悪いコードにコメントをつけるな。書き直せ。 - プログラミング作法

コードの良し悪しはコードに宿ります。コメントができることはその飾り付け程度です。悪いコードに付けられるコメントは、悪いコメントです。コメントができることは、せいぜいコードの悪さを薄める程度です。打ち消すことも、軽減することもできません。
悪いコメントはコードの抱える負債です。無駄に利息を払うことになる前に返却するべきでしょう。

悪いコメントの処方箋

悪いコメントに遭遇したときできることは、減らすことです。そのコメントが残っていると、次に呼んだ時もまた同じだけの多くのコストを払い、僅かなリターンを得られることになります。そんなことは誰も望んでないはずです。

素直に消す

履歴コメントなどは即刻削除です。嘘コメントも削除です。あるだけで害になるコメントに問答は必要ありません。とにかく消してから考えましょう。

まともな名前をつける

変数名やメソッド名で表現できるならば、そのようにしておくのが良いです。コメントで取り繕えるのはその場だけですし、リファクタリングツールの恩恵を受けることもできません。何を書いているかわからないコードに対して必要なのは、コメントではなく、意味の通じる変数名に変える、説明用の一時変数を使用する、説明メッセージを使用する、等です。

クラス・メソッドを抽出する

同じコンテキストのものを抽出することで、コードの責務を局所化し、明瞭なコードにすること。領域コメントなどに有効なパターンです。領域毎にメソッド抽出できるならば行い、抽出したメソッドをさらにメソッドオブジェクトに抽出するなど、この辺りの説明はリファクタリングに譲ります。

対称性の原則に従う
  • 実装パターンより

対称性の原則は対になるもので構成されるべきであると言う考え方。以下は対称性の原則を侵害している例です。

void process() {
    input();
    count++;
    output();
}

inputとouputの抽象度は揃っていますが、count++はいきなり処理の詳細です。このコードにはコメントを書きたくなります。この例ではcount++の一行だけなので、そこまで目くじら立てなくても……と思われるかもしれませんが、往々にしてここには複雑な分岐や深いネストの処理が書かれることになります。そこにはコメントを書くのではなく、メソッド抽出し、抽象度を揃える。するとコメントを付ける気が起きないくらいコード自体の可読性が上がります。

テストで表現する

コメントはコードに置き去りになります。一方で、テストはコードと同期します。テストが十分に読みやすければ、コメントを書くのではなくテストで表現する手もあるかもしれません。
また、コメント(つまり自然言語)では表現し辛い挙動をテストで表現することは良いアプローチに思えます。守らなければならない注意書きをコメントに書くくらいなら、守らなければ失敗するテストを書いておくのが良いでしょう。
「テストの可読性が十分に高い」や「テストで表現されることが共有できている」などが半ば理想論に聞こえるかもしれませんが、その時は既に来ている気もします。

読む人を意識する

「誰でも読めるように」なんて言って、「誰もが読み辛いコード」が書かれることは頻繁にあります。さらにそれに追い討ちをかけるように、誰でも読めるようにコメントを追加するとかあります。読み辛いコードに膨大なコメントが付与されると、読み辛さは加速していきます。
コードにコメントが必要なのは、コードで表現し辛いコンテキストが存在するからかもしれません。その場合、そのコードに触れる人はどのような人なのかを考えて見ても良いかもしれません。読むのはその言語を修得したプログラマであり、さらにそのシステムの概要は理解している。そんな前提ならば、書くべきコメントは減る可能性があります。
具体例は控えますが、バカらしいコメントが求められることもあります。「書くことになっているから」なんて理由で書くべきコメントはそう多くありません。そんなコメントは読まれませんし、そんなコメントばかりになるとコメント全てが無視されてしまいます。
どの程度のコンテキストを意識すれば良いのかは、これも状況次第になってしまうのですが……。

コメントは何か?

コメントは、コードのすぐ傍にある、コンピュータではなく人――特にプログラマ――にを対象にしたコミュニケーションツールです。コメントは軽視されがちですが、上手く使えばもっと良い効果をもたらしてくれるはずです。DRY原則に従えば、コメントとコードに同じことを書くべきではなく、低水準なコードに対して、コメントには高水準な情報を記述するべきと言えます。
コメントに対する借りて来た例を紹介します。

コメントは消臭剤である

コメントはコードの臭いを消してくれるものです。つまり、臭くないコードに消臭剤コメントは書かれないのです。コメントがある時点で「このコードは臭いのかもしれない」の予断を与えることになります。もしそのコードが臭くないならば、消臭剤コメントは書くべきではないでしょう。
さらにこの消臭剤、無臭ではありません。過剰にあると逆に臭くなってしまいます。これでは本末転倒も良いところです。

コメントは装飾

コメントはデコレーションケーキを飾る砂糖菓子のようなものです。見栄えの良い綺麗に作られた砂糖菓子は、ケーキをより綺麗に、美味しそうに見せてくれます。一方で、過剰な装飾は食欲を減退させますし、飾りに覆われてしまうと本当は何だったのかわからなくなります。
コメントはあくまで飾りに留まるべきで、必要な場所に必要なだけ、パンチの効いたコメントがあれば、コードはぐっと読みやすくなるはずです。

まとめ

コメントは純粋に良いものではありません。CleanCodeの中では「必要悪」とすら書かれています。コメントを書くのは、プログラミング言語での自身の表現力が及んでいないことの現れです。それは単に自分のプログラミング能力不足かもしれませんし、プログラミング言語自体の限界かもしれません。コメントは、コードでは表現できなかったことを未来に伝えるために、コードに情報を追加するためのものです。
どうしてもコードで表現できなかったもの、コードに表現すべきでないと判断したものを除いて、コメントでコードを誤魔化したら負けです。コメントを書いて納得してしまうと、もっとコードが良くなるチャンスを奪ってしまったことになります。ですが、コメントを書くべきところに書かないのは最悪です。コメントを書いたら負けなんてそんな目先の勝敗に気を取られて大局を見失うのは愚の骨頂です。書くべき時は書きましょう。

置き去りになり、誤ったものになるリスクを許容してでも伝えるべきことであり、かつコードのすぐ傍にあることに価値がある。このコメントはそうなっているか?なんて判断をコメントを残す時にしてみるのも良いんじゃないでしょうか。

参考文献

Clean Code アジャイルソフトウェア達人の技

Clean Code アジャイルソフトウェア達人の技

Code Craft ~エクセレントなコードを書くための実践的技法~

Code Craft ~エクセレントなコードを書くための実践的技法~

実装パターン

実装パターン

達人プログラマー―システム開発の職人から名匠への道

達人プログラマー―システム開発の職人から名匠への道

  • 作者: アンドリューハント,デビッドトーマス,Andrew Hunt,David Thomas,村上雅章
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/11
  • メディア: 単行本
  • 購入: 42人 クリック: 1,099回
  • この商品を含むブログ (349件) を見る
プログラマが知るべき97のこと

プログラマが知るべき97のこと

プログラミング作法

プログラミング作法

リファクタリング―プログラムの体質改善テクニック (Object Technology Series)

リファクタリング―プログラムの体質改善テクニック (Object Technology Series)

  • 作者: マーチンファウラー,Martin Fowler,児玉公信,平澤章,友野晶夫,梅沢真史
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/05
  • メディア: 単行本
  • 購入: 94人 クリック: 3,091回
  • この商品を含むブログ (312件) を見る

書いてることは同じだったりぶつかったりしてて、結構面白かったです。

*1:ガイドレールコメントとか名付けようとしたのですが、イマイチ語呂がよくないのでやめておきました。

*2:コメント処理ツールはこの場では考えないでおいてください。

*3:議論の経緯などはチケット管理システム等に残す方が良い場合も多くあると思います。

*4:選択したことがわかる名前にするアプローチもあると思います。例えばArrayListからはListに他の実装があることがわかる気がします。

*5:この例では可算後の年齢を取得するための age + 1 などを行う