コピペ(コピー・アンド・ペースト)とは、コードの断片を新しいコンテキストに適応させずに、ある場所から別の場所にコピーする習慣です。多くの場合、開発者は既存のモジュールからブロックをコピーし、最小限の編集を行って新しい場所に貼り付けます。バグ、古いコメント、不要な依存関係も一緒に。 TIOBE Code Quality Survey(2025)によると、コピペのレベルが高いプロジェクトは、統一された抽象化を持つプロジェクトよりも、コード1000行あたりの欠陥が3倍多いことがわかりました。 コードの重複は技術的負債の主な原因です。各コピーは個別のメンテナンスを必要とし、1か所でバグを修正しても、他の場所で修正される保証はありません。
重要ポイント
コピペ(コピー・アンド・ペーストプログラミング)とは、既存のコードを少しまたはまったく変更せずに新しい場所に移動することです。この用語は軽蔑的な意味で使われます。開発者が解決策を設計しているのではなく、既成のブロックを機械的にコピーしており、多くの場合、その仕組みを完全に理解していないことを意味します。
コピペには2つのタイプがあります。意図的なものと偶発的なものです。意図的とは、開発者が後でリファクタリングする計画で意図的にコードをコピーする場合です(しかし計画はしばしば放棄されます)。偶発的とは、例えば2人の開発者が独立して異なる画面に同じロジックを書くなど、重複が気づかれずに発生する場合です。
SonarQube State of Clean Code(2025)レポートによると、商用プロジェクトでは重複コードが総コード量の平均12~18パーセントを占めています。同時に、重複コードのバグを修正するコストは、単一実装のコードの2.5倍です。開発者はすべてのコピーを見つけて修正する必要があるからです。
コピペに対する主要なツールはDRY(Don't Repeat Yourself)原則です。しかし、DRYを絶対視することも危険です。2つのコピーが互いに独立して進化する必要がある場合、コピーが正当化されることもあります。「偶発的な重複」(排除すべきもの)と「必要な重複」(文書化すべきもの)を区別することが重要です。
最初で最も重要な危険はバグの伝播です。元のコードに欠陥があると、コードとともにすべての新しい場所にコピーされます。欠陥が発見されて元のモジュールで修正されても、コピーは修正されないままです。開発者はバグが5つの異なるファイルに存在することさえ知らないかもしれません。
2つ目の危険は不均等な進化です。同じアルゴリズムの2つのコピーは、時間の経過とともに異なる修正を蓄積します。一方のコピーには境界値の検証が追加され、もう一方では出力形式が変更されます。数ヶ月後には、どのバージョンが「正しい」のか判断できなくなり、プロジェクトは動作の一貫性を失います。
3つ目の危険はテスト量の増加です。コピペのインスタンスごとに独自のテストが必要です。共通ロジックが単一の関数に抽出されれば、1つのテストセットでカバーして再利用できます。重複があると、各コピーを個別にテストする必要があり、CIの実行時間と維持すべきテストベースのサイズが何倍にも増加します。
4つ目の危険は生産性の錯覚です。コピペは速度の誤った感覚を生み出します。開発者は素早くコードを貼り付けて、画面が動作するのを確認します。しかし、この「速度」は技術的負債に変わり、重複ブロックでバグが見つかったりビジネスロジックの変更が必要になったりしたときに、利子をつけて返済しなければなりません。
コピペの理由を理解することは、適切な予防策を立てるのに役立ちます。ほとんどの場合、開発者は怠惰のためではなく、納期のプレッシャー、知識不足、または不便なアーキテクチャのためにコードをコピーします。
最初の理由は納期です。2日で画面を作成する必要があり、類似の画面が既に存在する場合、開発者はそれを丸ごとコピーして、ユーザーが見る部分だけを変更します。共通コンポーネントを抽出してリファクタリングする時間はありません。クライアントは結果を求めています。その結果、80パーセントの共通コードを持ちながら、変更履歴が独立した2つ目の画面が生まれます。
2つ目の理由は統一された抽象化の欠如です。プロジェクトに典型的なタスク(プル・トゥ・リフレッシュ付きリスト画面など)の共通コンポーネントがない場合、各開発者は独自の実装を書くか、隣の実装をコピーします。プロジェクト開始時に下されたアーキテクチャ上の決定は、将来のコピペの量に直接影響します。
3つ目の理由は動作しているコードを壊す恐れです。開発者は既存のモジュールが動作することを知っています。共通コードを抽出するリファクタリングは、既存の機能に影響を与える可能性があります。テストカバレッジが低い場合、破損のリスクがリファクタリングの認識された利益を上回り、開発者は安全な道であるコピーを選択します。
症状ではなく原因に対処してください。プロジェクトに強固なアーキテクチャ基盤がなければ、納期を短縮したりコードレビューを導入したりしても問題は解決しません。初期段階で再利用可能なコンポーネントの作成に時間を投資してください。それが将来のコピペの誘惑を減らす唯一の方法です。
コピペの検出は、コードフラグメントを比較し、指定されたしきい値以上の一致を識別する自動アナライザーによって実行されます。最良のツールはAST(抽象構文木)レベルで動作し、書式設定、変数名、コメントを無視します。
PMD CPD(コピーペースト検出器)は、Java、Kotlin、Swift、JavaScript、Python、C++向けの最も一般的なツールです。CPDはソースコードトークンを分析し、指定された最小トークン数(デフォルト100)より長い重複を見つけます。品質の高い結果を得るにはしきい値の設定が重要です。低すぎるしきい値は多くの偽陽性(インポートなどの一般的なパターン)を生み、高すぎるしきい値は実際の重複を見逃します。
plugins {
id 'pmd'
}
pmd {
toolVersion = '7.0.0'
ruleSetFiles = files("pmd-rules.xml")
}
tasks.register('cpd') {
doLast {
exec {
workingDir = projectDir
commandLine 'cpd',
'--minimum-tokens', '75',
'--language', 'kotlin',
'--files', 'src/main/kotlin',
'--format', 'xml',
'--failOnViolation', 'true'
}
}
}
SonarQubeは重複検出器をQuality Gateに直接組み込んでいます。Duplicated Blocks(%)ルールは重複コードの割合を示します。商用プロジェクトでは5%のしきい値が健全とみなされます。これを超えるとリリースブランチへの昇格がブロックされます。SonarQubeはさらに、重複をタイプ別にグループ化します。完全一致と構造的コピー(名前が変更された識別子を含む)です。
JavaScriptとTypeScriptの場合、重複はESLintのeslint-plugin-sonarjsプラグイン(no-duplicate-stringルール)とjscpdユーティリティを使用して検出されます。jscpdは150以上の言語をサポートしています。jscpdは特にモノレポに便利で、単一モジュール内だけでなくパッケージ間の重複も検出します。
コピペのリファクタリングは1つの原則に集約されます。共通部分を抽出し、違いをパラメータ化することです。具体的な手法は重複の範囲とコンテキストによって異なります。
最も単純なケースは単一クラス内の重複です(例:同じロジックだが異なる型を持つ2つのメソッド)。解決策はジェネリクスで一般化するか、型パラメータを持つメソッドを再利用することです。重複が複数のクラスにまたがる場合は、共通コードをユーティリティクラスまたは拡張関数に抽出します。
より複雑なケースは画面またはモジュールレベルでの重複です。この場合、単に関数を抽出しても役に立ちません。UI構造、ライフサイクルロジック、データバインディングがすべて重複しているからです。解決策は、共通のベース画面クラスまたは複合ビューコンポーネントを作成し、パラメータまたはプロトコルを介して違いを渡すことです。
// before - two copies of the same UITableViewController
class UserListController: UITableViewController {
private let viewModel = UserListViewModel()
// 40 lines of code
}
class ProductListController: UITableViewController {
private let viewModel = ProductListViewModel()
// same 40 lines but with Product instead of User
}
// after - generic base class shared
class ListViewController<T: ListViewModel>: UITableViewController {
let viewModel: T
// 40 lines of code - once only
init(viewModel: T) {
self.viewModel = viewModel
super.init(style: .plain)
}
}
最も複雑なケースはマイクロサービス間またはライブラリ間の重複です。共有コードを抽出すると、循環依存関係や不当な結合を引き起こす可能性があります。そのような場合、コピペは意識的な決定であり得ます。2つのチームが独立したサービスを維持しており、共有ライブラリが解決するよりも多くの問題を生み出す場合です。重要なのは、そのような決定を文書化し、コピーが統合を正当化するほどに分岐したかどうかを定期的に確認することです。
コピペを防ぐことは、すでに重複したコードをリファクタリングするよりも効果的です。主な予防策はテクノロジーではなく、開発プロセスの組織化にあります。
最初の対策は重複に焦点を当てたコードレビューです。レビューチェックリストには「このPRにはプロジェクトに既に存在するコードが含まれていますか?」という項目を含める必要があります。レビューアがコピペを発見した場合、共有コンポーネントが抽出されるまでマージをブロックします。この要件はチームのDefinition of Doneの一部であるべきです。
2つ目の対策は共有コンポーネントライブラリです。2つ以上の画面に表示されるUIパターンはすべて、共通モジュールに抽出する必要があります。プロジェクトに共有モジュールを作成し、すべてのUIコンポーネントの必須エントリポイントにします。コンポーネントが存在しない場合は、最初に作成してから画面で使用します。
3つ目の対策はCI/CDの自動化です。パイプラインに重複コードチェックステップ(PMD CPD、jscpd、SonarQube)を追加します。しきい値を超えるとビルドが失敗します。開発者は許可されたレベルを超えてコピペ比率を上げるPRをマージできません。これにより責任がコードレビューから自動化に移り、どの重複も見逃されないことが保証されます。
“1つの実装 — 1つの場所”の文化を育成してください。再利用の機会を見つけたら、リファクタリングを後回しにしないでください。「後で」残されたコピペは増殖し、制御不能な技術的負債に変わります。
よくある質問
いいえ、意識的な重複のシナリオもあります。独立して進化する必要のある異なるマイクロサービス、削除計画のある実験用にコピーされたコード、異なるAPIバージョン用のテンプレートDTOなどです。重要なのは理由を文書化し、リファクタリングのための確認期限を設定することです。
コピペとは、コードの2つの部分が同じことを行っているが、共通の抽象化を持っていない場合です。健全な再利用とは、共通コードが関数、クラス、またはモジュールに抽出され、違いがパラメータ化されている場合です。ロジックの変更に3か所以上の編集が必要な場合 — それがコピペです。
PMD CPDはSwiftとObjective-Cをサポートしています。Xcode用にはSwiftCopのようなプラグインやAppCodeに組み込まれた重複検出器があります。SonarQubeもSwiftプロジェクトを分析し、プルリクエストで直接重複ブロックを表示します。
主要なコピーごとにリファクタリングの技術チケットを作成します。優先順位を設定します。頻繁に変更される画面を優先し、安定した画面は後回しにします。重複コードに触れる新しいPRごとに、段階的な統合のために15~20パーセントの時間を割り当てます。
はい、最新のAIアシスタント(GitHub Copilot、Codeium)はコンテキストを分析し、繰り返しパターンを検出すると共有コードの抽出を提案できます。ただし、自動アナライザーに代わるものではありません。Copilotは予防に、CPD / SonarQubeは検出に使用してください。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。