前置き
今回は、使われなくなったコードを安全に削除する手順のメカニズムを、
「エンタープライズアーキテクチャ(EA)の4階層モデル」と「クリーンアーキテクチャの同心円モデル」の両方に対応付けて詳細に解説します。
両モデルに共通する原則は
「外側(ユーザーの入り口)から内側(データの保管場所)に向かって削除する」
ことです。
安全性を最優先するなら、入り口(プレゼンテーション層)から内側(エンティティ、インフラ)に向かって削除を進めるのが最も安全で現実的なアプローチです。
使われなくなったコードの削除は、建物の解体作業に似ています。
土台基礎(インフラ)から取り壊すことはなく、必ず最上階の入り口から作業を始めます。これは大鉄則です。
エンタープライズアーキテクチャ(EA)4階層モデルでの削除手順
EAモデルは「ビジネス」「アプリケーション」「データ」「テクノロジー」の4層で構成されます。
削除の意思決定はビジネス層から始まりますが、実行は逆の順序に近い形をたどります。
ステップ1:意思決定 (ビジネスアーキテクチャ層)
・目的
削除対象のビジネス機能やサービスを公式に決定する。
・アクション
プロダクトマネージャーや事業責任者が、利用率の低下、ROIの悪化、戦略の変更などを理由に「機能Xを廃止する」と意思決定します。
この決定は、関係する全てのチーム(開発、マーケティング、サポート)に通知される必要があります。
ステップ2:アプリケーションの入り口を塞ぐ (アプリケーションアーキテクチャ層 - プレゼンテーション部分)
・目的
ユーザーが廃止された機能にアクセスできなくする。
・アクション
UIの無効化
フロントエンドのコードから、機能Xに関連する画面、ボタン、リンク、メニュー項目を削除または非表示にします。
APIエンドポイントの閉鎖
APIゲートウェイやバックエンドのルーティング設定から、機能Xのエンドポイントを削除します。これにより、外部システムからのアクセスも遮断されます。
フィーチャーフラグの利用
段階的な廃止の場合、まずフィーチャーフラグで機能をOFFにし、全ユーザーからアクセスできなくなったことを確認してからコード削除に進むのが安全です。
ステップ3:アプリケーションロジックの削除 (アプリケーションアーキテクチャ層 - ビジネスロジック部分)
・目的
到達不能になったビジネスロジックと関連コードを安全に削除する。
・アクション
機能Xを実現していたサービスクラス、コンポーネント、関連するビジネスルールやバリデーションロジックをコードベースから削除します。
関連する単体テストや結合テストも同時に削除し、残りのテストスイートがすべて成功することを確認します。
次のトピックで詳しく解説します。
ステップ4:データ構造の削除準備 (データアーキテクチャ層)
・目的
データ層の整合性を保ちつつ、不要なデータ構造を削除する準備をする。
・アクション
アプリケーションコードから、機能Xが利用していたデータベースのテーブル、カラム、
ビューへの参照(Read/Write)が完全になくなったことを確認します。
必要に応じて、削除前にデータをバックアップまたはアーカイブします。
(この時点ではまだDBスキーマは削除しません)
ステップ5:インフラの撤去 (テクノロジーアーキテクチャ層)
・目的
機能Xのためだけに存在していた物理的なインフラリソースを撤去する。
・アクション
IaCコードの修正
TerraformやCloudFormationのコードから、機能X専用のデータベーステーブル、S3バケット、Lambda関数、メッセージキューなどのリソース定義を削除します。
terraform applyの実行
IaCツールを実行し、クラウド環境からインフラリソースを実際に削除します。
DBスキーマの削除
ここで初めて、データベースマイグレーションツールなどを使って、ステップ4で特定した
不要なテーブルやカラムを物理的に削除します。
テスト駆動削除
最初にアプリケーションコードを削除し、その後にテストコードを削除するのが、最も安全で規律ある手順です。
この手順は、テスト駆動開発(TDD)の思想を逆再生するようなもので、
「テスト駆動削除(Test-Driven Deletion)」
と考えることができます。
考え方
「レッド→グリーン→リファクタリング」のサイクルは、テストがコードの振る舞いを保証する「安全網」であることを示しています。
「テスト駆動削除」は、この安全網を逆手に取った応用です。
プロダクションコードを削除して意図的にテストを失敗させ(レッド)、その失敗したテストを削除することで元に戻す(グリーン)という流れは、TDDの規律を削除プロセスに適用したものです。
では、なぜ「アプリコード → テストコード」の順なのか?を追っていきましょう。
理由1:テストを「安全網」として利用するため
テストコードは、リファクタリングやコード変更が意図しない副作用(デグレード)を起こしていないことを保証する「安全網」です。
コードを削除する際も、この安全網を最大限に活用します。
①. 最初にアプリケーションコードを削除する
対象のビジネスロジック(サービスクラスなど)を削除します。
②. テストを実行し、意図的に失敗させる
この時点でテストを実行すると、削除されたクラスやメソッドを参照しているテストがコンパイルエラーになるか、実行時エラーで失敗します。
この「失敗」こそが、私たちの期待しているシグナルです。
③. 失敗したテストを確認する
失敗したテストが、まさに先ほど削除したロジックのためだけに存在していたテストであることを確認します。
もし、全く関係ないと思っていた別のテストが失敗した場合、それは削除したコードが予期せぬ場所で使われていた証拠であり、重大なバグの発生を未然に防いだことになります。
理由2:テストコードの削除漏れを防ぐため
もし先にテストコードを削除してしまうと、テストスイートは常に成功(グリーン)の状態を保ちます。
先にテストを削除した場合の問題
アプリケーションコードを削除した後、関連するテストコードを削除し忘れたとしても、
それに気づく術がありません。
使われていないテストコード(Orphaned Tests)が残り続け、将来的にコードベースの理解を妨げる技術的負債となります。
ここのまとめ
この手順は、一見遠回りに見えますが、以下の絶大なメリットがあります。
したがって、
「まずプロダクションコードを消してテストを赤くする(壊す)。
次に、その赤いテストを消して再び緑にする(直す)」
というリズムが、最も戦略的なな削除の作法と言えます。
クリーンアーキテクチャの同心円モデルでの削除手順
クリーンアーキテクチャは「Entities」「Use Cases」「Interface Adapters」「Frameworks & Drivers」の同心円で構成されます。
ステップ1:外殻の無効化 (Frameworks & Drivers層)
・目的
UIや外部DB、APIなど、システムの最も外側にある具体的な実装を無効化する。
・アクション
UIの削除
Webフレームワークのビュー定義から、関連する画面コンポーネントを削除します。
ルーティングの削除
Webフレームワークのルーターから、関連するURLエンドポイントの定義を削除します。
DBマイグレーションの準備
データベースのテーブルやカラムを削除するマイグレーションスクリプトを作成します。(まだ実行はしません)
ステップ2:アダプターの削除 (Interface Adapters層)
・目的
外界との変換役を担っていたアダプター群を削除する。
・アクション
Controllersの削除
HTTPリクエストを受け取り、ユースケースを呼び出していたコントローラークラスを削除します。
Presentersの削除
ユースケースの出力をUI向けのデータモデルに変換していたプレゼンターを削除します。
Gateways/Repositoriesの実装クラスの削除
DBとやり取りしていたリポジトリの具体的な実装クラス(例: UserPostgresRepository)を削除します。
ステップ3:ビジネスルールの削除 (Use Cases層)
・目的
アプリケーション固有のビジネスロジック(ユースケース)を削除する。
・アクション
機能Xを実現していたインタラクターやサービスクラス(DeleteFeatureUseCaseなど)を削除します。
このユースケースが依存していたリポジトリのインターフェース(抽象)も、他で使われていなければ削除します。
ステップ4:中核エンティティの整理 (Entities層)
・目的
アプリケーション全体の中核であるエンティティを整理する。
・アクション
もしエンティティが機能Xのためだけに存在していた場合は、エンティティクラスそのものを削除します。
エンティティが他の機能と共有されている場合は、機能Xのためだけに使われていたメソッドやプロパティを削除します。
ステップ5:永続化層の物理削除 (Frameworks & Drivers層 - 再び)
ココはインフラ基盤そうなので、やはり最後に削除するのは変わりません。
この順序を守ることで、
「アプリケーションはまだそのDBテーブルを参照しているのに、先にテーブルを削除してしまい、大規模な障害を引き起こす」
といった最悪の事態を避けることができます。
・目的
全ての参照がなくなったことを確認し、物理的なデータを削除する。
・アクション
ステップ1で準備したデータベースマイグレーションスクリプトを実行し、テーブルやカラムを物理的に削除します。
IaCコードを修正し、インフラリソースを撤去します。
削除の容易さというビジネス上のメリット
削除戦略の容易さは、アーキテクチャの依存関係と階層を揃えることによって得られる、
最も重要かつ見過ごされがちなメリットの一つです。
ついつい、我々はアーキテクチャを考える際に技術的負債を溜めないというリスク回避に関心が行きがちですが、この側面に加え、整えられたアーキテクチャには
「削除の容易さ」というビジネス上の大きな価値
があります。
だからこそ、レイヤー化され、かつ各レイヤーの依存関係がエンタープライズアーキテクチャレベルで準拠された規律のある建築学を学ぶ価値が絶大なんです。
1. 影響調査とコスト見積もりの精度向上 🎯
整ったアーキテクチャ
依存関係が一方向(外側→内側)に揃っているため、ある機能を削除したい場合、その機能が依存している内側のコンポーネントには影響がないことが自明です。
影響範囲は、その機能を利用している外側のコンポーネントに限定されるため、調査が非常に容易になります。
乱れたアーキテクチャ
依存関係が双方向に入り乱れていると、ある機能の削除がシステムのどこに影響を及ぼすか予測が困難です。影響調査だけで数週間を要し、見積もり精度も著しく低下しやすいです。
2. 安全かつシンプルな削除実行 🗑️
整ったアーキテクチャ
「入り口を塞ぎ、外側から順に内側へ」という明確で安全な削除手順を実行できます。
各コンポーネントの責務が明確であるため、削除による予期せぬ副作用(デグレード)の
リスクが最小限に抑えられます。
乱れたアーキテクチャ
いわゆる「ジェンガ」のような状態で、どこを抜くと全体が崩れるかわからないため、削除作業自体が非常に高リスクなプロジェクトになります。
結果として、誰も使っていない機能(デッドコード)がリスク回避のために放置され、システムが肥大化していきやすいです。
この状態で、マイクロサービス化をするなど、世も末な意思決定です。
3. ビジネスの俊敏性の向上 🚀
整ったアーキテクチャ:
使われなくなった機能を低コストで安全に削除できるため、企業は新しい機能への投資や、より価値の高い機能の改善にリソースを集中できます。
市場の変化に合わせてプロダクトを素早くピボットさせる(方向転換する)決断も容易になります。
乱れたアーキテクチャ
機能の削除コストが高すぎることが、新しいビジネス戦略への挑戦をためらわせる技術的な制約となり得ます。
まとめ
結論として、依存関係と階層が整ったアーキテクチャは、コードの追加や変更だけでなく、「捨てる」という戦略的な選択を容易にします。
これにより、企業は常に健全でスリムな状態を保ち、変化の激しい市場環境に迅速に対応し続けることができるのです。