自分がプログラミングを仕事にし始めたのが10数年前なんですが、その頃から今に至るまでずっと命名には悩んでいますね。
結局はいろんなコードを読んで、書いて感覚を身につけるしかないのですが、原理原則みたいなものはあるので、少しまとめておこうと思います。
あ、ちなみに具体的なトピックはリーダブルコードから抽出しました。
1. 具体的でわかりやすい名前を付ける
避けるべき汎用的な名前
変数名に data、tmp、res といった汎用的すぎる名称を使うと、コードを読む人はその変数が何を表しているのか判断できません。
// 悪い例
const data = await fetchUserInfo(userId);
const res = calculateTotal(items);
const tmp = user.name.split(' ');
これらの変数名からは、何のデータなのか、何の結果なのか、何を一時保存しているのかが全く分かりません。
// 良い例
const userProfile = await fetchUserInfo(userId);
const orderTotal = calculateTotal(items);
const nameParts = user.name.split(' ');
具体的な名前にすることで、コードを読んだだけで変数の役割が明確になります。
命名規則の一貫性を保つ
プロジェクト全体で一貫した命名パターンを採用することで、コードの予測可能性が高まります。
よく使われる命名パターン:
-
データ取得:
get、fetch、load+ 対象-
getUserProfile(),fetchOrderHistory(),loadConfiguration()
-
-
データ保存:
save、store、persist+ 対象-
saveUserPreferences(),storeCache(),persistSession()
-
-
データ更新:
update、modify、change+ 対象-
updateUserEmail(),modifySettings(),changePassword()
-
-
データ削除:
delete、remove、clear+ 対象-
deleteAccount(),removeItem(),clearCache()
-
-
真偽値:
is、has、can、should+ 状態-
isAuthenticated,hasPermission,canEdit,shouldRetry
-
// 一貫性のある命名例
class UserRepository {
async getUser(id) { /* ... */ }
async getUserList(filters) { /* ... */ }
async saveUser(userData) { /* ... */ }
async updateUser(id, updates) { /* ... */ }
async deleteUser(id) { /* ... */ }
}
2. 品質を高める命名の原則
コードの品質を向上させるためには、要素の種類に応じた適切な命名パターンを使用することが重要です。
メソッド・関数は動詞で始める
メソッドや関数は「アクション」を表すため、動詞で始めることで何をするのかが明確になります。
// 悪い例:名詞で始まっている
class UserAccount {
validation(data) { /* ... */ }
creation(userData) { /* ... */ }
removal(userId) { /* ... */ }
}
// 良い例:動詞で始まる
class UserAccount {
validateData(data) { /* ... */ }
createUser(userData) { /* ... */ }
removeUser(userId) { /* ... */ }
}
一般的な動詞とその用途:
// データ操作
const user = getUser(id); // 取得
const users = findUsers(criteria); // 検索
const result = createUser(data); // 作成
const updated = updateUser(id, data); // 更新
const deleted = deleteUser(id); // 削除
// 計算・変換
const total = calculateTotal(items); // 計算
const json = convertToJson(object); // 変換
const formatted = formatDate(date); // 整形
const validated = validateEmail(email); // 検証
// 状態変更
const initialized = initializeApp(); // 初期化
const rendered = renderComponent(); // 描画
const enabled = enableFeature(); // 有効化
const disabled = disableFeature(); // 無効化
// 非同期操作
const data = await fetchUserData(id); // 取得(非同期)
const saved = await saveToDatabase(data); // 保存(非同期)
const loaded = await loadResources(); // 読み込み(非同期)
クラスは名詞・名詞句で命名する
クラスは「モノ」や「概念」を表すため、名詞または名詞句で命名します。
// 悪い例:動詞が含まれている
class ManageUsers { /* ... */ }
class HandlePayment { /* ... */ }
class ProcessOrder { /* ... */ }
// 良い例:名詞・名詞句
class UserManager { /* ... */ }
class PaymentHandler { /* ... */ }
class OrderProcessor { /* ... */ }
クラス命名のパターン:
// エンティティ(実世界のモノ)
class User { /* ... */ }
class Product { /* ... */ }
class Order { /* ... */ }
class Invoice { /* ... */ }
// サービス・マネージャー(機能の集合)
class AuthenticationService { /* ... */ }
class DatabaseManager { /* ... */ }
class CacheHandler { /* ... */ }
class EmailSender { /* ... */ }
// コントローラー(制御を行うもの)
class UserController { /* ... */ }
class PaymentController { /* ... */ }
class OrderController { /* ... */ }
// ビルダー・ファクトリー(生成を行うもの)
class UserBuilder { /* ... */ }
class QueryBuilder { /* ... */ }
class ConnectionFactory { /* ... */ }
// リポジトリ(データアクセス)
class UserRepository { /* ... */ }
class ProductRepository { /* ... */ }
class OrderRepository { /* ... */ }
// ユーティリティ(補助機能)
class DateUtils { /* ... */ }
class StringHelper { /* ... */ }
class MathUtils { /* ... */ }
変数・定数は名詞で命名する
変数や定数は「データ」を表すため、名詞で命名します。
// 悪い例:動詞が含まれている
const getResult = await fetchData();
const doProcess = true;
const handleClick = (e) => { /* ... */ };
// 良い例:名詞
const result = await fetchData();
const isProcessing = true;
const clickHandler = (e) => { /* ... */ };
変数命名のパターン:
// 単純な値
const userName = 'John Doe';
const itemCount = 42;
const totalPrice = 1500;
// コレクション(複数形を使う)
const users = [];
const products = [];
const orderItems = [];
// 真偽値(疑問文の形)
const isAuthenticated = true;
const hasPermission = false;
const canEdit = true;
const shouldRetry = false;
// オブジェクト
const userProfile = { name: 'John', age: 30 };
const apiConfig = { baseUrl: '...', timeout: 5000 };
const formData = new FormData();
// 関数参照(動詞 + 'er'または'Handler')
const validator = (data) => { /* ... */ };
const formatter = (text) => { /* ... */ };
const clickHandler = (event) => { /* ... */ };
const errorHandler = (error) => { /* ... */ };
インターフェース・型は名詞で命名する
TypeScriptなどでインターフェースや型を定義する際も、名詞を使用します。
// インターフェース
interface User {
id: string;
name: string;
email: string;
}
interface PaymentMethod {
type: string;
cardNumber: string;
}
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
// 型エイリアス
type UserId = string;
type ProductId = number;
type OrderStatus = 'pending' | 'completed' | 'cancelled';
// ジェネリック型(通常は大文字1文字)
interface Repository<T> {
findById(id: string): Promise<T>;
save(item: T): Promise<void>;
}
真偽値は疑問文の形で命名する
真偽値の変数や関数は、Yes/Noで答えられる疑問文の形にすると、条件文が読みやすくなります。
// 悪い例:疑問文でない
const login = user.checkLogin();
const valid = validateForm();
const permission = user.checkPermission('edit');
if (login && valid && permission) {
// 読みにくい
}
// 良い例:疑問文の形
const isLoggedIn = user.checkLogin();
const isValid = validateForm();
const hasEditPermission = user.checkPermission('edit');
if (isLoggedIn && isValid && hasEditPermission) {
// 読みやすい
}
真偽値命名のプレフィックス:
// is: 状態を表す
const isActive = true;
const isLoading = false;
const isVisible = true;
const isEmpty = list.length === 0;
// has: 所有を表す
const hasChildren = element.children.length > 0;
const hasError = errors.length > 0;
const hasPermission = checkPermission(user);
// can: 可能性を表す
const canEdit = user.role === 'admin';
const canDelete = user.id === owner.id;
const canSubmit = form.isValid();
// should: 推奨を表す
const shouldUpdate = version < latestVersion;
const shouldRetry = attemptCount < maxAttempts;
const shouldNotify = preferences.notifications;
// will: 未来の動作を表す
const willExpire = expiryDate < tomorrow;
const willOverflow = newSize > maxSize;
3. 名前の規則はスコープに依存する
変数のスコープ(影響範囲)によって、適切な命名の詳細度は変わります。
スコープが小さい場合
ループカウンタや数行で完結する一時変数には、短い名前を使っても問題ありません。
// 許容される短い名前の例
for (let i = 0; i < items.length; i++) {
const item = items[i];
processItem(item);
}
// 数行で完結する一時変数
const tmp = rawData.trim().toLowerCase();
const normalized = tmp.replace(/\s+/g, '-');
この i や tmp は数行のスコープ内でのみ使用され、すぐに参照が終わるため、短い名前でも可読性を損ないません。
スコープが大きい場合
クラスのプロパティ、グローバル変数、モジュールレベルの定数など、スコープが広い変数には具体的で説明的な名前が必要です。
// 悪い例:スコープが大きいのに汎用的な名前
class DataManager {
constructor() {
this.data = {}; // 何のデータ?
this.temp = []; // 何の一時データ?
this.flag = false; // 何のフラグ?
}
}
// 良い例:スコープに応じた具体的な名前
class UserSessionManager {
constructor() {
this.activeUserSessions = {};
this.pendingAuthRequests = [];
this.isMaintenanceMode = false;
}
}
スコープと命名の関係性
| スコープの範囲 | 推奨される名前の長さ | 例 |
|---|---|---|
| ループ内(1-3行) | 1-2文字 |
i, j, k, x, y
|
| 関数内(数行-十数行) | 1-2単語 |
user, total, result
|
| クラスプロパティ | 2-3単語 |
userProfile, orderTotal
|
| グローバル/定数 | 3-4単語以上 |
MAX_RETRY_ATTEMPTS, DEFAULT_API_TIMEOUT
|
4. 長くなりすぎることを避ける
具体的な名前を心がけると、どうしても変数名が長くなりがちです。しかし、長すぎる名前は逆に可読性を損ないます。
適切な短縮形を使う
プロジェクト内で合意された略語を使用することで、可読性を保ちながら名前を短縮できます。
// 一般的に受け入れられている短縮形
const config = loadConfiguration(); // configuration → config
const ctx = canvas.getContext('2d'); // context → ctx
const params = parseUrlParameters(); // parameters → params
const repo = new UserRepository(); // repository → repo
const auth = new AuthService(); // authentication → auth
const img = loadImage('photo.jpg'); // image → img
const btn = document.querySelector('.submit'); // button → btn
const msg = formatErrorMessage(error); // message → msg
短縮形を使う際の注意点:
- チーム内で共通認識のある略語のみを使用する
- プロジェクトの命名規則ドキュメントに記載する
- 母音を削除する方式(例:
message→msg)は一般的に理解しやすい - 独自の略語を作りすぎない
不要な単語を避ける
冗長な単語や情報を削除することで、名前を簡潔にできます。
// 悪い例:冗長な命名
const theUserList = getTheUserList();
const doProcessData = (data) => { /* ... */ };
const convertToString = (value) => String(value);
const managerOfUsers = new UserManager();
// 良い例:不要な単語を削除
const userList = getUserList();
const processData = (data) => { /* ... */ };
const toString = (value) => String(value);
const userManager = new UserManager();
削除できる不要な単語:
- 冠詞:
the,a,an - 汎用的な動詞:
do,handle,manage(文脈で明らかな場合) - 型を示す接尾語:
Array,Object,String(型システムがある場合) - 冗長な前置詞:
of,for(名詞の連結で十分な場合)
// リファクタリング例
// Before
const listOfActiveUsers = getListOfActiveUsers();
const convertDataToJsonString = (data) => JSON.stringify(data);
// After
const activeUsers = getActiveUsers();
const toJson = (data) => JSON.stringify(data);
フォーマットで情報を伝える
命名規則やフォーマットを使って、変数の種類や役割を視覚的に区別できます。
// ケーススタイルによる区別
const userName = 'John'; // camelCase: 通常の変数
const UserService = class {}; // PascalCase: クラス
const MAX_ATTEMPTS = 3; // UPPER_SNAKE_CASE: 定数
const is_valid = true; // snake_case: (言語による)
// プレフィックスによる区別
class Component {
constructor() {
this._privateField = null; // アンダースコア: プライベート
this.publicField = null; // プレフィックスなし: パブリック
this.$element = null; // $記号: jQuery要素(慣習)
}
}
// タイプ別の接頭語
const arrUsers = []; // 配列
const objConfig = {}; // オブジェクト
const fnCallback = () => {}; // 関数
const bIsActive = true; // 真偽値
const nCount = 0; // 数値
実践的なフォーマット規則の例:
// TypeScriptでの実践例
class UserManager {
// プライベートプロパティ
private _cache: Map<string, User>;
private _isInitialized: boolean;
// パブリックプロパティ
public readonly maxUsers: number;
// メソッド
public async getUser(userId: string): Promise<User> { /* ... */ }
private _validateUserId(userId: string): boolean { /* ... */ }
}
// 定数
export const DEFAULT_TIMEOUT_MS = 5000;
export const API_BASE_URL = 'https://api.example.com';
// 型定義
export interface UserProfile { /* ... */ }
export type UserId = string;
まとめ
基本原則
-
具体的な名前:
dataやtmpではなく、何のデータか明確にする - 一貫性: プロジェクト全体で統一された命名パターンを使う
- スコープに応じた詳細度: 影響範囲が広いほど具体的な名前にする
- 適度な長さ: 具体的でありながら、冗長にならないバランスを取る
品質向上のための原則
- 品詞の使い分け: メソッドは動詞、クラスは名詞、変数は名詞
-
真偽値の命名:
is,has,can,shouldを使った疑問文の形 - 動作の一致: 名前から期待される動作と実際の動作を一致させる
- チームでの合意: 命名規則をドキュメント化し、チーム全体で共有する