hiyoko-programingの日記

プログラミングを勉強したてのひよっ子。   エンジニア目指して勉強中。

良いコードの書き方

良いコードとは

良いコードとは、読みやすいコードのこと。読みやすいコードは、「他の人が短時間で理解できるように書かれている」ことを指す。
読みやすいコードを書くことは、一緒に開発するチームのためだけではなく、自分のためにも重要。

今後複数人で開発するにあたって、コードを書く時間よりもコードを読む時間の方が多くなる。複数人で開発をしていると他の人の書いたコードを読むことも増える。コードの理解しやすさは、プログラマが大切にすべき要素の1つである。
これから良いコードを書くための知識を身につける必要がある。

良いコードの要素

命名規則

変数やメソッドの名前はプログラムの挙動に影響しないため、好きなように命名することができる。変数であれば、どのようなデータを格納するのか、メソッドであれば、どのような処理をするのかを明確に伝えられるような命名を心がける。

命名規則のポイント

良い命名を行うポイント。

  • 明確で具体的な単語を選ぶ
  • 汎用的な名前を避ける
  • 名前に情報を含める
  • 誤解されない名前を使う
  • 他の開発者の期待に合わせる

明確で具体的な単語を選ぶ

メソッド名は他の開発者がひと目で意味を理解できるようにする。
例えば、 「取得する」という意味で get を使うことは多いが、「Webページを取得する」のように目的が決まっている場合は get_page のようにしたほうが良い。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Webページを取得するメソッド

# bad
def get(url)
  # ...
end

# good
def get_page(url)
  # ...
end

汎用的な名前を避ける

メソッドや繰り返しの途中で一時的な変数を保存しておきたいことがよくある。この時に tmp(一時的なという意味) のような汎用的な変数名を使ってしまうと可読性が下がってしまう。
可読性を損なわないために、「計算途中の合計値」を保存しておきたい場合は sum という名前を使ったほうが良い。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 平均を計算するメソッド

# bad
def average(list)
  tmp = 0.0
  list.each { |item| tmp += item }
  return tmp / list.count
end

# good
def average(list)
  sum = 0.0
  list.each { |item| sum += item }
  return sum / list.count
end

名前に情報を含める

変数の値を大文字や小文字にするなど、あるルールに沿って変換して新しい変数として利用したい場合を想定する。
その場合は変数名に「大文字にされた」(変換された)という意味をもたせることで、何のための変数かが伝わりやすくなる。

1
2
user_name = 'Yamada Taro'
uppercased_user_name = user_name.upcase

誤解されない名前を使う

変数名やメソッド名はほとんどの場合、英単語を使う。使用する英単語が複数の意味を持っていたり、ニュアンスが異なる意味をもっている場合は注意。
例えば、以下の read_books というメソッドは「読んだことのある本」なのか「本を読む=既読にする」なのかがすぐには理解できない。
そのような場合には、別の単語に変えるか、名前を長くする。

1
2
3
4
5
# bad
@user.read_books # 本を読む or 読んだことのある本

# good
@user.already_read_books # 読んだことのある本

already_read_booksと定義してあげれば、すぐに「読んだことのある本」だということがわかる。名前をつけるときは、他の意味と間違えられることはないか と何度も自問自答すると誤解されにくい名前をつけることができる。

他の開発者の期待に合わせる

開発に慣れると、これはこのような動作をするはずという「期待する動作」がつかめてくる。そのような「期待」を他の開発者も持っている。その「期待」に沿うことで冗長な記述を省略できたり、レビューが効率的になりチーム全体の開発スピードが上がる。このような「期待」を裏切らないコードを書く。
例えば、以下のように「ユーザーが持っている本」を配列で返す books というメソッドがあるとする。あるユーザーが本を持っていない場合に nil を返してしまうと、本を持っていない場合は空の配列になっていると思っている開発者の「期待」からは外れてしまう。
他の開発者が、@user2.books.each { ... } とした時にてエラーが出て迷惑をかけてしまう。

1
2
3
4
5
6
7
@user1.books => ['よくわかるRuby', 'JavaScript入門']

# bad
@user2.books => nil

# good
@user2.books => []

よく使う英単語

プログラミングで使う英単語はある程度決まっている。また、一般的な英単語に加えて特別な意味が含まれていることがある。一般的な英単語では同義語でも、プログラミング中で特別な意味を持つこともあるので注意。

英単語 説明 具体例
set 主にクラスのプロパティに値を代入する場合に用いる。 set_projects
add 追加。配列・リストにデータ、オブジェクトを加えるケースが多い。数値の加算もこちらを用いる。 add_user_nickname
render 画像などを編集した結果を表示したり、項目を HTML などに変換した上で返す。 render_html
import ファイルを対応形式に変換して読み込む。 import_projects_show_file
fetch (別の場所から)情報を取ってくる。CPU がメモリから命令コードを取ってくることを指す場合もあるが、データベースから情報を取得することを指すことが多い。 fetch_account
modify 部分的に修正する。edit より用途が広く、データのメインとなる箇所の変更はもちろん、それ以外の小さな変更であっても modify と表せる。 modify_indentation
apply 適用する。当てはめる。 apply_projects_css
remove データをリストや閲覧・アクセス可能な場所から取り除く。アクセス権のない領域への移動。取り除かれたデータが消滅するとは限らず、元に戻せる可能性がある。 remove_user_data
is~ オブジェクトが特定の型、状態であるか調べて true/false を返す。 is isChecked

他にもよく使われる英語があるので都度調べる。

コードのレイアウト

コードのレイアウトはプログラムの挙動に影響することはほとんどない。コードのレイアウトを整えることで可読性を大幅に上げることができる。

コードのレイアウトのポイント

コードのレイアウトを整えるポイント。

  • 整列
  • 一貫性
  • ブロック化

整列

整列とは、縦の位置を揃えること。スペースを使って=(イコール)などの位置を揃えることで可読性を高めることができる。

1
2
3
4
5
6
7
8
9
# bad
name = params[:name] || 'No name'
age = params[:age] || 30
prefecture = params[:prefecture] || 'Tokyo'

# good
name       = params[:name]       || 'No name'
age        = params[:age]        || 30
prefecture = params[:prefecture] || 'Tokyo'

一貫性

一貫性とは、似たようなコードのフォーマットを揃えること。コードの中に似たような構造が現れたら、同じようなフォーマットに統一できないか考える。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# bad
taro = User.new(name: 'Taro')
hanako = taro = User.new(
  name: 'Hanako',
  age: 24,
  prefecture: 'Tokyo',
  job: 'Sales'
)
jiro = taro = User.new(name: 'Jiro', age: 30)

# good
taro = taro = User.new(
  name: 'Taro'
)
hanako = taro = User.new(
  name: 'Hanako',
  age: 24,
  prefecture: 'Tokyo',
  job: 'Sales'
)
jiro = taro = User.new(
  name: 'Jiro',
  age: 30
)

ブロック化

ブロック化とは、同じ系統の変数などをまとめてグループ化すること。似たような構造が繰り返し現れたら、並び替えた上で空行で視覚的にブロック化できないか考える。たった1行の空行で可読性が高まることがある。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# bad
user_name       = params[:user_name]
company_name    = params[:company_name]
user_tel        = params[:user_tel]
company_tel     = params[:company_tel]
email           = params[:email]
age             = params[:age]
birthday        = params[:birthday]
company_address = params[:company_address]

# good
user_name       = params[:user_name]
email           = params[:email]
age             = params[:age]
birthday        = params[:birthday]
user_tel        = params[:user_tel]

company_name    = params[:company_name]
company_tel     = params[:company_tel]
company_address = params[:company_address]

useの情報とcompanyの情報が整理されていないコードより、整理されている方が読み手にとってわかりやすい。整理してまとめられるものは、積極的にまとめる。

コメント

コメントはプログラムの動作を説明するためだけではなく、他の開発者がそのコードを読む時の理解を助けるためにある。コメントがないと、コードを理解するまでに多くの時間がかかってしまう。逆に不要なコメントが多すぎると、コード量が増えプログラムの全体的な把握を阻害してしまう。バランスの良いコメントができるように心がける。

コメントのポイント

  • 「Why」をコメントする
  • 他の開発者へのメモを残す
  • 実際の例を記入する

「Why」をコメントする

なぜこのコードを書いたのか、背景や理由を説明をつける。

1
2
3
# UserとCompanyからメールアドレスで検索する
# 両方から見つかる場合はUserを優先する
account = User.find_by(email: params[:email]) || Company.find_by(email: params[:email])

他の開発者へのメモを残す

多くのプログラミング言語では、コード中に決まったフォーマットでコメントを書くことで、タスクやメモを残すことができる。

例えば、 TODO(いつかやる) や FIXME(直せる人直して)を使って以下のようなコメントを残すことで、他の開発者へ伝えることができる。

1
2
3
# TODO: 他のファイルに同じ記述があるので共通化する

# FIXME: メソッドが非推奨になったため使わないように変更する

実際の例を記入する

文章だけのコメントでは意図が伝わらない場合には、実際の挙動の例を記述する。

1
2
3
4
5
6
7
# 郵便番号を3桁と4桁に分割する
# 例)
#   '123-1234' => ['123', '1234']
#   '123'      => ['123', nil]
def split_postal_code(postal_code)
  省略
end

まとめ

他の人が理解しやすいコードの書き方、自然に読みやすいコードを書けるように、下記の3つのことを気をつける。

  • 実際に学んだことを実行する
  • 当たり前に「読みやすいコード」を書く
  • コードで伝える