KDE BLOG

バイブス

名前付け大全を読んだメモ

WEB+DB PRESS VOL.110 の特集「設計も、実装も、ここから始まる!名前付け大全」を読んだメモです。

第1章 「良い名前」とは何か

なぜ名前付けが重要なのか

良いコード:「理解しやすく、変更に強く、読みやすい」コードを書くため。

  • プログラマーはテーブルやクラス、メソッド、変数名など何かしら「名前付け」を行っていて、前任者などが書いたコードの名前を読むという行為も行っており、プログラマーの仕事と名前は密接な関係がある。
  • オープンソースの世界では「良い機能だが、良い名前が思いつかないので追加しない」ということも起きている。
  • 名前の良し悪しがプログラミング、システム開発にとって重要な事柄

悪い名前の問題

理解が困難
  • 直感に反していたり、誤った名前は何が起こるのか理解するのが難しい
  • 名前から理解できないから実装を読んだ、という経験が多いのでは?
  • 読んでいて引っかかるポイントとなり、脳に負担をかける
  • 時間が経てば忘れてしまうので、また解読が必要になってしまう
勘違いが起こる
  • 名前と挙動があっていなくて思わぬ不具合が出る
    • saveRecord というメソッドが既存のレコードを一部削除してしまう、というのは明らかに名前に反した挙動
変更が難しい
  • 上記のような名前と挙動があっていないメソッドが、システムのあらゆるところで使われていたり、テストコードがないという状況では改修も困難。
    • 新たに名前をつけようにも、すでに名前が使われているため付けられないという事態も起こる
読みにくい
  • 命名規則に合っていない、誤字脱字、スペルミスなどは読んでいてつらい、名前を認識するのにコストがかかる

なぜ名前はあるのか

  • 名前なしでもプログラムはかける
  • しかし実際は関数やメソッド、クラスなどの部品に挙動が推測できる名前をつけて、その意味を理解することによって処理内容を推測してプログラマーはプログラムを読み書きしている
  • 名前を通じて、すべてのコードを読まずともシステムが何をやっているのかがわかる

第2章 名前付けの理論

良い名前とは

  • CODE COMPLETE によると「命名において最も重要な考慮すべき点は、変数が表現するものを正確に完全に言い表していること」
  • 良い名前=「適切な名前を選び」「正しく書かれた」もの

適切な名前とは

  • 名前の意味と挙動が一致しているもの
    • 良い例) 名前:saveRecord 名前から想像される意味:「レコードを保存」 ↑ 一致 ↓ システム上の挙動 db.save(record)

※「レコードを保存して、過去のレコードを更新するジョブをキューに追加する」という複雑な挙動を持ったメソッドの場合、名前の意味と挙動を正確に一致させることが大変になる。 (つまり、なるべくシンプルな役割に対して名前を付けていく方がよさそう)

名前の意味と挙動の不一致 3つパターン

  • 名前の意味と挙動がずれている
  • 名前の意味が狭すぎる
  • 名前の意味が広すぎる

シンプルさを保つ:適切な名前の基準の一つ

  • 最もコンパクトで適切な表現を使い、余計な理解のコスト省く
  • 例)従業員
    • Bad: parson_who_is_hired_by_company
    • Good: employee

正しい書き方

  • よい名前を思いついても、書き方が悪ければ台無し
  • 英語としての正しさ
    • 英文として正しいか(英文法)
    • 綴りが合っているか(スペリング
  • 文体の統一など記法の正しさ
    • コーディング規約
      • CamelCase
      • snake_case
      • kebab-case
    • 省略表記
      • 慣用的なもの以外は避けた方が良い

第3章名前付けの実践

パターン1. 名前の意味と挙動がずれている

  • スペルミスにより違う意味になってしまう
    • 従業員を示そうと思って stuff とした
      • 本当は staff
      • stuff は 「もの」という意味
  • 思いついた言葉の意味が微妙にずれている
    • 複数あるカテゴリの中で重要なカテゴリを示すために primary_category とした
      • 本当は primal_category が正しい
      • primary は「複数あるものの中で順番が前にあるもの」という意味
        • primary_category は「前のほうにあるカテゴリ」という意味になってしまう
  • 対策:自分の語彙力を過信せずに、怪しかったら調べる

パターン2. 名前の意味と挙動がずれている

  • 名前の冗長さを嫌った結果、挙動をカバーできない
    • 「影響がないであろう些末な処理」を名前から省略するというのはよくやりがち
      • マーフィーの法則If it can happen, it will happen.(起こる可能性のあることは、いつか実際に起こる) というようにこれが原因で意図せぬ挙動が起こりバグが生まれる
      • やっていることがすべて伝わるような名前にしておけば未然に防げる
  • 完全で長い名前 vs 不完全で簡潔な名前
    • 完全で長い名前の方が良い
    • 不完全な名前にどんな価値がある?
    • Ruby on Rails 作者も長くて正確な名前を強く擁護している
      • Clarity over brevity in variable and method names – Signal v. Noise
        • (過去に書いた)コードに戻ってきて、それが何をやっているのか正確に知れた時に(長い名前は)バカっぽいという印象はすぐ消える
        • 命名を明確にすることに努力すれば、コードにコメントを書く必要もほとんどない(コメントは本来、なぜそのような実装になったかを書くべき)

パターン3. 名前の意味が広すぎる

  • 最も失敗として多いパターン。この中でもおおよそ3つのサブパターンがある
  • 安易な単語を選ぶ
    • すぐに思い浮かぶ単語でつい使ってしまうが、実際にはあまり意味がなくて何かを特定するには役立たない
  • 重要な単語を不用意に使ってしまう
    • categoryuser などシステム開発でよく使われる重要な単語を不用意に使い、間違いやすい名前になったりその後の命名の選択肢を減らしてしまう
  • 実装変更により既存の名前の意味が変わってしまう
    • 元々は良い名前だったのに、実装内容が変わることでその名前が悪い名前になってしまうパターン
    • 実装の追加や変更に伴い「名前の意味が狭すぎる」というパターンも起きるが、より多いのは「名前の意味が広すぎる」パターン

ケーススタディ1:安易な単語を選ぶ

  • 情報量の少ない単語を選んでしまう
    • info
    • data
    • つい使いがちだが何も伝えられない。ほかの単語を探すか、意味を狭める言葉を追加する。
  • イベントハンドラがどこを指しているか分からない
class Hoge extends React.Component {
  handleOnClickButton() {
    // 略
  }
  render() {
    return (
      <div>
        // 略
        <button onClick={this.handleOnClickButton.bind(this)}>削除</button>
      <div>
    )
  }
}
  • handleOnClickButton という名前ではどのボタンに対するものか分からない
  • 削除ボタン以外にもボタンがあればなおさら混乱
  • 検索すればわかるがその手間は名前で防ぐべき
  • handleOnClickButton -> handleOnClickDeleteButton とすれば混乱を防げる。
  • イベントハンドラは「どこの」イベントハンドラかわかるようにすべき。
check は真偽がどちらかよく分からない
  • 一般論として check は避けるべき
    • 真の場合が正常系なのか、偽の場合が正常系なのか分からない
    • check した結果、何が起こるのか自明ではない
  • 【ポイント】
    • check という言葉はできるだけ使用しない
    • どこで読んでも真偽のどちらが正常系かわかる名前を付ける
    • 結果が不明瞭な言葉は避ける

ケーススタディ2:重要な単語を不用意に使ってしまう

スコープなしで重要な単語を使ってしまう
  • 特にテーブル名で使う際は後々困ることが多いので要注意
  • 【ポイント】
    • 使う場合は「何を」などを加えて意味を狭める
役割が抜けている
  • 【ポイント】
    • 役割を意識した名前をつける(「ユーザー」よりも「著者」「受信者」)

ケーススタディ3:実装変更により、既存の名前の意味が変わってしまう

  • 新たな名前の追加で既存の名前の意味が広がってしまう

    • より狭い名前を追加することで、既存の名前から想像できる意味が挙動に比べて広くなりすぎる、ということがある
    • 【ポイント】
      • 新たな名前を追加するときは既存の名前の意味に影響がないか確認する
      • 既存の名前の意味が変わる場合はリネームする
  • 処理を減らしたがもとの名前のことを忘れていた

    • 元のコードを何かしらの事情で処理を削った結果、名前と挙動が一致しないケースが発生
      • 元の名前に気づきにくい場合、ちゃんとしたテストコードがあれば、コードの変更があった時点でテストが通らなくなり気が付くはず
        • その時に元の名前を変更すればよい
    • 【ポイント】
      • 実装変更で挙動が変わっても、名前に気が付きにくいこともある
      • テストコードを書いて、名前と挙動が一致していることを保証する

もうひとつの基準:シンプル

  • 名前と挙動が一致しているところまで来たら、それをよりシンプルな言葉で表現できないか探す
  • 一言で表現できる言葉がないか探す
  • 情報量のない言葉はただのノイズなので削る
  • 新たな言葉を加える前に、すでにある言葉で表現できないか考える

第4章 英語の壁の克服

  • ネイティブにとっても命名は難しい
  • コードの約7割が「名前(識別子)」
  • 英語での名前付けは4つのステップで考える

品詞

  • 動詞か名詞か考える
    • 動詞
      • [動詞] + ([目的語])の順番にする
      • 引数(目的語)との関係をはっきりさせるために to, at などの前置詞を使うのも良い
      • ルーチン(「何かをする」) = 動詞 と言い切るのは短絡的
        • 何かを返すルーチンであれば名詞でもおかしくはない
          • しかし keyKEY() かで勘違いが起きるくらいなら getKey() の方がいいかも
      • 重い処理に簡単すぎる名前をつけると頻繁に使われてしまう可能性がある
      • 重いだろうと想像できる名前にする

時制・相・法助詞

  • 時制(過去・現在・未来)と相(単純・進行・完了・過去完了)を掛け合わせた12パターンの中で、プログラムで多用するのは5つ
    • 過去:単純(updated
    • 現在:単純(update)、進行(isUpdating)、完了(hasUpdated
    • 未来:単純(willUpdate
  • 前後で比較するときは、時制よりも before / after で比較した方が分かりやすい
  • has は一般動詞と助動詞で違いが曖昧になりえる
  • 法助詞は canshouldmust でほぼカバーできる
    • can は能力、allow は許可、could は可能性を示す
    • shold + [動詞]
    • needs + [名詞]

語彙

  • 知っている単語だけを組み合わせて使う粗削りな表現を、より良く改良するために必要なのが語彙力
  • 日本語で考えずにイメージでとらえる
  • 対義語は適切なものを使う
  • イディオムも使われる
  • 必要以上に高度な表現は避ける
    • 平易な言葉で通じるのであればそちらの方が親切
  • 長期なプロジェクトであればドメイン用語に馴染んでおくと良いかも

スペリング

  • もっともやってはいけない
    • 英語圏ではそれだけでF判定、不合格となりえる
  • 頻出英語ほど間違えやすい

誤読・誤解を防ぐ配慮

  • item items のような複数形を表すために s だけで区別するのは文脈によっては分かりにくい
    • あえて別名にして差をはっきりさせた方が誤解は生じにくい
  • 省略形や同音異義語にも気を付ける
  • 品詞の曖昧さにも気を付ける
    • 動詞と名詞が同じ単語
      • どちらかに分けて使い分ける
  • 「どう勘違いされうるか」を先回りして考えると良い

第5章 さらなる効率化の手法

  • 辞書を引く
    • 精密な名前を使うときに英英辞典、類語辞典が役立つ
  • schema.orgOSSなども参考になる

読んだ感想

この特集を読んでから、これまで自分の命名がとても浅はかだったと感じました。
この知識はコードレビュー時にも役立てることができると思います。
特集の中では何回か「命名にかける労力は無駄ではない」といった意味合いの言葉が出ていました。 「名前と挙動を一致させる」ことを念頭において、より良いコードが書けるように努力していきます。