clean-code について
読みやすく保守しやすいコードを目指し、必要のない抽象化を避ける開発者向けのコーディングSkillです。変更範囲の確認から実装、テスト、提出前のチェックまでの手順を示します。
主な機能
- 01依頼の範囲に変更を限定
- 02シンプルな実装を優先
- 03目的が伝わる命名
- 04関数の責務を明確化
- 05動作と境界条件をテスト
- 06提出前に変更範囲を確認
活用シーン
- 01コード変更案のレビュー
- 02範囲を絞った機能追加やバグ修正
- 03リファクタリングで不要な抽象化を削減
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mindrally/ skills --skill clean-codeインストール後、Claude Code で/clean-codeを入力して読み込まれたか確認します。
詳細
クリーンコード
このスキルでは、読みやすく変更しやすいコードを書く方法と、同じくらい重要な、いわゆる「ベストプラクティス」の名のもとにコードを読みにくく、変更しにくくする過剰な設計を避ける方法を扱います。この2つはどちらも大切です。クリーンコードとはシンプルなコードであり、単に装飾が行き届いたコードではありません。
コードを書くとき、またはレビューするときのワークフロー
- 変更の範囲を定める — 何を求められているのかを正確に特定します。何かを書く前に、対象外のことを明確にします。
- まずは最もシンプルな解決策を選ぶ — 具体的な現時点の必要性から、より汎用的または設定可能な実装が正当化されない限り、直接的で分かりやすい実装を優先します。
- 目的が伝わる名前を付ける — 関数の本体や型の形を考える前に、意図が分かる名前を選びます。
- 関数の役割を1つに絞る — 何をする関数なのか説明するコメントが必要なら、関数を分割します。
- 重複を意図的に取り除く — 先回りして抽出するのではなく、実際に重複してから共通ロジックを抽出します(下記の三回ルールを参照)。
- テストを書く、または更新する — 新しい動作と、それによって生じるエッジケースをテストでカバーします。
- 提出前に範囲を確認する — 依頼されたコードだけが変更されていることを確認し、よりシンプルな方法を見落としていないか確認したうえで、依頼されていないファイルに触れていないことを確かめます。
意味のある名前
- 変数、関数、クラスは、名前だけで目的が分かるようにします。
- 名前からは、単に型や内容だけでなく、なぜ存在するのか、どのように使われるのかが分かるようにします(
list1ではなくactiveUserIds)。 - ドメイン内で広く理解されているものを除き、略語は避けます(
id、urlは問題ありませんが、usrCfgTmpは適切ではありません)。
マジックナンバーではなく定数を使う
- ハードコードされた値は、名前付き定数に置き換えます(数行後の3か所で裸の
3を使うのではなく、MAX_RETRY_COUNT = 3とします)。 - 値そのものではなく、その値の目的を説明する分かりやすい定数名を使います。
- 定数はファイルの先頭、または複数のファイルで共有する場合は専用の定数モジュールにまとめます。
賢いコメント
- コードが何をするかについてのコメントは避け、命名や構造によってコード自体が説明的になるようにします。
- 特に理由がコードから分からない場合は、なぜその方法を選んだのかを説明するコメントを使います(ライブラリのバグへの回避策、分かりにくい順序要件など)。
- 公開 API、本当に複雑なアルゴリズム、分かりにくい副作用についてはドキュメントを作成します。
単一責任
- 各関数が行うことは、正確に1つにします。
- 関数は、スクロールせずに理解できる程度に小さく、焦点を絞ったものにします。
- 何をする関数なのか説明するコメントが必要なら、名前付きのサブ関数に分割すべきというサインです。
DRY — 繰り返しを避ける
- 重複が実際に発生してから、繰り返されるコードを再利用可能な関数に抽出します。重複を予想して先回りすることは避けます。
- 共通ロジックは、調整を加えたコピー&ペーストではなく、適切な抽象化(共通関数またはモジュール)を通じて共有します。
- 個々のビジネスロジックや設定値について、信頼できる唯一の情報源を保ちます。
カプセル化
- 実装の詳細を明確なインターフェースの背後に隠します。呼び出し側は、利用するために仕組みを知る必要がないようにします。
- ネストした条件分岐は、深い
if/elseのツリーにするのではなく、分かりやすい名前の関数やガード節に移します。
// Before
function canCheckout(cart) {
if (cart.items.length > 0) {
if (cart.user.isVerified) {
if (cart.total <= cart.user.creditLimit) {
return true;
}
}
}
return false;
}
// After
function canCheckout(cart) {
const hasItems = cart.items.length > 0;
const isWithinCreditLimit = cart.total <= cart.user.creditLimit;
return hasItems && cart.user.isVerified && isWithinCreditLimit;
}
クリーンな構成
- 関連するコードはまとめて配置します(機能のコンポーネント、フック、スタイルは、ファイルの種類ごとに分散させず、1つのディレクトリにまとめます)。
- ドメインの理解の仕方を反映した、論理的な階層でコードを整理します。
- コードベース全体で一貫したファイル名とフォルダー名の規則を使います。
過剰なエンジニアリングを避ける
- 依頼されたことだけを変更します。依頼を満たす最もシンプルな解決策を優先します。
- 適切な抽象化のレベルが明確でない場合は、より複雑な案を推測で選ぶのではなく、確認してください。
- 依頼されていないコードは、改善できそうに見えても変更しないでください。無関係な関数をついでにリファクタリングすると、レビュー範囲が広がり、変更のリスクも高まります。
- 具体的かつ現時点での必要性がない限り、抽象化(インターフェース、ファクトリー、プラグインシステム、設定レイヤー)を追加しないでください。将来2つ目が現れるかもしれないという理由だけで、単一の実装にインターフェースを設ける必要はありません。これは推測に基づく汎用化です(YAGNI:「You Aren't Gonna Need It」)。
- 既存のコードを数行使えば解決できる問題に、新しい依存関係を導入しないでください。
- 小さな変更のためにファイル全体を書き換えないでください。対象を絞った差分のほうが、ファイル全体の書き換えよりレビューしやすく、安全にリリースできます。
- 周囲のコードが保証しているため起こりえない状況に対して、エラー処理を追加しないでください。不可能な状態に対する防御コードは、安全性を高めず、コードを読む負担を増やします。
三度目の法則
- あるパターンが最初の2回現れたときは、重複を許容します。
- 共有ロジックの実際の形が明確になる3回目に、抽象化を切り出します。1回目や2回目で切り出すと、実際に何が共有されているのかを誤って推測しがちです。
過剰なエンジニアリングの兆候
- 一度も1以外の値に設定されたことがない設定オプション。
- 実装が1つだけで、2つ目を必要とするテスト用ダブルもないインターフェース。
- 将来の呼び出し元を想定したフィールドが増え続ける汎用的な
optionsオブジェクト。 - 切り替える戦略が2つになる前に導入されたプラグイン/ストラテジーパターン。
コード品質の維持
- 専用の「リファクタリングスプリント」までクリーンアップを先送りせず、小さなステップで継続的にリファクタリングします。
- コードが現在の形になっている理由をまだ覚えているうちに、技術的負債を早めに解消します。
- すでに変更している範囲に限って、コードを変更前よりきれいな状態にします。これは無関係なファイルをリファクタリングする許可ではありません。
テスト
- バグを修正する前に失敗するテストを書き、修正を検証できるようにするとともに、バグが気づかれないまま再発しないようにします。
- テストは読みやすく、保守しやすい状態にします。テスト対象のコードより理解しにくいテストは、その役割を果たせていません。
- 正常系だけでなく、エッジケースやエラー条件も明示的にテストします。
バージョン管理
- 変更の理由が伝わる、明確で具体的なコミットメッセージを書きます。
- 小さく、対象を絞ったコミットを作成します。1つのコミットにつき、論理的な変更は1つにします。
- 作業内容がわかる、意味のあるブランチ名を使います。作成者や日付を表す名前は避けてください。
提出前のチェックリスト
- 依頼されたコードだけが変更されており、無関係なファイルには手を加えていない。
- 現時点で具体的な必要性がない抽象化を追加していない。
- すでに利用可能な機能と重複する依存関係を追加していない。
- よりシンプルな方法を検討して却下しており、単に検討を省略していない。
- 新しい動作にテストがあり、少なくとも1つのエッジケースもカバーしている。
コメント
Markdown 対応