clean-code について
コードの作成、レビュー、リファクタリングに使えるクリーンコードのリファレンススキルです。可読性と保守性を軸に、原則やコードの問題点、提出前の確認項目をまとめています。
主な機能
- 01命名と関数設計
- 02コードの問題点の特定
- 03フォーマットとエラー処理
- 04コメント作成の指針
- 05オブジェクトカリステニクスの練習
- 06提出前チェックリスト
活用シーン
- 01明確な名前と関数構成を選ぶ
- 02保守性の観点からプルリクエストを確認する
- 03レガシーコードをリファクタリングして複雑さを減らす
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marcioaltoe/ skills --skill clean-codeインストール後、Claude Code で/clean-codeを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlreferences/CODE_SMELLS.mdNAMING.mdOBJECT_CALISTHENICS.md詳細
クリーンコード
「動くコード」を「クリーンなコード」へと変えるための原則です。すべての開発者が読み、理解し、改善できるコードを目指します。
「チームの誰もが簡単に理解できるなら、そのコードはクリーンです。」 — Dave Thomas
適用する場面
次のような場面で、これらのガイドラインを参考にしてください。
- 新しいコードを書き、名前、関数シグネチャ、構造を決めるとき
- プルリクエストの可読性と保守性をレビューするとき
- レガシーコードをリファクタリングしたり、複雑さを減らしたりするとき
- コードの不吉な臭いを特定して解消するとき
- チームのコーディング標準を改善するとき
ルールのカテゴリ
| 優先度 | カテゴリ | 影響 |
|---|---|---|
| 1 | 命名 | 高 — コードのすべての行に影響します |
| 2 | 関数 | 高 — 抽象化の中核となる単位です |
| 3 | コードの不吉な臭い | 高 — 早期発見により劣化を防げます |
| 4 | フォーマット | 中 — 一目で読みやすくなります |
| 5 | エラー処理 | 中 — 堅牢性と明確さにつながります |
| 6 | コメント | 中 — ほとんどは避けられます |
| 7 | オブジェクト・キャリステニクス | 目標 — より良い OO 設計のための演習です |
1. 命名
読みやすさに最も大きな影響を与えるのは、適切な名前を付けることです。
優先順位:
- 一貫性 — 同じ概念には、どこでも同じ名前を使います
- 意図を明確に示す — どのように動くかではなく、何をするかを名前で表します
- 具体性 —
data、info、manager、handler、utilsなどの曖昧な名前は避けます - 検索しやすさ — grep で見つけられるだけの固有性を持たせます
- 簡潔さ — 短くしつつ、暗号のような名前は避けます
// Bad
const d = new Date();
const arr = users.filter((u) => u.a);
function process(data: any) {}
// Good
const createdAt = new Date();
const activeUsers = users.filter((user) => user.isActive);
function validatePayment(payment: Payment) {}
慣例:
- クラス/型:名詞を使います(
Customer、OrderRepository)。Manager、Data、Infoは避けます。 - メソッド/関数:動詞を使います(
createOrder、validateEmail、isEligible) - 真偽値:疑問形にします(
isActive、hasPermission、canWithdraw) - コレクション:複数形の名詞を使います(
users、orderItems)
詳しいガイドラインは references/NAMING.md を参照してください。
2. 関数
// Bad — does too many things, unclear name
function handle(order: Order, sendEmail: boolean, log: boolean) {
// validate, calculate, save, email, log — all in one
}
// Good — small, single-purpose, descriptive
function validateOrder(order: Order): ValidationResult { ... }
function calculateTotal(items: OrderItem[]): Money { ... }
function saveOrder(order: Order): Promise<void> { ... }
ルール:
- 小さくする — 20 行未満を目指します
- 一つのことだけを行う — 説明に「そして」を使うなら、分割します
- 抽象化レベルを一つにする — ビジネスロジックと低レベルの詳細を混在させません
- 引数を少なくする — 0~2 個が理想です。3 個以上ならパラメーターオブジェクトを使います
- 副作用をなくす — または、
saveではなくsaveAndNotifyのように、名前で明示します - コマンドとクエリを分離する — 関数は何かを実行するか、何かを返すかのどちらか一方にします
3. コードの不吉な臭い
リファクタリングが必要かもしれないコードの兆候です。バグではありませんが、設計上の摩擦を示しています。
| 不吉な臭い | 症状 | 簡単な修正 |
|---|---|---|
| 長いメソッド | 20 行を超え、複数の関心事を扱っている | メソッドを抽出する |
| 大きなクラス | 多くの責務を持っている | クラスを抽出する(SRP) |
| 長い引数リスト | 引数が 3 個を超えている | パラメーターオブジェクトを導入する |
| プリミティブ型への執着 | ドメイン概念に文字列や数値を使っている | 値オブジェクトでラップする |
| 機能の羨望 | メソッドが自身のデータよりも他のクラスのデータを多く使っている | メソッドを移動する |
| データの塊 | 同じフィールドの組み合わせが一緒に現れる | クラスを抽出する |
| switch 文 | コードベース全体で型チェックの switch/if-else を使っている | ポリモーフィズムに置き換える |
| 発散する変更 | 一つのクラスがさまざまな理由で変更される | 責務ごとに分割する |
| ショットガン手術 | 一つの変更で多くのファイルに手を加える | 関連するコードをまとめる |
| 投機的な汎用性 | 「念のため」の抽象化 | 削除する(YAGNI) |
| デッドコード | 到達不能または未使用のコード | 削除する |
| メッセージチェーン | a.getB().getC().doSomething() | 委譲先を隠す(デメテルの法則) |
詳しい例とリファクタリングの戦略は references/CODE_SMELLS.md を参照してください。
4. フォーマット
新聞の比喩 — コードは新聞記事のように上から下へ読み進められるようにします。最初に概要を示し、その下に詳細を記述します。
class OrderProcessor {
// Public API first — the "headline"
process(order: Order): ProcessResult {
this.validate(order);
const total = this.calculateTotal(order);
return this.save(order, total);
}
// Supporting methods below, in order of appearance
private validate(order: Order) { ... }
private calculateTotal(order: Order): Money { ... }
private save(order: Order, total: Money): ProcessResult { ... }
}
ルール:
- 関連するコードは近くにまとめます(縦方向の密度)
- 概念の区切りには空行を入れます(縦方向の開放性)
- 変数は使用箇所の近くで宣言します
- 呼び出し元を呼び出し先より上に置きます(段階的詳細化のルール)
- インデントを一貫させます — 妥協は認められません
5. エラー処理
- エラーコードより例外 — 正常系の処理をすっきり保てます
nullを返さない —undefined、Result型、または例外を使いますnullを渡さない — あらゆる場所で防御的なチェックが必要になります- 迅速に失敗させる — 境界で検証し、内部の処理を信頼します
- 具体的な例外 — 汎用的な
ErrorよりInsufficientFundsErrorを使います
// Bad — null checks cascade through codebase
function getUser(id: string): User | null {
return db.find(id);
}
const user = getUser(id);
if (user === null) { ... } // Every caller must check
// Good — throw at boundary, trust within domain
function getUser(id: string): User {
const user = db.find(id);
if (!user) throw new UserNotFoundError(id);
return user;
}
6. コメント
「悪いコードにコメントを付けるのではなく、書き直しましょう。」
多くのコメントは、コードで意図を表現できていないことを補っています。コメントよりも、 自己文書化されたコードを優先しましょう。
良いコメント:
- 理由(ビジネス上の理由、すぐには分からない判断)
- 警告(「Xのため処理が遅い」、「ここでは順序が重要」)
- 背景情報を添えたTODO(課題へのリンク)
- 法的事項/ライセンスのヘッダー
- 公開APIのドキュメント(ライブラリ向けのJSDoc)
悪いコメント:
- コードの動作を言い換えたもの(
// increment counter) - コメントアウトされたコード(そのためにgitがあります)
- 作業記録/変更履歴のコメント
- 冗長なもの(
// constructor、// getters) - 義務的な定型文
// Bad — restates the obvious
// Check if user is active
if (user.isActive) { ... }
// Good — explains a non-obvious business rule
// Users who haven't verified email within 30 days are auto-deactivated
// per compliance requirement GDPR-2024-42
if (user.isAutoDeactivated) { ... }
7. オブジェクト・カリステニクス
Jeff Bayが考案した、オブジェクト指向設計を改善するための9つの練習です。これらは目標として 捉えてください。練習中は厳格に、本番環境では現実的に適用します。
| # | ルール | 目標 |
|---|---|---|
| 1 | メソッドごとのインデントは1段階まで | 積極的にメソッドを抽出する |
| 2 | elseを使わない | 早期リターン、ガード節、ポリモーフィズム |
| 3 | すべてのプリミティブ型にドメイン上の意味を持たせる | 値オブジェクト(Email、Money、UserId) |
| 4 | コレクションを第一級オブジェクトにする | 配列をドメイン固有のクラスでラップする |
| 5 | 1行につきドットは1つまで | デメテルの法則 — 直接関わる相手とのみやり取りする |
| 6 | 略語を使わない | 名前が長すぎるなら、クラスの責務も多すぎます |
| 7 | エンティティを小さく保つ | クラスは50行未満、メソッドは10行未満 |
| 8 | インスタンス変数を制限する | 2〜3個を目指し、クラスの責務を絞る |
| 9 | ゲッター/セッターを使わない | オブジェクトには振る舞いがあり、データだけではありません |
各ルールの例については、references/OBJECT_CALISTHENICS.mdを参照してください。
実装チェックリスト
コードを提出する前に確認してください。
- 新しいチームメンバーが質問せずに理解できますか?
- 名前は意図が伝わり、一貫していますか?
- 各関数は、ちょうど1つのことだけを行っていますか?
- ここで修正できるコードの臭いはありませんか?
- コメントは「何を」ではなく「なぜ」を説明していますか?
- エラー処理はすっきりしていて、具体的ですか?
- コードを、見つけたときより良い状態にしましたか?(ボーイスカウト・ルール)