1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

リーダブルコード 要約

Last updated at Posted at 2020-12-30

なにこれ

筆者がリーダブルコードを読んで、アウトプット用として書いてます!
独自で具体例を考えて記述している部分もあります。
解釈の間違いなど、不明点 or 懸念点がありましたら、コメントでご指摘いだだけますと幸いです!

1 理解しやすいコード

コードは理解しやすくしろ!

1−1「優れた」コードってなに?

簡潔か安心か。
三項演算子を頑張って使うか、if-else文で書くか。

1−2 読みやすさの基本定理

他の人が最短時間で理解できるコード
他の人とは、1年後の未来の自分かも知れない

短ければ良いけど、それより理解できるコードがどうか自問自答する

自分しか使わないコードでも、他の人が再利用して使うかも。
もしくは別のプロジェクトで使うかも。
だから怠慢にならず、しっかりと考えて書く。

2 名前の情報を詰め込む

別の言い方をすると、変数名に情報を詰め込む

2−1 明確な単語を選ぶ

例えばgetはあまり明確な単語じゃない
もっとカラフルな単語を探す

気取った言い回しよりも、明確で正確な方がいい。

2−2 tmpやretvalなど汎用的な名前を避ける

tmp, retVal, foo
これらは数行だけで終わる短命な変数だけ使う

2−3 抽象的な名前よりも具体的な名前を使う

明確な単語を使う。抽象的な単語は使うな

2−4 名前に情報を追加する

2−5 名前の長さを決める

スコープが小さければ、短い変数名でもおk

広く使う変数は、長くて良いから明確な名前にしてね。

省略形

例)
BackEndManager ➡️ BEManager

メリット:短い
デメリット:暗号みたいになる

新しくチームに来た人が理解できるならおk。ダメなら変えて。
書いた自分でも、後で見たら暗号に見えるかもだから。

不要な単語は消す

ConvertToStringをToStringにしても大きな問題はない!

2−6 名前のフォーマットで情報を伝える

3 誤解されない名前

「他の意味と間違えられることはないか?」と何度も自問自答する

3−1 filter

filterresultsをセットで使う時は、何の結果なのか書いて!
選択するならselect、除外するならexclude

3-2 clip

次の2つの見分けがつかない
・最後からlength文字を切り取る(remove)
・最大length文字まで切り詰め(truncate)

lengthも良くない
バイト数、文字数、単語数なのか分からん。
文字数なら、max_lengthよりmax_charsの方が適切

3−3 minとmax

限界値を含める時に使う

3−4 firstとlast

範囲を指定

3−6 ブール値の名前

常にtrueかfalseの意味を明確にしろ

read_password
上記の変数名はアンチパターン。次の2つの意味がある。
・これからパスワードを読む
・既にパスワードを読んでる

is, has, can, shouldなどをつけろ。

変数名は否定形より肯定形にした方が読みやすい。
dissable_ssl = false;よりuse_ssl = trueなど
NotFind = falseよりIsFind = trueみたいな?右側はぱっと思いつきだけど。

3−7 ユーザーの期待に合わせる

先入観を持ってコードを読んだ時に読み間違える場合がある。
その場合は名前を変えるのも有り

get

getは簡単な処理と思い込まれてる。
それがもし重い処理だったら困る。
get_avg_all_productよりcompute_avg_all_productの方が良い

3−8 複数の名前を検討する

4 美しさ

コードは雑誌のように美しくあるべき。

そのために次の3つが必要
・読み手が慣れているパターンと、一貫性のあるレイアウトを使う
・似ているコードは似ているように見せる
・関連するコードをまとめてブロックにする

4−2 一貫性のある改行を心がける

・似たメソッドがある中で、1つだけ改行がずれてると違和感
・同じようなメソッドなら共通化できるか考える

wifi = Calc.new(
  hoge: 'fuga'
)
mobile_calc_now =
  Calc.new(
  hoge: 'fuga'
)

wifi = Calc.new(
  hoge: 'fuga'
)
mobile_calc_now = Calc.new(
  hoge: 'fuga'
)

4−3 メソッドを使った整列

見た目をよくすれば、コードの構造も改善できることがある!

4−4 縦の線を真っ直ぐにする

checkUser('yamada', 'tarou', 21)
checkUser('tani', 'kyoukomaru', 100)

checkUser('yamada', 'tarou'     , 21)
checkUser('tani',   'kyoukomaru', 100)

Q.手間をかけても整列すべきか?
A.整列すべき!
いちいち整列するのは面倒と思うけど、ほとんどのケースで対して負担にならない。

4−5 一貫性のある並び

並べる順番には気をつける。上から順に行くように

details = request.POST.get('details')
url     = request.POST.get('url)

4−6 宣言をブロックにまとめる

4−7コードを段落に分割する

def bad_send_email
  friend = User.find(1)
  puts "comment"
  friend.save
end

def good_send_email
  friend = User.find(1)
  friend.save

  puts "comment"
end

個人的な好みと統一性

次の2つはどちらも差はないけど、このコードが混在すると理解に苦しむ。
なので、チームとして統一する書き方を決める

class Logger {
  //
}

class Loger
{
  //
}

一貫性のあるスタイルは、「正しい」スタイルよりも大切

5 コメントすべきことを知る

コメントの目的は、書き手の意図を読み手に知らせること

5−1 コメントするべきでは「ない」こと

コードからすぐに分かることをコメントに書かない

コメントのためのコメントをするな

コードから分かることはコメントするな

ひどい名前はコメントを付けずに名前を変えろ

5−2 自分の考えを記録する

映画のコメンタリーのイメージ

なぜこのコードはこのやり方なのか?

・ここの処理は訳ありだ
・この変数名は100%の意味ではない
・このクラスは汚い。別のsubclassを作って分割した方が良い

コードの欠陥にコメントを付ける

TODO(大きな修正)
FIXME(既知の不具合がある)
HACK(あまり綺麗じゃない解決策)
XXX(危険!大きな問題あり)

 これからどうしたいかをコメントに書く

定数にコメントを付ける

定数にまつわる背景

5−3 読み手に立場になって考える

他の人とは、あなたほどプロジェクトを熟知していない人のこと

質問されそうなことを想像する

コードを読んだ人が「え?」と思うところを予想してコメントする

ハマりそうな罠を告知する

平均的な読み手が驚くような動作は文章化しておく

次の2つを考える。
・このコードを見て、びっくりすることは何だ?
・どんなふうに間違えて使う可能性がある?

全体像のコメント

・ソースコードを読んでも得られない情報のコメントなど
・短い文章でおk.何もないよりマシ

要約コメント

関数のブロックの処理ごとに書いてもおk。概要をまとめるイメージ
ただ重要なのは、優れたコメントより優れたコード

5−4 ライダーズブロックを乗り越える

とりあえず考えていることをコメントに書き出す

コメントを書く作業は3つに分割できる
1.頭の中にあるコメントをとにかく書き出す
2.コメントを読んで、改善が必要なものを見つける
3.改善する

やばい。これは面倒になる
⬇️
やばい➡️この処理は気をつけて。
これは➡️入力時にやると
面倒になる➡️実装の難易度が上がる。

6章 コメントは正確で簡潔に

コメントは領域に対する情報の比率を高くしろ

6−1 コメントを完結にしておく

3行で説明するより1行で簡潔に

6−2 あいまいな代名詞を避ける

「それ」や「これ」みたいな

6−3 歯切れの悪い文章を磨く

これまでクロールしたURLかどうかによって優先度を変える
⬇️
これまでにクロールしていないURLの優先度を高くする

6−4 関数の動作を正確に記述する

このファイルに含まれる行数を返す
⬇️
このファイルに含まれる改行文字\nを数える

6−5 入出力のコーナーケースに実例を使う

変数srcの戦闘や行末にあるcharsを除去する
⬇️
strip(‘abba/a/ba’, ‘ab)は`’/a/‘を返す
⬇️
strip(‘ab’, ‘a’)は’b’を返す

長い説明コメントより、実例を交えて入出力の結果を書くと分かりやすい

6−6 コードの意図を書く

コメントは、コードを書く時に考えていることを読み手に伝えるためのもの
コードの意図は、詳細レベルより高レベルで記述する。
抽象度を上げるかは時と場による

listを逆順に居てレートする
⬇️
listを値段の高い順に表示する

6−7 「名前付き引数」コメント

Connect(/* timeout_ms */ 10)

ややこしい引数だったりに使うと良いかも

6-8 情報密度の高い言葉を使う

7章 制御フローを読みやすくする

条件やループなどの制御フローは、できるだけ「自然」にする。コードの読み手が立ち止まったり読み返したりしないように書く

7−1 条件式の引数の並び順

if (length >= 10)

左側 右側
調査の対象。変化する。 比較の対象。あまり変化しない。

7-2 if-elseブロックの並び順

・条件は否定形if(!debug)よりif(debug)を使え
・単純な条件を先に書く。if-elseが同じ画面に表示されるので見やすい
・関心を引く条件や目立つ条件を先に書く。

「ピンクの象を考えないでください」現象と同じ。1回頭に入ると考えちゃうから、複雑な条件を書くな!

if(!url('pink_zoo')) {
  p 'ng'
} else {
  p 'ok'
}

if(url('pink_zoo')) {
  p 'ok'
} else {
  p 'ng'
}

7−3 三項演算子

賛否あり。
行数を短くするよりも、他の人が理解するのに掛かる時間を短くしろ
頑張って1行で書かなくても良い。分かりやすさのほうが重要

// good
time = (hour >= 12) ? 'pm' : 'am'
// bad
time = if(hour >= 12) {
  'pm'
} else {
  'am'
}

// bad
return exp >= 0 ? man * (1 << exp) : man / (1 << -exp)

# good
return if(exp >= 0) {
  man * (1 << exp)
} else {
  man / (1 << -exp)
}

基本的にはif/elseを使う。三項演算子で簡潔に書けそうならおk

7−4 do/whileループを避ける

コードを2回読む必要があったり、下から上に移動して読む必要があるので使うな

7−5 関数から早く返す

7−6 悪名高きgoto

スパゲティコードになる。

7−7 ネストを浅くする

修正する時はコードを新鮮な目で見る。一歩下がって全体を見る
ネストを変える時は特に!

// 初期
if(result1 == SUCCESS) {
  done()
} else {
  return
}

// 変更後(ややこしい)
if(result1 == SUCCESS) {
  if(result2 != SUCCESS) {
    return
  }
  done()
} else {
  return
}
done()

早めに返してネストを削除する

if(result1 != SUCCESS) {
  return
}
if(result2 != SUCCESS) {
  return
}
done()

ループ内部のネストを削除

ガード節を使ったり、continueをワンライナーで書いたりする。
返す条件を先に書くこと!
直線的に分かるコードを書こう

8 巨大な式を分割する

巨大な式は、飲み込みやすい大きさに分割する
人間は同時に3〜4のことしか考えられない。

8−1 説明変数

・(巨大な)式を分割できる
・簡潔な名前で式を説明することで、コードを文書化できる
・コードの主要な概念を読み手が認識しやすくなる

if params[:user_name] == 'root'

user_name = params[:user_name]
if user_name == 'root'

8−2 要約変数

if (request.uesr.ud == document.owner_id) {
  // ユーザーはこの文章を編集できる
}
if (request.user_id != document.owner_id) {
  // 読み取り専用
}

user_owns_document = (request.uesr.ud == document.owner_id)
if (user_owns_document) {
  // ユーザーはこの文章を編集できる
}
if (!user_owns_document) {
  // 読み取り専用
}

8−3 ド・モルガンの法則を使う

if(!(file_exists && !is_protected)) Error

if(!file_exists || is_protected) Error

8−4 短絡評価の悪用

「頭がいい」コードには気をつける。後で他の人(未来の自分含む)が読む時にわかりにくくなる
1行で収めることが素晴らしいことじゃない!!
2行になっても最短時間で理解できる方が重要!

8−5 複雑なロジックと格闘する

return (begin >= other.begin && begin < other.end) ||
       (end > other.begin && end <= other.end)

if(other.end <= begin) return false // 一方の終点が、この始点よりも前にある。
if(other.begin >= end) return false // 一方の始点が、この終点よりも後になる
return true

8-6 巨大な式を分割する

if($("#vote_value").html() === "Up") {
  $("#thumbs_up).addClass("highlighed")
  $("#thumbs_down).removeClass("highlighed")
} else if($("#vote_value").html() === "Down") {
  $("#thumbs_up).removeClass("highlighed")
  $("#thumbs_down).addClass("highlighed")
}


let thumbs_up = $("#thumbs_up")
let thumbs_donw = $("#thumbs_down")
let vote_value = $("#vote_value")
let hi = "highlighed"

if(vote_value.html() === "Up") {
  thumbs_up.addClass(hi)
  thumbs_down.removeClass(hi)
} else if("vote_value").html() === "Down") {
  thumbs_up.removeClass(hi)
  thumbs_down.addClass(hi)
}

8−7 四季を完結にするもう1つの創造的な方法

9 変数と読みやすさ

変数を雑に使うと起きる問題
・変数が多いと、変数を追うのが難しい
・変数のスコープが大きいと、スコープを把握する時間が長くなる
・変数が頻繁に変更されると、現在の値を把握するのが難しい

9−1 変数を削除する

邪魔な変数を削除する

役に立たない一時変数

次のコードの問題
・複雑な式を分割してない
・より明確になっていない。Date.nowでも伝わる
・一度しか使ってないので、重複コードの削除になっていない

now = Date.now()
message.created_at = now

message.created_at = Date.now()

私的メモ

すごいあるあるだと思ったので、こういったコードがないか見直す習慣をつける
逆に見つけられるようになる

中間結果を削除する

タスクはできるだけ早く完了するほうが良い

let remove_index = null
for (let i = 0; i < array.length; i++) {
  if (array[i] === null) {
    remove_index = array[i]
  }
  if (remove_index !== 0) {
    array.splice(remove_index, 1)
  }
}

for (let i = 0; i < array.length; i++) {
  if (array[i] === null) {
    array.splice(i, 1)
    return
  }
}

制御フロー変数を削除する

制御フロー変数は、ほとんどの場合でうまくプログラミングすれば削除できる。

done = false // 制御フロー変数
while(!done) {
  if(処理) {
    done = true;
    continue
  }
}

while (処理) {
  if(処理) {
    break;
  }
}

面接時には変数が3つ以上あると言われてる。
コードを読む時に変数が3つ以上あったら、常に面接を受けるのと同じ心意気になってしまう。そんな思いをさせたいか?

9-2 変数のスコープを縮める

すべての変数のスコープを縮めるのは良いこと
変数のことが見えるコード行数をできるだけ減らす

・ミニグローバル変数をローカル変数に格下げする

JavaScriptのプライベート変数

submitted = false // グローバル変数

let submit_form = function(){
  if(submitted) {
    return // 2重投稿させないため
  }
  submitted = true
}


let submit_form = (function(){
  submitted = false // グローバル変数

  return function(){
    if(submitted) {
      return // 2重投稿させないため
    }
    submitted = true
  }
}()) // 即時関数でプライベート化

JavaScriptのグローバルスコープ

letなどをつけずに変数を定義すると、グローバル変数になる?今はそもそも定義できない気がする

私的メモ

だからvueだとlet or constがないとエラーを吐くのかな?

9−3 変数は一度だけ書き込む

constなどイミュータブルな変数を使えば、コードが理解しやすくなる

変数を操作する場所が増えると、現在地の判断が難しくなる
Javaの作者いわく、イミュータブル(不変)はトラブルが少なくなる傾向

9-4 最後の例

中間変数を消して、すぐに返す。
elemの代入回数が多かったので、1回にして置いやすくした。

let setFirstEmptyInput = function(new_value) {
  let found = false
  let i = 0
  let elem = document.getElementById('input' + i)
  while(elem !== null) {
    if(elem.value === ''){
      found = true
      break
    }
    i++
    elem = document.getElementById('input' + i)
  }
  if(found) elem.value = new_value
  return elem
}


let setFirstEmptyInput = function(new_value) {
  for(let i = 0; i < 4; i++) {
    elem = document.getElementById('input' + i)
    if(elem.value === null) {
      return null
    }
    if(elem.value === '') {
      elem.value = new_value
      return elem
    }
  }
}

10 無関係の下位問題を抽出する

無関係の下位問題を積極的に見つけて抽出する

1.関数やコードブロックを見て、「このコードの高レベルの目標は何か?」と自問する
2.コードは各行に「高レベルの目標に直接的に効果があるのか?」or「無関係の下位問題を解決してるか?」と自問する
3.無関係の下位問題を解決してるコードが相当量あれば、それらを抽出して別の関数にする

10−1 入門的な例

すごいざっくり言うと次の例。
メソッド内の別の処理を、他のメソッドに切り分けること。
これが下位問題

def puts_result
  a = 1
  b = 2
  result = a * b
  puts result
end


def addition(num1, num2)
  num1 * num2
end

def puts_result
  a = 1
  b = 2
  result = addition(a, b)
  puts result
end

10−2 純粋なユーティリティーコード

「この関数ないなあ」と思った時には、作れば良い!
それが他でも使いまわせる。

10-3 その他の汎用コード

思いも寄らない恩恵

関数は、小さくて独立したものになっていれば、機能追加、読みやすさの向上、エッジケースの処理などが楽にできる!
例外の処理を切り分けたりも楽。

10−4 汎用コードをたくさん作る

プロジェクトから完全に切り離されているコードは3150!
メンテしやすい・影響を与えないことでコードが短く保てる!

大事なのは、下位問題を分離して処理できるか

10−5 プロジェクトに特化した機能

下位問題を取り除くだけでも効果あり!

10−6 既存のインターフェースを簡潔にする

理想とは程遠いインターフェースに妥協することはない。
自分でラッパー関数を用意して、汚いインターフェースを覆い隠せ!

10−7 必要に応じてインターフェースを整える

多くのコードはその他のコードを支援するためだけに存在する。
例えば、関数の事前処理や事後処理。
こういうグルーコードは、プログラムの本質的なロジックと関係ないことが多い。

10−8 やりすぎ

小さな関数を作りすぎると、逆に読みにくくなる。(当たり前だけど)

11 一度に1つのことを

コードは1つずつタスクを行うようにしろ

一度に1つのタスクをするために使ってる手順

1.コードが行ってるタスクをすべて列挙する。タスクは「オブジェクトが妥当かどうか」や「ツリーのすべてのノードをイテレートする」など曖昧~~(アイマイミーマイン)~~

2.タスクをできるだけ異なる関数に分割。少なくとも異なる領域に分割する

11−1 タスクは小さくできる

一度に1つのタスクを適用する

12 コードに思いをこめる

おばあちゃんに説明して分かるぐらい。

コードをより明確にする手順
1.コードの動作を簡単な言葉で同僚にもわかるように説明する
2.その中で使っているキーワードやフレーズに注目する
3.その説明に合わせてコードを書く

12−1 ロジックを明確に説明する

12-2 ライブラリを知る

13 短いコードを書く

最も読みやすいコードは、何も書かれていないコード

なるべく新しいコードを書かずに、ライブラリのコードを使う。
ライブラリのコードは素晴らしい。緻密に練られたコードだから。
エンジニアが1日に書く出荷用のコードは、平均10行!

定期的に組み込みライブラリの一覧を見て、使えそうなライブラリがないか頭にインデックスを作れ!

新しいコードを書くと、その分テスト・文書化・保守が必要になり、プロジェクトが重たくなり、開発も難しくなる。

新しいコードを書かない3つの原則
・不必要な機能をプロダクトから削除する。過剰な機能は持たせない
・最も簡単に問題を解決できるような要求を考える
・定期的に全てのAPIを読んで、標準ライブラリに慣れる

14 テストと読みやすさ

14−1 テストを読みやすくて保守しやすいものにする

他のプログラマが安心してテストの追加や変更ができるように、テストコードを読みやすくする

テストコードが大きくて恐ろしいことの弊害
1.本物のコードの修正を恐れる
2.新しいコードを書いた時にテストを追加しなくなる

14−3 テストを読みやすくする

大切ではない詳細はユーザ(自分含む)から隠し、大切な詳細は目立つようにする

テスト失敗時には、失敗した時の値が出力されるとデバックしやすい!確かに。

コードを完全にテストする、最も単純な入力値の組み合わせを選択しろ!
(テストは最もキレイで単純な値を選べ)

一つの機能に複数のテスト

1度ですべての項目をテストするんじゃなくて、複数回に分ける。
その方がデバッグしやすく読みやすい。他の人も修正しやすい。

assertだと失敗分がそのまま表示される。独自でエラー文を作るのも良いかもね!

14−9 やりすぎ

・テストのために本物のコードの読みやすさを犠牲にしてしまう
本末転倒

・テストのガバレッジを100%にしないと気がすまない
コードの90%をテストする方が楽。
残りの10%の多くは、ユーザインタフェースやどうでもいいエラーケースがほとんど。
コストが高くないので、わりに合わない!

・テストがプロダクト開発の邪魔になる
プロジェクトの一部にすぎないテストのはずが、プロジェクト全体を支配してることがある。テストを書くのが儀式になってる。

14−10 まとめ

・テストのトップレベルはできるだけ簡潔にする。入出力のテストはコード1行で記述できるとおk
・テストが失敗したら、バグの発見や修正がしやすいようなエラーメッセージを表示する
・テストは有効で最も単純な入力値を使え!
・テスト関数は長くてもおk。他で呼び出すことはないからね!ほぼ説明文でも良い

特に新しいテストの追加や修正を簡単にすることが大切。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?