【プログラマが知るべき97のこと – #07】共有は慎重に:コード再利用の前にコンテキストを確認する
概要
コードの再利用は、重複を減らし、保守を容易にするための有効な方法とされています。しかし、異なる役割を持つ部分のコードを安易に共通化すると、不要な依存関係が生まれ、かえって保守コストが増えることがあります。共通ライブラリを作成する前に、コードのコンテキストや各部分の関係性を理解することが重要です。
なぜコードの再利用が必ずしも良いとは限らないのか?
ソフトウェア開発において、コードの再利用は一般的に良い設計の一つとされています。
同じ処理を何度も記述する代わりに、共通の関数やライブラリとしてまとめることで、重複を減らし、保守しやすくすることができます。
しかし、システム全体の構造を考慮せずに共通化すると、予期しない問題が発生する場合があります。
『プログラマが知るべき97のこと』で、Udi Dahan(ウディ・ダーハン) 氏は、大学卒業後に初めて参加したプロジェクトでの経験を紹介しています。
当時、技術者として自分の能力を証明したかった著者は、既存のコードを熱心に読み、学んだことを実践しようとしていました。
初めて担当した作業では、コメントの追加やログの記録に加えて、似た処理を共通ライブラリにまとめました。
ところが、コードレビューで、そのような再利用が必ずしも良い結果をもたらさないことを知ります。
問題はコードの再利用そのものではありませんでした。
見落としていたのは、「コンテキスト(Context)」だったのです。
コードの類似性よりもコンテキストが重要
例えば、システム内の二つの部分に、ほとんど同じ処理が存在するとします。
一見すると、その処理を共通の関数にまとめるのは合理的です。コードの行数を減らし、重複をなくすことができます。
しかし、その二つの部分が、システム内でまったく異なる役割を担っていたらどうでしょうか。
現在の実装が似ていても、業務上の責任や変更が必要になる理由まで同じとは限りません。
コードが似ているのは、たまたま今だけかもしれないのです。
共通化する前は、二つの部分を独立して変更できました。一方の処理を変更しても、もう一方には影響しません。
しかし、共通ライブラリにまとめると、両方が同じ実装に依存するようになります。
Udi Dahan氏は、この状態を**「一本の靴ひもを両足の靴に通す」**ことに例えています。
それまで独立していた部分が、互いに結び付けられてしまうのです。
共通ライブラリのわずかな変更が、複数の機能に影響する可能性があります。
コードの重複を減らすことが、必ずしもシステム全体の複雑さを減らすとは限りません。複雑さが依存関係に移ることもあるのです。
共通化によって保守コストが増える理由
二つのコンポーネントに、それぞれ同じような4行のコードがあるとします。
最初は独立しているため、一方の業務要件が変わっても、もう一方を修正する必要はありません。
しかし、その4行を共通ライブラリにまとめると、両方のコンポーネントが同じコードに依存します。
その結果、共通部分を変更するたびに、次のような作業が必要になる可能性があります。
- 変更がほかの利用箇所に影響しないか確認する
- 関連する機能が正しく動作するか検証する
- 予期しない不具合を防ぐために追加のテストを行う
コードの総行数は減ったにもかかわらず、変更にかかる手間は増えてしまうのです。
ここで重要なのが、**「コードの重複」と「結合度(Coupling)」**の違いです。
コードの重複とは、似た処理が複数の場所に存在することです。一方、結合度は、システムの構成要素がどの程度互いに依存しているかを表します。
重複をなくすための共通化は、新しい依存関係を生むことがあります。
その依存関係が小さな範囲に収まっていれば、再利用のメリットが大きい場合もあります。
しかし、独立した複数の領域にまで依存関係が広がると、保守やテストの負担が増える可能性があります。
コードを再利用する前に考えるべきこと
この教訓は、コードの再利用を避けるべきだという意味ではありません。
適切なコンテキストであれば、共通化は非常に有効です。
共通ライブラリを作成する前に、次の点を確認するとよいでしょう。
- 各部分は同じ責任を持っているか? コードが似ていても、役割が同じとは限りません。
- 同じ理由で変更されるか? 異なる業務要件によって変更されるなら、独立させた方が適切かもしれません。
- 依存関係はどこまで広がるか? 小さなモジュール内の共有と、独立した複数の領域を結び付ける共有では、影響範囲が異なります。
- 本当に保守が容易になるか? 数行の削減よりも、追加のテストや変更の負担が大きくなる可能性があります。
代表的なケースを整理すると、次のようになります。
| 状況 | 設計上の考え方 |
|---|---|
| 同じ責任を持つ似たコード | 共通化によって保守が容易になる可能性がある |
| 異なる役割を持つ似たコード | 独立した実装を維持することも検討する |
| 関連性の高いモジュール内での共有 | 依存関係を比較的管理しやすい |
| 独立した複数の領域での共有 | 変更時の影響範囲を慎重に評価する |
大切なのは、単にコードが似ているかどうかではありません。
設計や業務の観点から、本当に共通化すべき関係にあるかどうかです。
ソフトウェア開発にどう活かすべきか?
実際の開発では、コードの重複を減らすことが品質改善の一環として重視されることがあります。
しかし、リファクタリングの目的は、コードの行数を減らすことだけではありません。
各コンポーネントの責任や境界、共通化によって生まれる依存関係も考える必要があります。
VAONのようなソフトウェア開発チームにとっても、アーキテクチャの検討やリファクタリングの際に参考となる考え方です。
共通ライブラリを作る前に、そのコードを利用する部分が本当に同じコンテキストに属しているかを確認することが重要です。
適切に設計されたライブラリは保守を容易にします。一方、不要な共通化は、本来独立して変更できる部分の保守コストを増やしてしまいます。
まとめ:コードの共有は慎重に
コードの再利用は有効な手法ですが、常に最善の選択とは限りません。
今は同じ処理に見えても、それぞれ異なる役割を持ち、将来的には別々の方向に変更される可能性があります。
共通化によってコードの重複は減りますが、同時に依存関係も生まれます。
優れたソフトウェア設計の目的は、単にコードの行数を最小限にすることではありません。
理解しやすく、変更しやすく、保守しやすい構造を維持することです。
コードを共有する前に、コンテキストを確認しましょう。再利用はシステムをシンプルにするためのものであり、不要な依存関係を増やすためのものではありません。
「プログラマが知るべき97のこと」シリーズについて
本シリーズでは、プログラマーに役立つソフトウェア設計の考え方や、問題解決、日々の開発業務に関する実践的な教訓を紹介します。
本記事は、Udi Dahan(ウディ・ダーハン) 氏による『プログラマが知るべき97のこと』の第07項目、「共有は慎重に」 を参考に、その中心的な考え方を整理したものです。
原文のメッセージを大切にしながら、ソフトウェア開発の現場で活かせるように解説しています。