コードの重複 vs. 間違った抽象化: DRY とオーバーエンジニアリングのバランスをとる
間違った抽象化のコスト
重複したコードを維持することは、間違った抽象化を管理することよりも大幅に安価であることが多いです。DRY (Don't Repeat Yourself) 原則は広く教えられていますが、時期尚早に、あるいは教条的に適用すると、単純な重複があるコードベースよりも修正が困難な「オーバーエンジニアリング」されたコードベースを招く可能性があります。
設計が不十分なコードベースは、問題領域を正確に反映していない複雑で硬直的な抽象化に悩まされるコードベースよりも、一般的に扱いやすいものです。抽象化が不正確な場合、開発者は自分が作成したフレームワークと戦うことになり、単一の変更が継承クラスやパラメータ化された関数の迷宮をナビゲートすることを必要とする、脆弱なシステムへとつながります。
偶発的な重複 vs. 真の重複の区別
すべてのコードの重複が同じであるわけではありません。抽象化するかどうかの決定は、その重複が「真の」ものか「偶発的な」ものかに依存します。
- 真の重複: 同じロジックが複数の場所に適用されるのは、それが単一の普遍的な真実(例: 特定の物理公式)を表している場合です。これは「単一の真実のソース (single source of truth)」を確保するために抽象化されるべきです。
- 偶発的な重複: 2つの異なる機能が、現在は偶然似ているだけで、将来的に独立して進化する場合に発生します。これらを単一の抽象化に強制的にまとめることは、一方の機能のための変更が予期せずもう一方を壊してしまう「長距離結合 (long-distance coupling)」を生み出します。
あるコントリビューターが指摘したように、もしコードの重複が、2つの分岐するパスを1つに強制的にまとめることで発生するバグを防げるのであれば、リファクタリングは必要です。しかし、もし抽象化が単に利便性のためのものであり、それが不便になり始めたのであれば、その目的は果たせていません。
抽象化すべきタイミングを判断するための戦略
重複と抽象化のバランスを見つけることは、エンジニアリングの核心的なスキルです。リファクタリングの適切なタイミングを判断するために、いくつかのヒューリスティックが使用されます。
三回の法則 (The Rule of Three)
多くの開発者は「三回の法則」に従っています。これは、重複が2回までは許容されるが、3回目に同じパターンが現れたとき、抽象化の時であると示唆しています。これにより、単一の類似性の事例に基づいた時期尚早な抽象化を防ぐことができます。
圧縮 vs. 抽象化
コードの行数を減らすこと(圧縮)と、概念的に一貫性のある部分を抽出すること(抽象化)を区別することは役立ちます。高品質なエンジニアリングは、単純な行数圧縮よりも概念的な抽象化を優先します。
継承よりもインターフェース (Interface over Inheritance)
DRYへの過度な準拠がよくもたらす結果である、深い継承ツリーの落とし穴を避けるために、多くのエンジニアはインターフェースの使用を好みます。これにより、硬直的なクラス階層よりも、柔軟でメンテナンスしやすい直交的なコードが可能になります。
重複の反論とリスク
「重複は安価である」という議論は人気がありますが、リスクも伴います。特に大規模な環境では以下の通りです。
大規模環境でのメンテナンス負担: プロジェクトが一定の規模(例: 数十の顧客)に達すると、重複が禁止的に高価になる場合があります。多くのインスタンスにわたって重複したコードを維持することは、開発者リソースを消費し尽くす可能性があります。
LLM Factor (LLM の要因): AI生成コードの時代において、重複は危険になる可能性があります。LLMは、すべての重複パターンに対して一貫して同じ修正を適用することを信頼できない場合があり、不一致によってバグを導入する可能性があります。
沈没費用と慣性 (Sunk Cost and Inertia): 一部の人は、広範な重複が、誰もリファクタリングする権限を持てないようなメンテナンスコストを生み出し、間違った抽象化とは異なる種類の技術的負債を生み出すと主張しています。
エンジニアリングのトレードオフの要約
| アプローチ | 主なメリット | 主なリスク |
|---|---|---|
| 重複を受け入れる | 時期尚早で硬直的な抽象化を避ける; 迅速なイテレーションが可能。 | 分岐のリスク; 反復的な変更に対する高い手動作業量。 |
| 抽象化を適用する | 単一の真実のソース; 行数削減; グローバルな更新が容易。 | 「間違った」抽象化のリスク; 複雑な結合を生み出す; 元に戻すのが困難。 |