clean-code について
読みやすく保守しやすいコードを目指す開発者向けのコード品質スキルです。命名、責務を絞った関数、基本原則、アンチパターン、品質向上の実践を扱います。
主な機能
- 01命名の指針
- 02単一責任の関数
- 03早期リターン
- 04マジックナンバーの回避
- 05コード品質の実践
活用シーン
- 01深くネストした関数のリファクタリング
- 02曖昧な名前を明確な名前に変更
- 03重複コードの削減
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill clean-codeインストール後、Claude Code で/clean-codeを入力して読み込まれたか確認します。
同梱ファイルquick-ref/principles.mdquick-ref/refactoring.md
詳細
クリーンコードの基本原則
深い知識:包括的なドキュメントを参照するには、テクノロジーに
clean-codeを指定してmcp__documentation__fetch_docsを使用してください。
命名
// ❌ Bad
const d = new Date();
const yyyymmdd = formatDate(d);
function calc(a, b) { return a + b; }
const list = users.filter(x => x.active);
// ✅ Good
const currentDate = new Date();
const formattedDate = formatDate(currentDate);
function calculateTotal(price, quantity) { return price * quantity; }
const activeUsers = users.filter(user => user.isActive);
関数
// ❌ Bad - Too many responsibilities
function processUser(user) {
validateUser(user);
saveToDatabase(user);
sendWelcomeEmail(user);
updateAnalytics(user);
}
// ✅ Good - Single responsibility
function createUser(userData: UserInput): User {
const user = validateAndBuildUser(userData);
return userRepository.save(user);
}
// Separately handle side effects
async function onUserCreated(user: User) {
await sendWelcomeEmail(user);
await analytics.trackSignup(user);
}
早期リターン
// ❌ Bad - Nested conditions
function getDiscount(user) {
if (user) {
if (user.isPremium) {
if (user.yearsActive > 2) {
return 0.2;
} else {
return 0.1;
}
} else {
return 0;
}
}
return 0;
}
// ✅ Good - Early returns
function getDiscount(user: User | null): number {
if (!user) return 0;
if (!user.isPremium) return 0;
if (user.yearsActive > 2) return 0.2;
return 0.1;
}
マジックナンバーを避ける
// ❌ Bad
if (user.age >= 18 && items.length <= 10) { ... }
setTimeout(callback, 86400000);
// ✅ Good
const MINIMUM_AGE = 18;
const MAX_CART_ITEMS = 10;
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
if (user.age >= MINIMUM_AGE && items.length <= MAX_CART_ITEMS) { ... }
setTimeout(callback, ONE_DAY_MS);
原則のまとめ
| 原則 | 説明 |
|---|---|
| DRY | 同じことを繰り返さない |
| KISS | シンプルに保つ |
| YAGNI | 必要になるまで作らない |
| SRP | 単一責任の原則 |
| Composition | 継承よりコンポジションを優先する |
このスキルを使用しない場合
このスキルはコードレベルの品質に重点を置いています。次の場合は使用しないでください。
- SOLID の原則 - OOP 設計の原則には
solid-principlesスキルを使用してください - Git コミットの品質 - バージョン管理のベストプラクティスには
git-workflowスキルを使用してください - パフォーマンス最適化 - 速度やメモリの最適化には
performanceスキルを使用してください - セキュリティ脆弱性 - OWASP またはセキュリティ専用のスキルを使用してください
- ビルド/ツール設定 - フレームワーク専用のスキル(例:
biome、vite)を使用してください
アンチパターン
| アンチパターン | 問題点 | クリーンコードでの解決策 |
|---|---|---|
| 神オブジェクト | 責任が多すぎるクラス | 役割に特化したクラスに分割する(SRP) |
| マジックナンバー | 文脈を理解しにくい | 名前付き定数を使用する |
| 深いネスト | ロジックを追いにくい | 早期リターンを使い、関数を抽出する |
| 長いパラメーターリスト | 使用や保守が難しい | パラメーターオブジェクトまたはビルダーパターンを使用する |
| コピー&ペーストしたコード | バグが重複し、保守しにくい | 共通関数を抽出する(DRY) |
| 曖昧な名前 | data、temp、x | 意図が伝わる名前を付ける |
| ゲッターの副作用 | 予期しない動作 | 純粋関数を使い、クエリとコマンドを分離する |
| コードの代わりにコメントを使う | コメントが古くなり、コードが煩雑になる | 明確な名前を付けた自己文書化コードにする |
すぐにできるトラブルシューティング
| 問題 | 確認事項 | 解決策 |
|---|---|---|
| 複雑な関数 | 循環的複雑度 > 10 | 小さな関数に分割し、早期リターンを使う |
| 読みにくいコード | 説明にコメントが必要 | 変数や関数の名前を変更し、ロジックを抽出する |
| ロジックの重複 | ファイル間でコピー&ペーストされている | 共通ユーティリティ/サービスに抽出する |
| 長いファイル | > 300 行 | 責任ごとに分割し、関心事を分離する |
| 不明瞭な変数 | 名前から意図が伝わらない | 内容や表すものがわかる名前に変更する |
| マジックナンバーがある | 説明のないリテラル値 | 説明的な名前を付けた const を定義する |
権威ある情報源
- Clean Code(Robert C. Martin 著)- https://www.oreilly.com/library/view/clean-code-a/9780136083238/
- Refactoring Catalog(Martin Fowler 著)- https://refactoring.com/catalog/
本番環境への準備
コード品質ゲート
// eslint.config.js
export default [
{
rules: {
// Complexity limits
complexity: ['error', { max: 10 }],
'max-depth': ['error', 4],
'max-lines-per-function': ['warn', { max: 50 }],
'max-params': ['warn', { max: 4 }],
// Maintainability
'no-duplicate-imports': 'error',
'no-else-return': 'error',
'prefer-const': 'error',
'no-var': 'error',
},
},
];
// biome.json
{
"linter": {
"rules": {
"complexity": {
"noExcessiveCognitiveComplexity": {
"level": "error",
"options": { "maxAllowedComplexity": 15 }
}
}
}
}
}
エラー処理のパターン
// Custom error hierarchy
class AppError extends Error {
constructor(
message: string,
public code: string,
public statusCode: number = 500,
public isOperational: boolean = true
) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
class ValidationError extends AppError {
constructor(message: string) {
super(message, 'VALIDATION_ERROR', 400);
}
}
class NotFoundError extends AppError {
constructor(resource: string, id: string) {
super(`${resource} not found: ${id}`, 'NOT_FOUND', 404);
}
}
// Error boundary pattern
async function handleRequest<T>(operation: () => Promise<T>): Promise<Result<T>> {
try {
const data = await operation();
return { success: true, data };
} catch (error) {
if (error instanceof AppError && error.isOperational) {
return { success: false, error };
}
// Log unexpected errors
logger.error('Unexpected error', { error });
throw error; // Re-throw for crash recovery
}
}
コードレビューの自動化
# .github/workflows/code-quality.yml
name: Code Quality
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Check cognitive complexity
run: npx @biomejs/biome check --diagnostic-level=error .
- name: Detect code duplication
run: npx jscpd src/ --threshold 5
テスト基準
// Test naming convention
describe('UserService', () => {
describe('createUser', () => {
it('should create user with valid data', async () => {
// Arrange
const userData = { name: 'John', email: '[email protected]' };
// Act
const user = await userService.createUser(userData);
// Assert
expect(user.id).toBeDefined();
expect(user.name).toBe('John');
});
it('should throw ValidationError for invalid email', async () => {
// Arrange
const invalidData = { name: 'John', email: 'invalid' };
// Act & Assert
await expect(userService.createUser(invalidData))
.rejects.toThrow(ValidationError);
});
});
});
// Test coverage thresholds
// vitest.config.ts
{
test: {
coverage: {
provider: 'v8',
thresholds: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
},
}
ドキュメント基準
/**
* Creates a new user in the system.
*
* @param data - User creation data
* @returns The created user with generated ID
* @throws {ValidationError} If the data is invalid
* @throws {ConflictError} If email already exists
*
* @example
* ```ts
* const user = await createUser({
* name: 'John Doe',
* email: '[email protected]'
* });
* ```
*/
async function createUser(data: CreateUserData): Promise<User> {
// Implementation
}
// README template for modules
/**
* # User Module
*
* ## Overview
* Handles user management including CRUD operations.
*
* ## Usage
* ```ts
* import { UserService } from './user';
* const service = new UserService(repository);
* ```
*
* ## API
* - `createUser(data)` - Creates a new user
* - `getUser(id)` - Retrieves user by ID
* - `updateUser(id, data)` - Updates user data
*/
モニタリング指標
| 指標 | 目標 |
|---|---|
| 認知的複雑度 | < 15 |
| 循環的複雑度 | < 10 |
| コードの重複 | < 5% |
| テストカバレッジ | > 80% |
| 技術的負債比率 | < 5% |
チェックリスト
- 意味のある変数名/関数名
- 単一の責任を持つ関数
- ネストを減らす早期リターン
- マジックナンバーに定数を使用
- 一貫したエラー処理
- リンターで複雑度の上限を設定
- テストカバレッジのしきい値
- 公開 API に JSDoc を記述
- コードの重複を検出
- SonarQube の品質ゲート
参考ドキュメント
コメント
Markdown 対応