Skip to content
⚠️ This article was written in 2018. Some content may be outdated.

コードの可読性:変数命名と関数設計

書いた後に自分が読めて、チームメンバーも読めて、3ヶ月後に自分が戻ってきても読める——それが本当の可読性です。可読性を高める実践をまとめます。

命名:コード自身に語らせる

良い命名はコメントをほとんど不要にします。

変数命名の原則

ブール値:is/has/can/shouldプレフィックスを使う

javascript
// ❌
let login = true;
let admin = false;
let data = true;

// ✅
let isLoggedIn = true;
let isAdmin = false;
let hasData = true;
let canEdit = false;
let shouldRefresh = true;

配列:複数形または集合名詞を使う

javascript
// ❌
const item = [];
const list = [];

// ✅
const users = [];
const selectedIds = [];
const activeOrders = [];

関数:動詞で始めて行動を表現する

javascript
// ❌
function user(id) { ... }
function data() { ... }

// ✅
function fetchUser(id) { ... }
function getUserList() { ... }
function validateForm(form) { ... }
function handleSubmit(event) { ... }

略語を避ける(一般的なものは例外)

javascript
// ❌ 不明確な略語
const btn = document.querySelector('.btn')
const element = document.querySelector('.container')
const idx = arr.findIndex(...)
const fn = () => {}

// ✅ よく知られた略語を残す(id, url, api, max, min...)
const submitButton = document.querySelector('.submit-btn')
const containerElement = document.querySelector('.container')
const foundIndex = arr.findIndex(...)
const callback = () => {}

// 一般的に許容される略語:
// id, url, api, db, http, css, js, el(element), e(event), i(ループインデックス)

関数設計:1つの関数は1つのことをする

単一責任

javascript
// ❌ 1つの関数がやりすぎ
async function saveAndSendNotification(formData) {
  // バリデーション
  if (!formData.email) throw new Error("メールアドレスは必須です");
  if (!formData.name) throw new Error("名前は必須です");

  // 保存
  const user = await db.users.create(formData);

  // メール送信
  await emailService.sendWelcome(user.email);

  // ログ更新
  await auditLog.add({ action: "USER_CREATED", userId: user.id });

  return user;
}

// ✅ 責任を分割する
async function createUser(formData) {
  validateUserForm(formData);
  const user = await db.users.create(formData);
  return user;
}

function validateUserForm(formData) {
  if (!formData.email) throw new Error("メールアドレスは必須です");
  if (!formData.name) throw new Error("名前は必須です");
}

// 呼び出し側がフローを管理
async function handleRegistration(formData) {
  const user = await createUser(formData);
  await emailService.sendWelcome(user.email);
  await auditLog.add({ action: "USER_CREATED", userId: user.id });
  return user;
}

引数が多すぎないようにする

javascript
// ❌ 5つの引数——呼び出し時にどれが何かわからない
function createButton(text, type, size, disabled, onClick) {}
createButton("送信", "primary", "large", false, handleSubmit);

// ✅ オブジェクト引数を使う
function createButton({
  text,
  type = "default",
  size = "medium",
  disabled = false,
  onClick,
}) {}
createButton({ text: "送信", type: "primary", onClick: handleSubmit });

コメント:何をではなく、なぜを書く

javascript
// ❌ コードの内容を繰り返すコメント(無意味)
users.forEach((user) => {
  if (user.isActive) {
    processUser(user);
  }
});

// ✅ なぜか、または非直感的なロジックを説明するコメント
// 今月入会したユーザーのみを処理。過去のユーザーは別のバッチジョブで一括処理
users.filter((u) => u.joinedAt >= startOfMonth).forEach(processUser);

// 注意:整数ではなく.9を使うのはIEの精度バグのため
// issue #123参照
const safeHeight = Math.floor(height * 0.9);

マジックナンバーを避ける

javascript
// ❌ 3は何?300は何?
if (list.length > 3) { ... }
setTimeout(refresh, 300)

// ✅ 定数に名前をつける
const MAX_RECENT_ITEMS = 3
const DEBOUNCE_DELAY = 300  // ms、ユーザーの入力停止を待つ

if (list.length > MAX_RECENT_ITEMS) { ... }
setTimeout(refresh, DEBOUNCE_DELAY)

早期リターン(ガード節)

javascript
// ❌ 深いネスト
function processOrder(order) {
  if (order) {
    if (order.status === "pending") {
      if (order.items.length > 0) {
        if (order.totalAmount > 0) {
          // メインロジックはここ...
        }
      }
    }
  }
}

// ✅ 早期リターンでネストを減らす
function processOrder(order) {
  if (!order) return;
  if (order.status !== "pending") return;
  if (order.items.length === 0) return;
  if (order.totalAmount <= 0) return;

  // メインロジック。もうネストしない

MIT Licensed