こんにちは、ファインディで開発部長をしているhamです。
この記事は🎄ファインディエンジニア #3 Advent Calendar 2025 13日目の投稿です。
この記事では、継続してソフトウェア開発をしていると発生しがちな「似たような実装が増える」について、対処法と共通化の判断基準についてお話しします。
はじめに
ソフトウェア開発を長く続けていると、ソースコードの中に「双子」や「三つ子」、あるいは「クローン軍団」のような似た実装が量産されていることに気づく瞬間があります。
「最初は1つしかなかったはずなのに、気がついたら似たような処理が大量にある……」
疎結合(Coupling)の観点では、それぞれが独立しているため悪くないように見えますが、凝集度(Cohesion)や再利用性、そして何より保守性の観点では大きな問題を抱えています。
今回は、なぜこのような「重複実装」が生まれてしまうのか、そしてそれをどのように技術的・プロセス的に解消していくべきかについてまとめます。
なぜ「コピペ実装」は量産されるのか
開発者のスキル不足や怠慢が原因だと思われがちですが、実は 「責任感」と「恐怖心」 が原因であるケースが多々あります。
ある機能に改修が必要になったとき、既存のメソッドを汎用的に修正しようとすると、そのメソッドを利用している全ての機能への影響範囲(デグレ) を調査しなければなりません。
- 「既存の機能は絶対に壊したくない」
- 「納期が迫っており、全量テストをする時間がない」
- 「影響範囲が見えない(テストコードがない)」
こうした心理的圧力が働くと、エンジニアは最も安全な策を選びます。すなわち、「既存のコードをコピーして、新しい名前をつけ、そこだけを修正する」 という方法です。
これは短期的には「安全なリリース」を保証しますが、長期的には「技術的負債」という利子が積み上がっていきます。
重複実装が招く3つのリスク
-
修正漏れ(バグの温床)
ビジネスロジックに変更があった際、Aは直したが、コピーであるBとCを直し忘れるという事故が多発します -
可読性の低下(認知負荷)
新しく参画したメンバーは、似たようなメソッドが並んでいるのを見て「どれを使えばいいのか?」「微妙に何が違うのか?」を解読するのに膨大な時間を費やします -
変更への恐怖の増大
コード量が増えれば増えるほど、システム全体の見通しが悪くなり、余計に「どこも触りたくない」という状況が悪化します
対処法:コピペ地獄からの脱出ステップ
すでに増えてしまった重複コードを整理するには、段階的なアプローチが必要です。
Step 1. まずは「壊れていないことを証明する手段」を用意する(テストコード)
「既存を壊すのが怖い」のが根本原因ですから、「壊れていないことを証明する手段」 なしに共通化に着手してはいけません。
リファクタリングを行う前に、重複しているAとBの両方に対して、現在の挙動を保証する自動テスト(単体テスト等)を書きます。テストがGreen(成功)になって初めて、共通化のスタートラインに立てます。
Step 2. 「3回の法則 (Rule of Three)」に従う
プログラミングには 「2回までは重複を許容し、3回目で初めてリファクタリング(共通化)する」 という経験則があります。
2つだけなら「偶然似ているだけ」の可能性があり、無理に共通化すると逆にコードが複雑になる(早すぎる最適化)リスクがあるからです。3回同じ処理が出てきたら、それは間違いなく「パターン」です。このタイミングで共通化を行いましょう。
Step 3. 「継承」と「委譲」を武器として使い分ける
共通化のアプローチには大きく分けて「継承」と「委譲」の2つがあります。どちらかが絶対的な正解というわけではなく、状況に応じた使い分けが重要です。
A. 継承(Inheritance)が適しているケース
「親クラス=骨組み」を作り、子クラスで「肉付け」をするアプローチです。
代表的なのが Template Method パターン です。
-
どんな時?:
- 処理の「大まかな流れ(手順)」は完全に固定されていて、その一部のステップだけを少し変えたい場合
- 「AはBの一種である(Is-a関係)」が明確に成り立つ場合
-
メリット:
- コード量が少なく済み、直感的に実装しやすい
-
デメリット:
- 親クラスと子クラスが密結合になるため、親の変更が全子クラスに影響する
B. 委譲(Delegation / Composition)が適しているケース
「部品」を作って、それを本体に「持たせる(または渡す)」アプローチです。
代表的なのが Strategy パターン です。
-
どんな時?:
- 処理の一部を、実行時などに柔軟に切り替えたい場合
- 「AはBを持っている(Has-a関係)」の場合
- 将来的に組み合わせのバリエーションが増えそうな場合
-
メリット:
- 疎結合であり、個々の部品(クラス)を単体テストしやすい
-
デメリット:
- クラス数が増え、構成が少し複雑になる
判断のポイント:
迷ったら 「委譲(Strategy)」 を選ぶ方が安全です。継承は一度実装すると後から剥がすのが大変ですが、委譲は比較的変更に強い構造だからです。「どうしても処理のフローを固定化したい」という強い理由がある時だけ継承を選びましょう。
重要な判断基準:「偶然」か「本質」か
最後に、「本当に共通化すべきか?」 という判断基準についてです。
全ての似ているコードが悪ではありません。以下の問いかけをしてみてください。
「片方の要件が変わったとき、もう片方も必ず同じように変わるべきか?」
-
YESの場合(本質的な重複):
- 例:消費税計算、特定のバリデーションルールなど
- 対策: 共通化すべき(DRY原則)
-
NOの場合(偶然の重複):
- 例:ユーザー画面の商品リストと、管理画面の商品リスト
- 対策: 共通化しない
- 今はたまたま表示項目が同じでも、「管理画面には原価を出したい」となった瞬間、無理な共通化が足枷になります
まとめ:ボーイスカウト・ルールでいこう
大量の重複コードを一度に全て解消しようとすると、開発が止まってしまいます。
「ボーイスカウト・ルール(来た時よりも美しくして去る)」 を適用し、機能追加やバグ修正でそのファイルを開いたついでに、周辺の重複を1つだけ解消する。
地道ですが、テストという命綱をつけて、少しずつ「借金」を返済していくことが、健全な開発現場を取り戻す最短ルートです。
AIエージェントという新たな選択肢
近年、AIエージェント(Claude Code、GitHub Copilot Agent、Cursor など)の登場により、リファクタリング作業をAIに任せるという選択肢も現実的になってきました。
- 重複コードの検出と共通化の提案
- テストコードの自動生成
- 影響範囲の分析と安全なリファクタリングの実行
人間が方針を決め、AIが手を動かす——このハイブリッドなアプローチを取り入れることで、「地道な返済」のスピードを大幅に加速できる時代になっています。
