clean-code について
ソフトウェアの分析、リファクタリング、保守にクリーンコードの実践を適用するスキルです。コードの問題点を評価し、SOLID 原則、命名、エラー処理、複雑度管理を案内します。
主な機能
- 01コードの文脈と複雑度の分析
- 02コードの問題点の特定と優先順位付け
- 03段階的なリファクタリング
- 04SOLID と DRY 原則の適用
- 05命名とエラー処理の指針
活用シーン
- 01リファクタリング前のコードベース評価
- 02コードの問題点を減らす変更の計画
- 03可読性とテスト容易性の向上
- 04リファクタリング中の動作維持
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ pixel-process-ug/ superkit-agents --skill clean-codeインストール後、Claude Code で/clean-codeを入力して読み込まれたか確認します。
詳細
クリーンコード
概要
クリーンコードの原則を適用して、読みやすく、保守しやすく、テスト可能なソフトウェアを作成します。このスキルでは、SOLID 原則、DRY の適用、コードスメルの特定、リファクタリングパターン、命名規則、エラー処理、複雑さの管理を扱います。Robert C. Martin、Martin Fowler、Kent Beck の著作に基づいています。
開始時に宣言: 「コード品質を向上させるため、クリーンコードスキルを使用します。」
フェーズ 1:現在のコードを分析する
目標: 変更を加える前に、コードを全体の文脈で読み、理解します。
アクション
- コードを全体の文脈で読む(断片だけでなく)
- コードの責務と目的を特定する
- 循環的複雑度を測定する
- 結合関係と依存関係を把握する
- 既存のテストを確認する
停止 — 次の条件を満たすまでフェーズ 2 に進まないでください:
- コードを全体の文脈で読んだ
- 目的と責務を理解した
- 複雑度の高い箇所を特定した
- 既存のテストカバレッジを把握した
フェーズ 2:コードスメルを特定する
目標: 以下の参照表を使用して、すべてのコードスメルを記録します。
肥大化
| スメル | 検出方法 | リファクタリング |
|---|---|---|
| 長いメソッド | 30 行超 | メソッドの抽出 |
| 大きなクラス | 300 行超、または責務が 5 つ超 | クラスの抽出 |
| 長いパラメーターリスト | パラメーターが 3 つ超 | パラメーターオブジェクトの導入 |
| データの塊 | 同じパラメーターが一緒に現れる | クラスの抽出 |
| プリミティブ型への執着 | 小さなオブジェクトの代わりにプリミティブ型を使用 | 値オブジェクトへの置き換え |
オブジェクト指向の濫用
| スメル | 検出方法 | リファクタリング |
|---|---|---|
| switch 文 | 型による switch | ポリモーフィズムへの置き換え |
| 並行継承 | 各サブクラスに対応するサブクラスが必要 | 階層の統合 |
| 継承の拒否 | サブクラスが継承したメソッドを無視する | 継承を委譲に置き換え |
変更の妨げ
| スメル | 検出方法 | リファクタリング |
|---|---|---|
| 発散的変更 | 複数の理由で 1 つのクラスが変更される | クラスの抽出(SRP) |
| ショットガン手術 | 1 つの変更が多くのクラスに及ぶ | メソッドの移動、クラスのインライン化 |
不要物
| スメル | 検出方法 | リファクタリング |
|---|---|---|
| デッドコード | 到達不能、または未使用 | 削除 |
| 投機的一般化 | 「念のため」の未使用の抽象化 | 階層の縮小、削除 |
| 不適切なコードを説明するコメント | 不明瞭なコードを補うコメント | 名前の変更、メソッドの抽出 |
停止 — 次の条件を満たすまでフェーズ 3 に進まないでください:
- すべてのコードスメルを記録した
- 各スメルに優先度(高/中/低)を付けた
- それぞれのリファクタリング方針を特定した
フェーズ 3:リファクタリングを適用する
目標: リファクタリングパターンを 1 つずつ適用し、その都度テストで確認します。
アクション
- 一度に適用するリファクタリングは 1 つだけにする
- 変更のたびにテストを実行する
- テストが失敗した場合は、直ちに変更を元に戻す
- コードがクリーンになるまで続ける
- 命名、構造、ドキュメントを見直す
停止 — 次の条件を満たしたらリファクタリング完了です:
- 優先度の高いスメルをすべて解消した
- 変更のたびにすべてのテストが成功した
- リファクタリング中に動作を変更していない
- 新しいチームメンバーにも読みやすいコードになっている
SOLID 原則
S — 単一責任の原則
クラス/モジュールが変更される理由は、ただ 1 つであるべきです。
スメル: 無関係な複数の理由で変更されるクラス。 修正: 責務を別々のクラスに抽出します。
O — オープン・クローズドの原則
拡張に対して開き、変更に対して閉じます。
スメル: 新しい型が増えるたびに大きくなる switch 文。 修正: ポリモーフィズム、ストラテジーパターン、またはプラグインアーキテクチャを使用します。
L — リスコフの置換原則
サブタイプは、基底型と置き換え可能でなければなりません。
スメル: サブクラスが「未サポート」をスローするようにメソッドをオーバーライドする。 修正: 階層を再構成し、継承よりコンポジションを優先します。
I — インターフェース分離の原則
クライアントは、使用しないメソッドに依存すべきではありません。
問題点: メソッドが多いインターフェースがあり、実装側が一部を何もしない処理にしています。 修正方法: インターフェースを、より小さく目的を絞ったものに分割します。
D — 依存性逆転の原則
具象ではなく、抽象に依存します。
問題点: 上位モジュールが下位モジュールを直接インポートしています。 修正方法: インターフェースや抽象クラスを介して依存関係を注入します。
命名規則
ルール
| 要素 | 規則 | 例 |
|---|---|---|
| 変数 | 格納するものを表す名詞 | n ではなく userCount |
| ブール値 | is/has/can/should を接頭辞に付ける | isActive、hasPermission |
| 関数 | 動作を表す動詞 | calculateTotal、fetchUsers |
| 定数 | UPPER_SNAKE_CASE | MAX_RETRY_COUNT |
| クラス | PascalCase の名詞 | UserRepository、PaymentService |
| インターフェース | 能力を表す | Serializable、Cacheable |
名前の長さのガイドライン
| スコープ | 長さ | 例 |
|---|---|---|
| ループカウンター | 1~2文字 | i、j(短いループのみ) |
| ラムダーパラメーター | 文脈が明確な場合は1~3文字 | users.filter(u => u.active) |
| ローカル変数 | 短く、かつ説明的 | total、result |
| 関数名 | 中程度の長さで説明的 | calculateMonthlyRevenue |
| クラス名 | 必要なだけ長く | AuthenticationTokenValidator |
関数のガイドライン
サイズと構造
- 関数は1つのことだけを行うべきです
- 理想:5~15行(定型コードを除く)
- 最大:30行(超える場合は切り出します)
- パラメーターの最大数:3(超える場合はオプションオブジェクトを使います)
ガード節(早期リターン)
// Bad: nested conditions
function getDiscount(user) {
if (user) {
if (user.isPremium) {
if (user.orderCount > 10) {
return 0.2;
}
}
}
return 0;
}
// Good: guard clauses
function getDiscount(user) {
if (!user) return 0;
if (!user.isPremium) return 0;
if (user.orderCount <= 10) return 0;
return 0.2;
}
エラー処理のパターン
選択表
| 方法 | 使用する場面 | 例 |
|---|---|---|
| Result 型 | 関数型スタイル、想定される失敗 | Result<T, E> の戻り値型 |
| 個別の例外 | OOP スタイル、例外的なケース | throw new ValidationError(...) |
| エラーコード | C スタイルの API、言語をまたぐ場合 | 戻りコード + メッセージ |
| Option/Maybe | 値が存在する場合としない場合がある | Option<User> |
Result 型パターン
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function parseConfig(raw: string): Result<Config, ParseError> {
try {
const config = JSON.parse(raw);
if (!isValidConfig(config)) {
return { success: false, error: new ParseError('Invalid config structure') };
}
return { success: true, data: config };
} catch {
return { success: false, error: new ParseError('Invalid JSON') };
}
}
エラー処理で避けること
- エラーを捕捉して、何もせずに握りつぶしてはいけません
- 制御フローに例外を使ってはいけません
- エラーを示すために null を返してはいけません
- 文脈を追加せずにログを記録して再スローしてはいけません
複雑度の指標
| 範囲 | リスクレベル | 対応 |
|---|---|---|
| 1-5 | 低 | 対応不要 |
| 6-10 | 中程度 | リファクタリングを検討します |
| 11-20 | 高 | リファクタリングするべきです |
| 21+ | 危険 | リファクタリングが必須です |
複雑度を下げる方法
- 複雑な条件式を、名前を付けたブール値に切り出します
- ネストした条件分岐をガード節に置き換えます
- 型チェックの代わりにポリモーフィズムを使います
- より小さな関数に分解します
- switch/if の連鎖の代わりにルックアップテーブルを使います
DRY 適用の選択表
| 状況 | DRY を適用するか | 理由 |
|---|---|---|
| ロジックが完全に重複している | はい | 同じロジックは1か所にまとめるべきです |
| 3回以上出現する | はい | Rule of Three によりパターンが確認できます |
| 2回だけ出現する | 待ちます | たまたま似ているだけかもしれません |
| 構造は似ているが目的が異なる | いいえ | 変更する理由が異なります |
| 抽象化によって複雑さが増す | いいえ | DRY より明瞭さを優先します |
コメントの考え方
良いコメント
| 種類 | 例 |
|---|---|
| 理由(判断の根拠) | // リストが事前にソートされており、項目数が10Kを超えるため二分探索を使う |
| 法的な情報 | 著作権、ライセンスのヘッダー |
| チケット番号付き TODO | // TODO(PROJ-123): レート制限を追加する |
| 警告 | // WARNING: これはスレッドセーフではありません |
| 公開 API のドキュメント | 公開インターフェース用の JSDoc/TSDoc |
悪いコメント(削除して、代わりにコードを修正します)
| 種類 | 例 |
|---|---|
| コードの言い換え | counter++ の前にある // カウンターをインクリメントする |
| コメントアウトされたコード | 代わりにバージョン管理を使います |
| 作業記録のコメント | 代わりに git log を使います |
| 閉じ波括弧のコメント | } // if の終わり |
アンチパターン/よくある間違い
| アンチパターン | 問題である理由 | 適切な方法 |
|---|---|---|
| 早すぎる抽象化 | 意図が異なるコードをDRY化する | Rule of Threeを待つ |
| ゴッドクラス | あらゆることを把握し、あらゆることを実行する | 責務ごとに分割する(SRP) |
| フィーチャーエンヴィー | メソッドが自クラスのデータよりも他クラスのデータを多く使用する | メソッドをデータの所有者に移す |
| 文字列型データ | 列挙型や型を使うべき箇所に文字列を使う | 適切な型を定義する |
| マジックナンバー | 意味が不明確で、エラーが起きやすい | 名前付き定数を使う |
| ブール値の罠 | ブール値の引数によって動作が変わる関数 | 名前付きオプションまたは別々の関数を使う |
| 過剰設計 | 存在しない問題のために抽象化する | YAGNI — 必要になるまで作らない |
統合ポイント
| スキル | 関係 |
|---|---|
code-review | レビューで特定されたコードスメルを、clean-codeで解消する |
test-driven-development | TDDにより、リファクタリング中の動作を維持する |
senior-frontend | フロントエンドコンポーネントはクリーンコードの原則に従う |
senior-backend | バックエンドサービスはSOLIDとクリーンアーキテクチャに従う |
performance-optimization | クリーンコードにより、パフォーマンス最適化が容易になる |
systematic-debugging | クリーンコードはデバッグしやすい |
不変性に関する方針
- デフォルトで
constを使う(JavaScript/TypeScript) readonlyプロパティとReadonlyArrayを使う- 変更よりスプレッド構文/分割代入を優先する
- 状態には不変な更新パターンを使う
- パフォーマンスプロファイリングで必要性が示された場合にのみ変更する
スキルの種類
FLEXIBLE — コンテキストに応じて原則を適用します。すべての関数を5行にする必要はなく、すべてのパターンがSOLIDに従う必要もありません。理論的な純粋さより、チームにとっての読みやすさを優先し、適切に判断してください。