世界を動かす技術を、日本語で。

コメントは「何」を説明すべきかもしれない (2017)

概要

  • コメント は「なぜ」だけでなく「何を」も説明すべきという主張
  • クリーンコード とコメントの役割の違いを解説
  • コンテキストスイッチ の弊害を指摘
  • メソッド分割 とコメントのバランスを議論
  • コメントの「何を」説明する有用性を具体例で提示

コメントは「なぜ」だけでなく「何を」も説明すべき理由

  • 一般的に「 コメントはなぜを書くべき、何を書くべきではない」とされる風潮
  • しかし、 コードだけで全てを明確にするのは困難 な場合も存在
  • 例:
    • //weight, radius, price w = 10, r = 9, p = 1 よりも weight = 10, radius = 9, price = 3 の方が明確
  • 省略名(w, r, p)は直後なら意味が分かるが、 後で見たときに混乱 しやすい
    • 例: 「w」を「width」と誤解するリスク
  • コメントはクリーンコードの代用にならない が、補助的な役割として有効

「なぜ」をコメントで説明する重要性

  • 「なぜ」は コミットメッセージやテストに残すべき という意見もある
  • 例: // Clear twice to deal with bug ABC in library XYZ, see [link] XYZ.clear(); XYZ.clear();
    • このコメントがなければ、「なぜ2回呼ぶのか」理解できず誤った修正をする恐れ
  • コミット履歴を遡るのは手間 であり、コンテキストスイッチが発生
  • コメントで直接理由を記載することで 即座に意図を理解 できる

説明的なコードが逆に理解を妨げる場合

  • Bob Martinの「Extract Till You Drop」例

  • 説明的なメソッド分割により コードが短く見える が、全体理解には 複数メソッドの追跡が必要

  • バグ修正時など、 何度もメソッド間を移動する手間 が発生

  • コメントで「何を」説明することで 一箇所で理解できる 利点

    • 例: Pattern symbolPattern = Pattern.compile("\$([a-zA-Z]\w*)"); // 例: $F1a3 Matcher symbolMatcher = symbolPattern.matcher(stringToReplace); // 全シンボルを置換 while (symbolMatcher.find()) { ... } // ここで全インスタンスを置換

「何を」説明するコメントの有用性

  • 全てのケースでコメントが必要なわけではない
  • しかし、 一部のケースでは「何を」説明するコメントが最適解
  • コメントを全否定せず、 状況に応じて柔軟に活用 する姿勢の重要性

Uncle Bob批判と賛同するエッセイ紹介

  • Uncle Bobの例をよく批判しているが、「The Lurn」というエッセイには賛同
  • プログラミング言語学習の過剰評価 よりも、他の有用な知識の重視を提案

  • 結論: コメントは「なぜ」だけでなく「何を」も適切に説明することで、 コードの可読性と保守性を向上 させる
  • クリーンコードコメント のバランスを意識した開発姿勢が重要

Hackerたちの意見

もう真面目にアンクルボブスタイルのプログラミングを使ってる人はいない気がするな(各行をそれぞれのメソッドに分けるやつ)。一時期は流行ってたけど、あんなコードベースのバグを直そうとしたことがある人なら、この記事が言ってることがよくわかるはず。定義に飛ぶキーを何度も押すのが本当にイライラするし、順番に動く別々の部分を行ったり来たりするのも面倒くさい。あの本があんなに大きくなった理由が全くわからない。試してみれば、めちゃくちゃ面倒で可読性には全然役立たないってわかるのに。

本を書いて「クリーンコード」ってタイトルで出版するのは、めちゃくちゃマーケティングになるんだよね。あのスタイルについて、実はそんなにシンプルじゃないって議論をたくさんしたけど、相手はただ本を指差すだけだった。

もう真剣に叔父ボブスタイルのプログラミングを使ってる人はいない気がする(各行が自分のメソッドに抽出されるやつ)でも、そういうのを楽しむGoの人たちがたくさんいるんだよね(8ファイルを通してインターフェースがインターフェースを呼び出すのを見てた頃を思い出す…結局「この暗号キーを設定する」ってだけになったし、最初からトップにあったらよかったのに)。

IDEの「レンズ」拡張機能を作るのはどれくらい大変なんだろう?マウスオーバーしている関数の再帰的にインライン化されたバージョンを、自動的に表示してくれるやつ。例えば、20行未満の時とか。

IDEの拡張機能で、マウスオーバーしている関数の再帰的にインライン化されたバージョンを自動的に表示してくれるのは、どれくらい難しいんだろう?例えば、20行未満の時とか。

彼が本物のプログラマーに出会った時の例はこれを見てみて: https://github.com/johnousterhout/aposd-vs-clean-code A Philosophy of Software Design はすごく素晴らしくて、過小評価されている本だよ: https://www.goodreads.com/en/book/show/39996759-a-philosophy... 俺はこれを超おすすめするし、俺のコードを明らかに改善してくれた --- もう一冊の本は、上司のデスクに置かれた時に彼の能力を疑わせるもので、でもその後モニターの下に置かれて、彼の意見を反映してたな…。

書かれた時代や対象が違ったからね。(t, p, luみたいな変数名が普通だった頃)自分には役立ったし、他の人にもそうだったと思うけど、あんまりそのアドバイスを文字通りに受け取ったことはないな(作者がそう意図してたとしても)。他の本や議論、アドバイス、経験を基に、私は「長い(例えば、複数ページの)関数は悪い」と覚えておくことにしてる。今のコンピュータサイエンスの卒業生はもっと良く知ってると思うけど、これは業界の常識になったからね。

それ、めっちゃ共感するわ。「各クラスはそれぞれのファイルに」とか「各関数はそれぞれのファイルに」とか言われるスタイルには本当にイライラする。必要なものは目の前に全部あった方がいいのに、無理にバラバラにする必要なんてないよね。前に働いてた会社でこれを言ったら、「すごく整理されてる」とか言われて笑われたわ。結局、普通の人は批判的に考える力がゼロだってことだね。

各行がそれぞれのメソッドに抽出される ジョン・カーマックが言ったように、「多くの操作が順番に行われるべきなら、そのコードも順番に書くべきだ」(https://cbarrete.com/carmack.html)。数行の単一メソッドは読みやすいけど、メソッド間を行ったり来たりするのは気が散るし遅い。プロセッサが様々なRAMの場所を読み込むのと同じようにね。言語によっては、多くの行を書く理由がある場合もある。例えば、Javaではメソッドが複数のプリミティブ値を返せないから、パフォーマンスのためにプリミティブにこだわるなら、インラインにして中括弧で内部のスコープを制限する必要がある。

その頃のRubyエコシステムは「DRY」(対 WET)や間接的な書き方が特にひどかった。Sandi Metzが「実践的オブジェクト指向デザイン」を紹介するまでは、かなり厳しかったよ。これがきっかけで「賢い」とか「職人技」とか「エレガント」から、未来のプログラマーにとってもっと実用的な方向に動き出したと思う。Rubyコードのデバッグをしたとき、スタックトレースの行が存在しないことを覚えてる人いる?コードが実行時に動的に生成されてボイラープレートを減らすためにね。ペパリッジファームは覚えてるよ。

ボブ・マーチンの例を鵜呑みにするのはやめた方がいいよ。「アンクルボブみたいにリファクタリングしないで」っていうのもあるしね: https://theaxolot.wordpress.com/2024/05/08/dont-refactor-lik...

ありがとう!まだ若手だった頃にそのリファクタリングのアドバイスをそのまま受け入れたせいで、すごく過剰に抽象化されたフレームワークを書いちゃって、デバッグや新機能追加のときに何年も苦しむことになった。習慣を変えるのは簡単じゃなかったよ…

Hacker Newsで議論の続きを見る