【動画付き・初心者向け】コメントにはWHYを書こう 〜不要なコメントと必要なコメントの違いについて〜

## はじめに
僕は[フィヨルドブートキャンプ](https://bootcamp.fjord.jp/)（以下FBC）というプログラミングスクールでメンターをやっています。そこでよく生徒さんが書いたプログラム（課題の提出物）のコードレビューをよくやるのですが、5人に4人ぐらいの割合でコメントの書き方について同じような指摘をしています。

コメントの書き方ついては、すでにネットや書籍で同じような議論が山ほどありますが、この記事ではあらためて僕の考える「不要なコメントと必要なコメントの違い」について書いてみようと思います。

## 動画があります！
この記事は以下の動画の内容を雑にテキストとして書き起こしたものです。動画で見たいという方は、以下の動画をご覧ください。（FBCの生徒さん向けに作った動画ですが、大半の内容はFBCの生徒さん以外にも役立つはずです）

<iframe width="560" height="315" src="https://www.youtube.com/embed/WNGRrfbs7eg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

## 不要なコメント＝説明的なコメント（見ればわかるコメント）
「コードを見ればわかる内容」をわざわざコメントで説明する必要はありません。たとえば以下のようなコメントはプロの現場では「削除してください」と言われることが多いです。

```ruby 
# 対象データが2件以上あるときは合計値も追加する
if wc_rows.size > 1
  lines << build_sum_line(wc_rows, line_only)
end
```

```ruby 
# 各行の件数を集計して一行ぶんの文字列を返す
def build_sum_line(wc_rows, line_only)
  sums = %i[line_count word_count byte_count].map { |attr| wc_rows.sum(&attr) }
  format_line(*sums, 'total', line_only)
end
```

説明的なコメントは以下のようなデメリットがあります。

- コードとコメントで同じ内容が2回書かれるだけなので、上級者が見るとノイズにしかならない
- 将来、実装が変わってもコメントがそのまま放置されて、実装とコメントの内容が乖離する可能性がある
- コードとコメントを一緒にコピペされた場合、コピペ先の改変内容がコメントと一致しないまま放置される可能性がある
- コードとコメントの内容が一致しないと、そのコードを読んだ人の誤解や混乱の原因となる

### 「でもコメントがないと後でコードを読んだときにわからないんだけど？」
プログラミング初心者のみなさんは「コメントを書いておかないとあとでコードを読んだときにわからないんです！」と言いたくなるかもしれません。その場合はコメントを書くのではなく、コードがわかりやすくなるようにリファクタリングするのが基本です。

- 変数名やメソッド名を見直す
- 長すぎるロジックをメソッド分割する
- 何段にもネストした条件分岐やループをシンプルにまとめる

といったリファクタリングを実施して、コメントがなくても理解できるコードに改善しましょう。

## 必要なコメント＝WHYを書いたコメント（読み手のなぜ？という疑問に答えるコメント）
反対にコメントが必要なときはどんなときかというと、他の人（または未来の自分）がそのコードを見たときに「なんでこんなことやってるの！？」と思うような場合です。

コードを読んだ人の

「普通だったらAするよね？なんでここはBしてるの？？」

という疑問に対して、

「実はかくかくしかじか、こういう理由でやむを得ずBしてるんです（または、あえてBしてるんです）」

という背景（言い訳？）をコメントに書くのが、プロの現場におけるコメントの書き方です。

たとえば、以下のコメントは「なんでメソッドを定義するんじゃなくて、わざわざlambdaを使ってるの？？」という疑問に対する回答（言い訳）を入れた例です。

```ruby 
def format_line(line_count, word_count, byte_count, file_name, line_only)
  numbers = [line_count]
  numbers += [word_count, byte_count] unless line_only
  # mapメソッドをシンプルに&メソッド名で書きたかったので、lambdaを使った
  fmt = ->(n) { n.to_s.rjust(8) }
  "#{numbers.map(&fmt).join} #{file_name}".rstrip
end
```

## その他、こういうときもコメントを書きます
### 出典や参考情報をコメントする
そのコードを書くにあたって、何か参考にした情報があれば、その参考URLや出典をコメントに書くことがあります。（特に自力では思いつけなかったロジックやアルゴリズムを拝借してきた場合など）

```ruby 
# 下記の記事を参考にした
# https://github.com/heartcombo/devise/wiki/OmniAuth:-Overview
def self.from_omniauth(auth)
  where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
    user.email = auth.info.email
    user.password = Devise.friendly_token[0, 20]
    user.name = auth.info.name   # assuming the user model has a name
    user.image = auth.info.image # assuming the user model has an image
  end
end
```

### TODOコメントやFIXMEコメントを書く
「あとでやろうと思っているが、今はやむを得ず後回しにする」という内容をTODOコメントとして書く場合があります。

```ruby 
test '一覧画面のページネーション' do
  visit books_path
  assert_text '本-30'
  assert_text '本-6'
  assert_no_text '本-5'

  # TODO: 「次へ」ボタンをクリックしたときのテストも書く
end
```

他にも「自分ではどうしても直せなかったので、誰かあとで直して！」の意味で、FIXMEコメントを入れることもあります。

```ruby 
test '本の削除' do
  click_link '削除する'

  # FIXME: なぜかここでsleepしないとCIでテストが落ちる
  sleep 1

  assert_text '本が削除されました。'
end
```

ただし、どちらのコメントもそのまま何年も放置され続けることがよくあるので、なるべくプルリクエストを作る前に全部潰しておくのが理想的です。

### 複雑怪奇なロジックをコメントで説明する（ただし最後の手段）

先ほどは「コメントを書くのではなく、コードがわかりやすくなるようにリファクタリングするのが基本」と書きましたが、どう頑張っても複雑にならざるを得ないロジックであれば、理解を助けるためにコメントを書くのはアリです。

ただし、熟練者が考える「これ以上はどうにもならん」というレベルと、初心者が考える「これが限界😣」というレベルには大きな隔たりがあります。
初心者の自覚があるうちは、コメントを書き足すよりも周りの上級者を頼ってコードのリファクタリングを試みるのが吉です。

## 応用：実装のTODOリストとしてコメントを書く（そして最後に消す）
コメントの応用的な使い方として、実装のTODOリストとしてコメントを書く場合があります。たとえば、こんな感じです。

```ruby
def to_wc_rows(text, file_names)
  # ファイルが空だったら
    # テキストを配列に入れて返す
  # それ以外
    # ファイル名を全件ループして、テキストを配列に詰めて返す
end
```

コメントを書いたら、このコメントに沿うように実装コードを書きます。

```ruby
def to_wc_rows(text, file_names)
  if file_names.empty?
    # ファイルが空だったら
    # テキストを配列に入れて返す
    [WcRow.new(text)]
  else
    # それ以外
    # ファイル名を全件ループして、テキストを配列に詰めて返す
    file_names.map do |file_name|
      text = Pathname(file_name).read
      WcRow.new(text, file_name)
    end
  end
end
```

実装が終わったらコメントを削除します。

```ruby 
def to_wc_rows(text, file_names)
  if file_names.empty?
    [WcRow.new(text)]
  else
    file_names.map do |file_name|
      text = Pathname(file_name).read
      WcRow.new(text, file_name)
    end
  end
end
```

結果としてコメントは残らないのですが、こういったコメントの使い方は上級者でもよくやります。

## 例外：教育用の説明コメント

仕事で書くコードではなく、初心者に教えるためのサンプルコードには、読者の理解を助けるために説明コメント（WHYでないコメント）が書かれます。

下記のコードは[Railsガイド](https://railsguides.jp/active_record_basics.html)に載っているサンプルコードの抜粋です。

```ruby
# 名前がDavidで、職業がコードアーティストのユーザーをすべて返し、created_atカラムで逆順ソートする
users = User.where(name: 'David', occupation: 'Code Artist').order('created_at DESC')
```

ただし、これはあくまで教育用のコメントです。もしこうしたコードをコピペして使うことがあっても、教育用の説明コメントはコピペした後に削除するようにしましょう。

```ruby
users = User.where(name: 'David', occupation: 'Code Artist').order('created_at DESC')
```

## まとめ

というわけで、本記事のまとめです。

**不要なコメント**

- 説明的なコメント（コードを見ればわかるコメント）
  - 見てもわからないコードはコメントで逃げるのではなく、見ればわかるコードにリファクタリングする

**必要なコメント**

- 「こういった理由で、やむを得ず（あえて）こうやってます」というコメント（WHYを説明するコメント） 
- 出典や参考情報を残すコメント
- TODOコメント、FIXMEコメント
  - できればプルリクを出す前にすべて潰しておきたい

**応用的なコメント**

- 実装のTODOリストとしてのコメント（最終的には消されるので他の人の目には触れない）


本記事がプログラミング初心者のみなさんの参考になれば幸いです！