
2026/01/04 19:43
「コメントは『何』について説明すべきだ(2017)」
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
概要:
記事は、コメントが「何を」コードが行っているかと「なぜ」書かれたのかを説明すべきだと主張していますが、それらが価値を付加する場合にのみです。短い変数名(例:、w、r)は意図を曖昧にし、読者がコメントに頼るよう促します。説明的なコメント―例えばp―が移動または再フォーマットされると、その根拠が失われ、git blame のようなツールで復元するのが困難になります。「なぜ」情報をコミットメッセージやテストに頼ると、コンテキストスイッチが発生し、コメントがすぐに表示されない場合に危険な仮定につながる可能性があります。説明的なコメントを参照するコードの隣に保つことで検索時間が短縮され、誤解釈リスクが低減します。この議論では、リファクタリング後にコメント付き行が消失したバグと、多数の小さなメソッドであっても関数間をジャンプし続ける必要があるボブ・マーティンの例が引用され、可読性とコンテキストスイッチのバランスが強調されています。最後に著者は、コメントはツールであり、クリーンコードの代替ではないことを読者に思い出させ、「何」と「なぜ」のコメントの適切な組み合わせについて継続的な“炎上”議論を呼びかけています。// Clear twice to deal with bug ABC in library XYZ
この改訂版は10項目すべてを取り入れ、クリーンコードとの関係におけるコメントの役割を明確化し、元のメッセージの明瞭さと焦点を保っています。
本文
人は「コメントは『何』ではなく『なぜ』を説明すべきだ」とよく言います。
私は今、コメントに「何」を説明させるべきだと主張したいので、炎上戦争の種になるかもしれませんが、その点について議論してみます。ただし、これを悪コードを書くための口実に使わないでくださいね。
なぜ「何」を説明するコメントは必要ない?
コメントで「何が起きているか」を説明する必要があるとすれば、ということはあなたのコード自体が不明瞭だということを示しています。
例として:
// weight, radius, price w = 10; r = 9; p = 1;
より
weight = 10; radius = 9; price = 3;
のほうが明確です。
w が「重量」であることは、連続して書かれた行を見れば直感的に分かりますが、その後で w の意味を確認するためにコードの別箇所へ移動しなければならず、文脈切替が発生します。その結果、誤って「幅(width)」と勘違いしたり、重大なバグにつながる危険性があります。コメントはきれいなコードの代わりにはなりません。
なぜ「なぜ」を説明するコメントは必要か?
一部では「なぜ」についてはコミットメッセージやテストに書くべきだと主張しますが、実際には多くの人がそれを不自然だと感じます。
// Clear twice to deal with bug ABC in library XYZ, see [link] XYZ.clear(); XYZ.clear();
このコメントを削除してコミットメッセージに移すことは可能でしょうか?
もし「なぜ
XYZ.clear() を2回呼ぶのか」を知りたければ、コミットログまで遡らなくてはいけません。行がリファクタリングされたり別ファイルへ移動した場合には git blame も機能しないことがあります。その検索は文脈切替であり、時に「不要なバグだ」と誤って判断してしまう危険性があります。
これら2つのケースは同じ問題を共有しています:情報を探す作業が難しいという点です。
- 最悪の場合:時間のかかる文脈切替
- もっとひどい場合:不正確な仮定
したがって、必要な情報は「コード自体」もしくは「コメント」の中に残す方が安全です。
「変わった」ケース
説明的なコードが逆に文脈切替を強いることがあります。Bob Martin の Extract‑Till‑You‑Drop から例を取り上げます。
String replace() { Pattern symbolPattern = Pattern.compile("\\$([a-zA-Z]\\w*)"); Matcher symbolMatcher = symbolPattern.matcher(stringToReplace); while (symbolMatcher.find()) { String symbolName = symbolMatcher.group(1); if (getSymbol(symbolName) != null && !alreadyReplaced.contains(symbolName)) { alreadyReplaced.add(symbolName); stringToReplace = stringToReplace.replace("$" + symbolName, translate(symbolName)); } } return stringToReplace; }
Martin はこれを次のようにリファクタリングします。
String replace() { replaceAllSymbols(); return stringToReplace; } private void replaceAllSymbols() { for (String symbolName = nextSymbol(); symbolName != null; symbolName = nextSymbol()) replaceAllInstances(symbolName); }
さらに下位メソッドへ分割していきます。
結果として
replace() はわずか2行ですが、クラス全体を理解するには6つの別メソッドに飛び回る必要があります。「読みやすくなった?」という質問に対し、バグ追跡中で上下へスクロールしながらメソッド間をジャンプするのは実際には手間が増えます。
このような場合、コメントによって「何」を説明しておけば別メソッドへ飛び回る必要がなくなるので理解しやすくなります。もちろん全ての場合に当てはまるわけではありませんが、「何」についてコメントを書くのが適切なケースも存在します。これらを一括りに否定するべきではありません。
String replace() { Pattern symbolPattern = Pattern.compile("\\$([a-zA-Z]\\w*)"); // 例:$F1a3 Matcher symbolMatcher = symbolPattern.matcher(stringToReplace); // 全シンボルを置換 while (symbolMatcher.find()) { String symbolName = symbolMatcher.group(1); // translate はすべてのインスタンスを置換するので、一度だけ実行すればよい if (getSymbol(symbolName) != null && !alreadyReplaced.contains(symbolName)) { alreadyReplaced.add(symbolName); stringToReplace = stringToReplace.replace("$" + symbolName, translate(symbolName)); } } return stringToReplace; }
このバージョンは、オリジナルやリファクタリング版よりも「何」を理解しやすいです。もちろんコメントが不要なケースも多々ありますが、「何」について説明することが正しい選択になる場面は確かに存在します。
まとめ
- コード自体が不明瞭であれば、「何」を説明するコメントが必要になります。
- 「なぜ」の理由はできるだけコードやコミットメッセージに残し、コメントはそれを補完する形で使用します。
- コメントとリファクタリングのバランスを取りながら、情報へのアクセスコストを最小限に抑えることが重要です。
以上が私の主張です。炎上戦争になりそうですが、ご賛否自由です!