コードレビューをしていると、特によく出会う場面があります。
機能を1つ修正しようと検索すると、ほとんど同じコードが3か所に出てきます。1か所だけ直すと、残り2か所はそのままバグとして残ります。
今日の主役は、この問題を正面から扱う原則、DRYです。前回取り上げたKISS原則とは、対になるように登場します。
DRYは「Don’t Repeat Yourself」の略です。システム内で同じ知識を繰り返さない、という原則です。
1999年に刊行された書籍<The Pragmatic Programmer>でAndy HuntとDave Thomasがまとめた概念で、原文の定義はとても正確です。
すべての知識は、システム内にただ1つの、曖昧さのない、権威ある表現を持つべきである。
ここで注目したいのは「コード」ではなく「知識」という言葉です。これが今日の話の核心です。
コードのコピペはなぜ問題なのか?
コピー&ペースト自体が悪いわけではありません。問題はその後に起こります。
同じロジックが3か所にあると、要件が変わったときに3か所すべてを探して修正する必要があります。1か所でも見落とせば、それがバグになります。
// 送料計算が3つのファイルに分散している場合
// Cart.swift
let fee = total >= 50000 ? 0 : 3000
// Checkout.swift
let shippingFee = totalPrice >= 50000 ? 0 : 3000
// OrderSummary.swift
let delivery = price >= 50000 ? 0 : 3000
ある日、「送料無料の基準を3万ウォンに下げよう」という要望が来たらどうでしょう。3つのファイルをすべて探す必要があります。検索語もそれぞれ違うので、1か所は必ず見落とします。
// 知識を1か所に集約すると
// Shipping.swift
enum ShippingPolicy {
static let freeShippingThreshold = 50000
static let fee = 3000
static func shippingFee(for total: Int) -> Int {
total >= freeShippingThreshold ? 0 : fee
}
}
これでポリシーが変わっても、1か所だけ修正すれば済みます。これがDRYです。
重複しているのはコードではなく「知識」です
しかし、ここで多くの人が一度つまずきます。DRYを「見た目が似ているコードはすべてまとめる」と解釈してしまうのです。
DRYが指す重複はコードの形ではなく、知識、つまりビジネスルールの重複です。
この区別が重要なのは、世の中にはたまたま似ているコードが存在するからです。
| 状況 | 重複? |
|---|---|
| 送料計算のロジックが3か所にある | 本当の重複(同じ知識) |
| 会員登録のバリデーションとイベント応募のバリデーションが偶然似ている | 見かけの重複(異なる知識) |
| 定数3000が送料とポイント付与の基準にそれぞれ登場する | 見かけの重複(意味が異なる) |
会員登録のバリデーションとイベント応募のバリデーションは、今はどちらも「名前必須、電話番号11桁」なのでコードが同じに見えます。しかし、2つのルールは異なる理由で変更されます。1つにまとめると、イベントのルールを変更しただけで会員登録が壊れます。
早すぎる抽象化は重複より高くつきます
そのため、開発者コミュニティでは「間違った抽象化より重複を選ぶ(prefer duplication over the wrong abstraction)」という言葉をよく耳にします。著名なRuby開発者、Sandy Metzの言葉です。
似ているように見える2つのコードを無理にまとめると、次のようなことが起こります。
- 共通関数を作る
- 片方の要件が変わり、オプションパラメータを追加する
- もう片方も変わり、if分岐を追加する
- いつの間にか、誰にも理解できない5つのパラメータを持つ関数になる
ここまで来ると、コピペを2つ残したほうがよかったのです。
そのため実務では「3の法則(Rule of Three)」をよく使います。同じパターンが2回出たらいったん様子を見て、3回目に現れたら抽象化します。3回ほど繰り返されると、本当に同じ知識なのか偶然なのかが見えてくるからです。
私が使う判断基準
重複を見つけたら、私はこの1つの質問を投げかけます。
この2つのコードは、同じ理由で変更されるのか?
同じ理由で変わるなら本当の重複なのでまとめます。異なる理由で変わるなら、どれほど似て見えてもそのままにします。
今日のまとめ
DRY原則を要約すると、こうなります。
- 重複の単位はコードの形ではなく、知識(ビジネスルール)です。
- 同じ理由で変わるコードだけをまとめましょう。たまたま似ているコードはそのままにしましょう。
- 確信がなければ3の法則です。3回目の繰り返しで抽象化しても遅くありません。
KISSが「シンプルに保つ」なら、DRYは「知識を1か所だけに置く」です。どちらも結局は変更コストを下げるための考え方です。
検索するたびに3か所で見つかるロジックが、1つくらい思い浮かぶのではないでしょうか。それが今日のリファクタリング候補です。

