Skip to content

필사 모드: コードの名前付け:最も難しい簡単なスキル

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

はじめに — なぜ名前付けはそんなに難しいのか

フィル・カールトン(Phil Karlton)の有名な一言があります。「コンピュータサイエンスで難しいことは二つだけだ。キャッシュの無効化と、名前付けである」。初めて聞くと、気の利いた言葉遊びに思えます。名前付けがキャッシュの無効化と同じくらい難しい? 変数に単語を一つ付けるだけなのに?

ところが何年かコードを書いていると、この冗談が身にしみてきます。名前付けが難しいのはタイピングが難しいからではなく、名前を付けた瞬間に、その概念を本当に理解しているかどうかが露わになるからです。ある関数に良い名前が浮かばないなら、それはたいてい、その関数が一つのことをしていないか、役割をまだ明確に定義できていないというサインです。名前付けは思考の道具であって、飾りではありません。

この記事では、良い名前を付けるためのいくつかの原則を、実務の視点から整理します。ルールを丸暗記するためではありません。それぞれの原則がなぜ存在するのかを理解すれば、見慣れない状況でも自分で判断できるようになります。

名前は実装ではなく意図を表すべき

一つだけ原則を残せと言われたら、これです。良い名前は「何をするか」ではなく「なぜ存在するか」を語ります。 読む人が実装を覗き込まなくても、その値や関数の意味を掴めるべきです。

悪い例を見ましょう。

# d とは何か? 実装を読まないと分からない
d = 30

# リストを走査し、ある条件を満たすものだけ選んで新しいリストに詰める
list1 = []
for x in list2:
    if x[3] == 1:
        list1.append(x)

dlist1list2x[3] == 1 のどれも自分では語りません。このコードを理解するには周囲の文脈をすべて読む必要があります。では意図を表す名前に変えてみましょう。

GRACE_PERIOD_DAYS = 30

active_users = []
for user in all_users:
    if user.is_active:
        active_users.append(user)

変わったのはロジックではなく名前だけです。それなのにコードが自分自身を説明します。30 という数字が何を意味するのか、なぜこのユーザーたちを選ぶのかが、名前に込められています。「これは30日の猶予期間だ」とコメントで説明する必要がなくなりました。良い名前はコメントの代わりになります。

名前の長さはスコープに比例する

初心者にありがちな二つの極端があります。一つはすべてを abtmp に縮めること。もう一つは逆に、theListOfAllActiveUsersInTheCurrentOrganization のような文章級の名前を付けることです。どちらも良くありません。

良い規則は、名前の長さはそのスコープ(生存範囲)に比例すべきというものです。

# 短いスコープ:一行の内包表記の中だけで生きる変数は短くてよい
squares = [n * n for n in numbers]

# 広いスコープ:クラス全体で使うフィールドやモジュール全域は十分に説明的に
class PaymentProcessor:
    def __init__(self):
        self.pending_settlement_count = 0

一行の中だけに存在するループ変数 n は短くても問題ありません。目がその小さな範囲を一度に捉えられるからです。逆にクラス全体で生き続けるフィールドが n だと悲惨です。この値がどこでどう使われるかを追うには、名前が自ら文脈を背負っていなければなりません。

まとめるとこうです。ループのインデックスや短いラムダの引数は短く、関数の引数はほどほどに、広い範囲で長く生きる名前ほど説明的に。スコープが広いほど、名前が担うべき説明の重さは増します。

エンコーディングとハンガリアン記法を避ける

かつて流行したハンガリアン記法は、変数名の先頭に型情報を接頭辞として付ける方式でした。strName(文字列 name)、iCount(整数 count)、arrUsers(配列 users)のように。今ではほとんどアンチパターンと見なされます。

# ハンガリアン記法 - 避けよう
strUserName = "alice"
iRetryCount = 3
arrActiveUsers = [...]

# 型は言語とIDEが教えてくれる。名前は意味に集中しよう
user_name = "alice"
retry_count = 3
active_users = [...]

理由は二つあります。第一に、現代の言語は型システムとIDEが型を教えてくれます。名前に型を重複させるのは冗長です。第二に、そしてより危険なのは、型が変わると名前が嘘をつくようになることです。arrUsers を後で集合(set)や辞書に変えると、接頭辞 arr が誤った情報になります。名前は常に真実であるべきなのに、実装の詳細を名前にエンコードすると、その真実は簡単に壊れます。

同じ理由で、メンバ変数に m_ を付けたり、グローバルに g_ を付ける慣習も、最近はあまり使われません。本当に区別が必要なら、self.count のように言語が提供する文法で十分です。

検索可能な名前を使う

コードは書く時間より、読んで探す時間のほうがはるかに長いものです。だから 名前が検索可能か(grep しやすいか) は、思ったより重要な実務の基準です。

数値リテラル 7 をコードのあちこちに散らばせたとします。後でこの 7 が「一週間」を意味すると気づいて値を変えようとするとき、プロジェクト全体で 7 を検索すると、無関係な 7 が大量に出てきます。しかし名前を付けておけば、まさにその概念だけを見つけられます。

# 悪い:この7が何を意味するか、他にどこにあるか探しにくい
if days_since_login > 7:
    send_reminder()

# 良い:名前で検索すれば、この概念が使われた場所だけが正確に出る
INACTIVE_THRESHOLD_DAYS = 7
if days_since_login > INACTIVE_THRESHOLD_DAYS:
    send_reminder()

同じ文脈で、一文字の変数やありふれた単語は検索を妨げます。datavalueitem のような名前がコードベースに何百個もあると、特定のその値を探すのが困難です。名前は固有で具体的なほど、後の自分を助けます。

真偽値・関数・コレクションの慣習

いくつかの定着した慣習は、覚えておくとチーム全体のコードを読みやすくします。

真偽値は「はい/いいえ」で答えられる名前に。 ishascanshould のような接頭辞を付けると、条件文で自然に読めます。

# 名前だけで真偽の問いになる
is_active = True
has_permission = user.role == "admin"
can_retry = attempt < max_attempts

if is_active and has_permission:
    ...

activepermission のように名詞形にすると、if active: が「アクティブ?」なのか「アクティブにせよ」なのか曖昧です。接頭辞一つでその曖昧さが消えます。

関数名は動詞で始める。 関数は行動だからです。getUsercalculateTotalsendEmail のように。一方、値を保持する変数は名詞です。そして何を返すかが名前から伝わると良いでしょう。読む人は getUser() がユーザーを、isValid() が真偽値を返すと自然に期待します。

コレクションは複数形で。 複数を保持するリストや集合は複数形の名前を使います。

# 単数は一つ、複数は多数 - 走査するとき自然に読める
users = fetch_all_users()
for user in users:
    notify(user)

user は一人、users は多数。この単純な規則のおかげで for user in users: が文のように読めます。逆に for u in userList: のようなコードは、毎回少し立ち止まって解釈する必要があります。

リネームは一つのリファクタリングである

ここで多くの人がつまずきます。「今の名前がいまいちなのは分かるが、変えるとあちこち直す羽目になるから…」。こうして悪い名前が化石のように残ります。

しかしリネームは、現代のツールで最も安全なリファクタリングの一つです。IDEの「シンボル名の変更」機能は単なる文字列置換ではなく、言語を理解したうえで、そのシンボルが参照される場所だけを正確に変えます。同名の無関係な変数には手を触れません。

原則はこうです。より良い名前が浮かんだなら、その瞬間が変えどきです。 名前を直すコミットは、ロジックの変更と混ぜず別にするのが良いでしょう。そうすればレビュアーが「これは純粋な名前変更で、動作はそのままだ」と一目で確認できます。

  良い習慣:
  コミット1: rename calc() -> calculate_monthly_total()   (動作変化なし)
  コミット2: 実際のロジック修正                             (動作変化あり)

  悪い習慣:
  コミット1: 名前も変えロジックも変えフィールドも移動し…   (レビュー不能)

名前を直すことを恐れないでください。コードは、理解が深まるほど良い名前を得ていく、生きたドキュメントです。最初から完璧な名前を付ける必要はありません。理解が育てば、名前も一緒に育てればよいのです。

よくある落とし穴

最後に、実務でよく出会う名前関連の落とし穴を挙げます。

  • 嘘をつく名前:関数名は getUser なのに内部でユーザーを作ったり削除したりするなら、名前が嘘をついています。取得系の関数は副作用がないと期待されるので、この種の裏切りは特に危険です。
  • 一貫性のない語彙:同じ概念をある所では fetch、ある所では get、ある所では retrieve と呼ぶと、読む人が「この三つは別物か?」と迷います。一つのコードベースの中では、一つの概念に一つの単語を決めて貫きましょう。
  • 意味のない冗語UserDataUserInfoUserObjectDataInfoObject は何も足しません。多くの場合、ただの User で十分です。
  • 発音できない名前genymdhms(generation year month day hour minute second)のような名前は、同僚と話すとき声に出して呼べません。コードはチームが一緒に語る対象なので、発音可能性も大切です。

おわりに

名前付けが「最も難しい簡単なスキル」なのは、文法的には何の制約もないのに、そこに私たちの理解がそのまま露わになるからです。良い名前は実装ではなく意図を語り、スコープに比例した長さを持ち、型をエンコードせず、検索可能で、チームの慣習に従います。

なにより、名前は固定されたものではありません。より良い名前が浮かべば、リネームはいつでもできる安全なリファクタリングです。完璧な名前を最初から絞り出そうと苦労するより、「今の自分の理解ではこれが最善」という名前を付け、理解が深まるたびに磨いていくほうがずっと健全です。結局、よく付けられた名前が積み重なれば、コードは別の説明書なしに読める文章になります。

参考資料

  • Robert C. Martin『Clean Code』(第2章: Meaningful Names)
  • Phil Karlton の引用の背景: https://www.karlton.org/2017/12/naming-things-hard/
  • Tim Ottinger, "Ottinger's Rules for Variable and Class Naming"
  • Kernighan & Pike『The Practice of Programming』(明確さと名前)

현재 단락 (1/76)

フィル・カールトン(Phil Karlton)の有名な一言があります。「コンピュータサイエンスで難しいことは二つだけだ。キャッシュの無効化と、名前付けである」。初めて聞くと、気の利いた言葉遊びに思えま...

작성 글자: 0원문 글자: 5,073작성 단락: 0/76