architecture
A
信頼度ランク
| S | 公式ソース確認済み |
| A | 成功実績多数・失敗例少数 |
| B | 賛否両論 |
| C | 動作未確認・セキュリティリスク高 |
| Z | 個人所感 |
クリーンコードの書き方:読みやすいコードの原則
命名・関数の単一責任・早期リターン・コメントの使い方など、実務ですぐ活かせるクリーンコードの書き方を具体的なコード例で解説します。
一言結論
クリーンコードの本質は「将来の自分を含む読み手への配慮」であり、意図が伝わる命名と単一責任の徹底だけで、コードの読み書きコストは大幅に下がる。
なぜクリーンコードが重要か
コードは書く時間より読む時間のほうが圧倒的に長い。自分が書いたコードでも、1ヶ月後には「このコード何をしているんだ?」となる。
“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” — Martin Fowler
原則1: 意図が伝わる命名
// ❌ 何を表しているか不明
const d = new Date();
const x = users.filter(u => u.a > 18);
// ✅ 意図が一目でわかる
const currentDate = new Date();
const adultUsers = users.filter(user => user.age > 18);
命名のルール:
- ブール値は
is/has/can/shouldで始める(isActive,hasPermission) - 関数は動詞から始める(
getUser,sendEmail,validateInput) - 定数はSCREAMING_SNAKE_CASE(
MAX_RETRY_COUNT) - 略語を避ける(
usr→user,btn→button)
原則2: 関数は一つのことをする
// ❌ 一つの関数がユーザー取得・バリデーション・メール送信・ログを全部やる
async function processUser(userId: string) {
const user = await db.users.findById(userId);
if (!user.email.includes("@")) throw new Error("Invalid email");
await sendEmail(user.email, "Welcome!");
console.log(`User ${userId} processed`);
}
// ✅ それぞれの責務を分離
async function getUser(userId: string): Promise<User> {
const user = await db.users.findById(userId);
if (!user) throw new UserNotFoundError(userId);
return user;
}
function validateEmail(email: string): void {
if (!email.includes("@")) throw new InvalidEmailError(email);
}
async function sendWelcomeEmail(user: User): Promise<void> {
await emailService.send({ to: user.email, template: "welcome" });
}
// 呼び出し側で組み合わせる
async function onboardUser(userId: string) {
const user = await getUser(userId);
validateEmail(user.email);
await sendWelcomeEmail(user);
}
原則3: 早期リターンでネストを浅く
// ❌ ネストが深く、正常系を把握しにくい
function processOrder(order: Order | null) {
if (order !== null) {
if (order.status === "pending") {
if (order.items.length > 0) {
// 正常系の処理がずっと下
chargePayment(order);
} else {
throw new Error("Empty order");
}
} else {
throw new Error("Invalid status");
}
} else {
throw new Error("Order not found");
}
}
// ✅ ガード節で先に異常系を排除する
function processOrder(order: Order | null) {
if (!order) throw new OrderNotFoundError();
if (order.status !== "pending") throw new InvalidOrderStatusError(order.status);
if (order.items.length === 0) throw new EmptyOrderError();
// ここに来たら必ず正常系
chargePayment(order);
}
原則4: コメントはなぜ(Why)を書く
// ❌ コードと同じことをコメントしているだけ
// ユーザーの年齢が18より大きいか確認する
if (user.age > 18) { ... }
// ❌ 古いコードをコメントアウトして残す(削除してgitに任せる)
// const oldMethod = () => { ... }
// ✅ コードから読み取れない「理由」をコメントする
// RFC 5321 ではSMTPのリトライ間隔を指数バックオフにすることが推奨されている
const retryDelay = Math.pow(2, retryCount) * 1000;
// ✅ ハックや回避策には必ず背景を書く
// Safari 15.3 では Date.parse() が ISO 8601 を誤解析する (Safari Bug #234567)
// そのため手動でパースしている
const date = parseDateManually(dateString);
原則5: マジックナンバーを排除する
// ❌ 300・86400・7の意味が不明
if (responseTime > 300) {
await sleep(86400 * 7);
}
// ✅ 定数に名前をつける
const SLOW_RESPONSE_THRESHOLD_MS = 300;
const ONE_WEEK_IN_SECONDS = 86400 * 7;
if (responseTime > SLOW_RESPONSE_THRESHOLD_MS) {
await sleep(ONE_WEEK_IN_SECONDS * 1000);
}
原則6: エラーは無視しない
// ❌ エラーを握り潰す
try {
await sendEmail(user.email);
} catch {
// 何もしない
}
// ❌ console.error だけで終わる(本番ではログが埋もれる)
try {
await sendEmail(user.email);
} catch (err) {
console.error(err);
}
// ✅ エラーを適切に処理する
try {
await sendEmail(user.email);
} catch (err) {
logger.error("Failed to send welcome email", { userId: user.id, error: err });
// リトライキューに入れるか、呼び出し元に伝播する
throw new EmailDeliveryError("Welcome email failed", { cause: err });
}
原則7: DRY よりも読みやすさ
DRY(Don’t Repeat Yourself)は大切だが、無理な抽象化のほうが害になることがある。
// ❌ 無理に共通化して逆に読みにくい
function createEntity(type: "user" | "post" | "comment", data: Record<string, unknown>) {
const id = generateId(type);
const timestamp = new Date();
// typeによって全然違う処理が混在...
}
// ✅ 少し繰り返しがあっても、それぞれ独立して読める
async function createUser(data: CreateUserInput): Promise<User> { ... }
async function createPost(data: CreatePostInput): Promise<Post> { ... }
“Duplication is far cheaper than the wrong abstraction.” — Sandi Metz
まとめ
| 原則 | 要点 |
|---|---|
| 命名 | 意図が伝わる名前。略語・単一文字変数を避ける |
| 関数 | 一つのことだけする。10行以内を目安に |
| 早期リターン | ガード節で異常系を先に排除、正常系をシンプルに |
| コメント | What(何をするか)はコードに。Why(なぜ)をコメントに |
| 定数 | マジックナンバーに名前をつける |
| エラー | 必ず処理する。握り潰さない |
参考: Clean Code(Robert C. Martin) / Refactoring(Martin Fowler)