dont-repeat-yourself について
dont-repeat-yourselfは、ロジックや知識を一か所で管理するためのコーディングスキルです。コピー&ペーストの兆候を見つけ、差分をパラメーター化して共通部分を抽出する考え方を示します。
主な機能
- 01重複したコードやロジックの検出
- 02共通関数やバリデーターの抽出
- 03類似コードの差分のパラメーター化
- 04定数やルールの集約
- 05重複の兆候をリファクタリングに活用
活用シーン
- 01新機能への既存コードのコピー回避
- 02フォーム間で重複する入力検証の集約
- 03複数ファイルにある計算処理の修正
- 04小さな差分がある類似関数のリファクタリング
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yanko-belov/ code-craft --skill dont-repeat-yourselfインストール後、Claude Code で/dont-repeat-yourselfを入力して読み込まれたか確認します。
詳細
DRY(同じことを繰り返さない)
概要
すべての知識は、システム内で一意かつ明確に表現されていなければなりません。
同じロジックを二度書いていることに気づいたら、切り出してください。重複は、いつ問題が起きてもおかしくないバグです。
使う場面
- 既存のコードに似たコードを書くとき
- コピーして変更するとき
- 複数のファイルで同じ変更をするとき
- フォーム間でバリデーションロジックが繰り返されるとき
- 異なる場所で同じ計算をするとき
絶対のルール
NEVER duplicate logic. Extract and reuse.
例外はありません:
- 「コピーしたほうが速いから」は理由になりません
- 「少し違うから」は理由になりません
- 「あとでリファクタリングするから」は理由になりません
- 「ほんの数行だから」は理由になりません
見分け方:コピー&ペーストの兆候
コードをコピーして変更しようとしているなら、やめてください:
// ❌ VIOLATION: Duplicated validation
function validateRegistrationEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function validateProfileEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); // Same logic!
}
// ✅ CORRECT: Single source of truth
function validateEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
// Reuse everywhere
const isValidRegistration = validateEmail(regEmail);
const isValidProfile = validateEmail(profileEmail);
見分け方:「複数箇所の変更」テスト
バグの修正に複数箇所の変更が必要なら、重複があります:
// ❌ Bug in tax calculation requires changes in 3 files
// cart.ts: const tax = price * 0.08;
// checkout.ts: const tax = price * 0.08;
// invoice.ts: const tax = price * 0.08;
// ✅ Single source of truth
// tax.ts: export const calculateTax = (price: number) => price * TAX_RATE;
正しいパターン:切り出してパラメーター化する
コードが「ほとんど同じ」なら、共通部分を切り出し、違いをパラメーター化してください:
// ❌ VIOLATION: Similar functions with minor differences
function formatUserName(user: User): string {
return `${user.firstName} ${user.lastName}`;
}
function formatAdminName(admin: Admin): string {
return `${admin.firstName} ${admin.lastName} (Admin)`;
}
// ✅ CORRECT: Parameterized
function formatName(person: { firstName: string; lastName: string }, suffix?: string): string {
const name = `${person.firstName} ${person.lastName}`;
return suffix ? `${name} (${suffix})` : name;
}
プレッシャーに負けないための手順
1. 「コピーしたほうが速い」
プレッシャー:「これをコピーして変更すればいい」
**回答:**コピーすると、保守する箇所が二つになります。バグの挙動にも違いが生じます。
**対応:**まず共通ロジックを切り出し、その後、両方の箇所で使います。
2. 「少し違う」
プレッシャー:「関数はほとんど同じだけど、完全には同じではない」
回答:「ほとんど同じ」なら、共通部分を切り出し、違いをパラメーター化します。
**対応:**共通部分を特定して切り出し、違いをパラメーターにします。
3. 「ほんの数行」
プレッシャー:「たった3行だから、切り出すほどではない」
**回答:**3行の重複が5箇所あれば、保守するのは15行です。バグも増えていきます。
**対応:**小さな重複も切り出します。適切な名前を付けてください。
4. 「あとでリファクタリングする」
プレッシャー:「今はリリースして、あとでDRYにする」
**回答:**しません。重複は広がっていきます。今DRYにすれば2分で済みます。
**対応:**重複をコミットする前に切り出します。
危険信号 — 立ち止まって再検討してください
次のいずれかに気づいたら、DRYに違反しようとしています:
- ワークフローでのCtrl+C / Ctrl+V
- 「あの別の関数と似ている」
- 複数箇所に同じ正規表現/バリデーションがある
- 同じエラーハンドリングパターンが繰り返されている
- 同じデータ変換ロジックが重複している
- 複数のファイルで定数を定義している
これらはすべて、共通の場所に切り出す必要があることを意味します。
重複の種類
| 種類 | 例 | 解決策 |
|---|---|---|
| コード | 同じ関数本体が二度ある | 関数を切り出す |
| ロジック | 同じアルゴリズムに異なる名前が付いている | 切り出してパラメーター化する |
| データ | 複数のファイルに同じ定数がある | 定数を一元管理する |
| 構造 | 同じ形のクラスが繰り返されている | インターフェース/基底クラスを切り出す |
| 知識 | 複数箇所にビジネスルールがある | 信頼できる唯一の情報源にまとめる |
クイックリファレンス
| 症状 | 対応 |
|---|---|
| コードをコピーして貼り付けている | 共通関数を切り出す |
| 同じバリデーションが二度ある | バリデーターのモジュールを作る |
| ファイル間で同じ定数がある | 定数ファイルを作る |
| 似た関数がある | 切り出してパラメーター化する |
| バグ修正に複数の変更が必要 | 一箇所に集約する |
よくある誤った言い訳(すべて不適切)
| 言い訳 | 実態 |
|---|---|
| 「コピーしたほうが速い」 | 重複したコードの保守には、より時間がかかります。 |
| 「少し違う」 | 共通部分を切り出し、違いをパラメーター化してください。 |
| 「ほんの数行」 | 少ない行数 × 多くの箇所 = 多くのバグです。 |
| 「あとでリファクタリングする」 | しません。今すぐ切り出してください。 |
| 「コンテキストが違う」 | コンテキストに関係なく、同じロジックなら同じコードです。 |
| 「コピーしたほうが読みやすい」 | 名前を付けて切り出した関数のほうが読みやすくなります。 |
要点
知識は一つ。コード内の場所も一つ。
似たコードを書くときは、立ち止まって既存のコードを探し、必要なら切り出して再利用してください。重複は、保守上の悪夢の元凶です。