remove-technical-debt について
remove-technical-debt は、既存システムの提供を続けながら、大規模で古いコードベースを改善する8段階のスキルです。変更箇所にテストの安全網を先に設け、リファクタリング、アーキテクチャ境界、信頼性の順に進め、記録を残して再開できます。
主な機能
- 018段階の段階的な負債返済
- 02変更箇所のテスト安全網を先に構築
- 03動作を保つ小さなリファクタリング
- 04負債とアーキテクチャ判断を記録
- 05プロジェクト文書から作業を再開
活用シーン
- 01システムを書き直さず技術的負債を削減
- 02頻繁に変更する箇所に特性テストを追加
- 03モノリスの境界を段階的に整理
- 04リリースを続けながら本番の信頼性を改善
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wondelai/ skills --skill remove-technical-debtインストール後、Claude Code で/remove-technical-debtを入力して読み込まれたか確認します。
同梱ファイルreferences/artifact-templates.md
詳細
技術的負債の解消
誰も触るのを恐れている、大規模で老朽化し、複雑に絡み合ったコードベースを引き受け、全面的な一括書き換えをせず、古いシステムを常にリリース可能な状態に保ちながら、その場で負債を返済します。書き換えたいという衝動は、苦戦しながらもリリースを続けているプロダクトを、苦戦し、リリースもできない状態に変える確実な方法です。この取り組みは、その代替策です。8 つのフェーズにわたる対話型の取り組みで、いつでも中断・再開できます。エージェントは判断のたびに確認を求め、その結果をプロジェクトの docs/ フォルダーに記録するため、どのフェーズの後でも中断して、後から再開できます。最初のフェーズでは、変更箇所にセーフティネットを構築します。以降のすべてのフェーズは、そのネットがあるため検証可能です。
基本原則
恐れよりフィードバックを重視します。カバーしてから変更し、変更して祈るのはやめましょう。実際に通る経路の負債をその場で返済し、リリースを止めないことです。 このスキルはフェーズの順序を定め、判断のための質問を行い、すべての選択を docs/ に記録します。各スキルが手法を担うため、独自のフレームワークを即興で作るのではなく、それらを呼び出してください。負債の山は、山を作り直しても解消できません。すでに変更している経路に沿って、安全でテスト済みの手順を一つずつ進めることで返済します。セーフティネットを構築する前に整理する、境界が実際に定まる前にサービスを分割するといった先走りは、この順序が防ごうとしている、まさにその失敗パターンです。
取り組みの全体像
| フェーズ | スキル | 解決する問い | 成果物 |
|---|---|---|---|
| 1 | working-with-legacy-code | 意図せず壊すことなくこのコードを変更できるでしょうか。また、どこから始めればよいでしょうか。 | docs/TESTING.md + docs/TECH-DEBT.md を作成 — 必須ゲート |
| 2 | refactoring-patterns | 振る舞いを変えずに構造を組み替えられるでしょうか。 | docs/TECH-DEBT.md を拡張 |
| 3 | clean-code | 変更した箇所は、次に読む人やエージェントにとって読みやすいでしょうか。 | docs/TECH-DEBT.md を拡張 |
| 4 | software-design-philosophy | 複雑さは、奥行きのあるモジュールの内側に隠されているでしょうか。 | docs/TECH-DEBT.md を拡張 |
| 5 | clean-architecture | ビジネスルールはフレームワークに依存しているでしょうか。それとも、その逆でしょうか。 | docs/ARCHITECTURE.md を拡張 |
| 6 | pragmatic-programmer | 負債の再蓄積を防ぐのは、どのような習慣でしょうか。 | docs/TECH-DEBT.md を拡張 |
| 7 | release-it | 過酷な本番環境でも耐えられるでしょうか。 | docs/RELIABILITY.md を拡張 |
| 8 | domain-driven-design | モノリスをどのように有界コンテキストへ切り分ければよいでしょうか。 | docs/ARCHITECTURE.md を拡張 |
運用ルール
- まず再開します。 何よりも先に、
docs/REMOVE-TECHNICAL-DEBT-PLAN.mdと Journey Map 内のすべての成果物を読みます。トラッカーが存在する場合は、ジャーニーの状態を3~5行で要約し、どのフェーズから始めるかを尋ねます。ユーザーが開始地点を確認した時点で完了です。トラッカーのあるジャーニーは再開するものであり、やり直してはいけません。 - 初回実行時のみインテークを行います。 トラッカーがない場合は、以下のインテークを実行してから、すべてのフェーズのステータスを
pending | in-progress | awaiting-evidence | done | deferred: reason | skipped: reasonとしたdocs/REMOVE-TECHNICAL-DEBT-PLAN.mdを作成します。トラッカーが存在し、ユーザーがフェーズ計画を確認した時点で完了です。 - フェーズへの着手。 そのフェーズで行うこと、求められる判断、作成する成果物、おおよその所要時間を説明します。実行/スキップ/延期の選択肢を提示します。GATE とマークされたフェーズは延期できますが、スキップはできません。実行する場合は、フェーズを
in-progressにします。ユーザーが選択した時点で完了です。 - スキルの呼び出しと代替手段。 フェーズのスキルを読み込み、使用します。各フェーズの Invoke 行ではスキルがスラッグで指定されています。そのスキルを使ってフェーズを実行してください。利用できない場合は、
npx skills add wondelai/skills/<slug> --globalを提示します。ユーザーが断った場合は、Brief に基づいて最小限の実行可能な方法でフェーズを進めます。どちらの方法で進めているかを明示します。 - フェーズ内の判断。 「ユーザーと決める」の下にあるすべての質問を、具体的な選択肢と推奨案を添えて尋ねます。選択内容をトラッカーの Key Decisions に記録します。確認せずに決定することは不備です。
- フェーズの完了。 書き込む前に、成果物のドラフト内容を提示し、承認を得ます。承認後、docs/ ファイルを作成または拡張し、トラッカーを更新します(ステータス、Key Decisions、Next Actions)。ファイルが書き込まれ、フェーズの行が
doneになった時点で完了です。 - 成果物の取り扱い。 書き込む前に読みます。ファイルがない場合にのみ作成し、すでにある場合は拡張します。ほかの人の内容を保持したうえで、自分のセクションを追加または更新してください。ファイル名は
docs/内で大文字にします。すべての推奨事項は、担当者と優先度を含むチェックボックスまたは表の行に記載します。docs/ファイルを初めて作成する際は、references/artifact-templates.md を参照してください。すべてのセクション見出しを含む完全なひな形から作成し、担当フェーズで指定されたセクションを埋めます。 - フェーズ1はGATEです。コミットは単一の目的に限定し、見つかったバグは修正せず記録します。 セーフティネットマップに記載されていないコードには一切変更を加えません。記載がない場合は、「Pinned behaviors」に記載されていないことを意味します。「Gaps」列の項目も変更禁止です。構造の変更と動作の変更を同じコミットに含めてはいけません。テストがすべて成功した状態で、構造だけを変更するコミットでリファクタリングし、その後、動作の変更は専用のコミットで行います。リファクタリング中にテストが失敗した場合は、デバッグせずに元に戻します。セーフティネットのテスト追加と docs/ の更新は、それぞれ専用のコミットにします。特性の確認中に見つかったバグは記録して Debt Ledger に起票し、黙って修正してはいけません。その挙動を呼び出し元が前提としている可能性があります。
取りかかる前の確認事項
トラッカーを作成する前に、以下を確認します。
- システムは何をするもので、規模や稼働年数はどの程度ですか。また、障害が発生した場合に起こり得る最悪の事態は何ですか。(リスクを把握し、フェーズの優先度を決めます)
- 次に変更するファイルはどれですか。また、
git logで変更頻度が特に高いファイルや、ドメインの中核となるファイルはどれですか。(三軸ヒューリスティックに基づいて、フェーズ 1 の開始モジュールを選びます) - テストスイートはありますか。現在 CI で正常に実行されていますか。それとも、不安定なため無効化されましたか。(フェーズ 1 のセーフティネットの範囲を決めます)
- スタックは何ですか。フレームワーク、ORM、データベースを教えてください。また、ビジネスロジックはコントローラーや ORM モデルの中に絡み合っていますか。(フェーズ 5 の境界作業とフェーズ 8 のコンテキストマッピングを実施するか判断します)
- 実際のユーザーがいる本番環境で稼働していますか。また、呼び出している外部依存関係は何ですか。サードパーティ API、決済、メール、キューなどを教えてください。(フェーズ 7 の統合ポイント監査を実施するか判断します)
- ビッグバンリライトを提案している人はいますか。(このジャーニーが段階的な道筋への置き換えを目的としている判断を明らかにします)
- 今回はジャーニーのどこまで進めますか。(フェーズ 1〜3 は、チームとコードの関係を最も早く変えます。境界や分解がボトルネックになったら、フェーズ 5〜8 に進みます)
フェーズをスキップする際のヒューリスティック:単一のモデルで無理なく扱える規模のコードベースなら、フェーズ 8 はスキップします。任意の system-design または ddia-systems を追加するのは、負債の返済によって実際のスケーリングやデータレイヤーの限界が明らかになった場合に限ります。解決策ではなく、要件から始めてください。実際のユーザーがいる場合、フェーズ 7 は任意ではありません。タイムアウトとサーキットブレーカーは必須要件です(deferred: reason の状態にはできますが、skipped にはできません)。フェーズ 1 は決してスキップしないでください。ここがゲートです。その後、テンプレートからトラッカーを作成し、計画を確認します。
docs/REMOVE-TECHNICAL-DEBT-PLAN.md が存在し、すべてのフェーズにステータスが設定され、ユーザーが計画を確認したら完了です。
フェーズ
フェーズ 1 — セーフティネットを構築し、着手箇所を見つける(working-with-legacy-code) — GATE
目的: 変更箇所の現在の振る舞いを固定し、影響範囲を把握することで、後続の各フェーズを検証可能にします。セーフティネットマップに記載されていないコードには、どのフェーズでも手を加えてはいけません。
概要(フォールバック): レガシーコードとは、テストのないコードです。カバーしてから変更し、編集して祈るようなことはしません。レガシーコード変更アルゴリズムを実行します。変更箇所を特定し、テスト箇所を見つけ、最も侵襲性の低い継ぎ目(本番用のデフォルト値を持つ Parameterize Constructor、Extract Interface)で依存関係を切り離します。実際の振る舞いを固定する特性テストを書きます(誤った値をアサートし、失敗を読み取り、実際の値を固定します)。その後、変更します。影響スケッチで影響範囲を限定し、少数のテストで最大の振る舞いをカバーする要所を見つけます。緊急の変更で期限内にカバーできない場合は、Sprout/Wrap を行い、テストされていないホストを負債として記録します。
実行: 取りかかる前の確認で選んだ開始モジュールを使用して、working-with-legacy-code スキルを使います。エントリーメソッドからの影響スケッチ、要所、切り離す継ぎ目、現在の振る舞いを固定する最小限の特性テスト一式を提示するよう求めます。
ユーザーと決めること: (1) 三軸ヒューリスティック(次に変更する箇所、変更頻度の高さ(git log)、ドメインの中核)に基づいて開始モジュールを確認します。(2) 特性テスト中に見つかったバグは、誤った振る舞いのまま固定し、Debt Ledger に記録します。黙って修正してはいけません。呼び出し元がその癖に依存している可能性があります。ユーザーがこれを受け入れることを確認します。
成果物: docs/TESTING.md を作成し、## Test Strategy、## Safety Net Map(module | pinned behaviors | test files | gaps)、## Characterization Backlog を記載します。docs/TECH-DEBT.md を作成し、## Debt Ledger(item | location | type | risk | effort | priority | status)と ## Sprout / Wrap Register を記載して、Sprout または Wrap を行ったコードを登録します。影響スケッチで見つけた要所を ## Test Strategy に記録します。トラッカーを更新します。
完了条件: 対象モジュールの振る舞いが固定され、テストスイートが正常に実行され、両方のファイルが存在し、トラッカーでフェーズ 1 が完了になっていること。この条件が満たされて初めて、フェーズ 2 に進めます。
フェーズ 2 — 名前付きリファクタリングで再構成する(refactoring-patterns)
目的: 「きれいにする」を、動作を保つ名前付きの変換へと置き換え、小さなステップで一つずつ適用します。
概要(フォールバック): リファクタリングは書き直しではありません。テストで裏付けられた、小さく動作を保つ変換です。それぞれのコードスメルを名前付きリファクタリングに対応させます。主力となるのは Extract Method です(コードブロックの説明にコメントを書きたくなったら、そのブロックを抽出し、コメントの内容を名前にします)。ほかにも、Replace Nested Conditional with Guard Clauses、Replace Conditional with Polymorphism、Introduce Parameter Object、Extract Class があります。ワークフローは、テストが成功することを確認し、変換を一つ適用し、再びテストが成功することを確認してからコミットします。テストが失敗したらデバッグするのではなく、変更を元に戻します。Branch by Abstraction は本番環境の大規模な構造を移行するために使い、Preparatory Refactoring は先に変更を容易にし、Rule of Three は早まった抽象化を防ぎます。
実行方法: 対象モジュールとフェーズ 1 のテストを使って、refactoring-patterns スキルを実行します。各コードスメルに名前を付け、対応する変換を示したうえで、テストを実行しながら一つずつ適用するよう依頼します。大規模な移行では、Branch by Abstraction の計画を依頼します。
ユーザーと決めること: 対象範囲 — 今回対応するコードスメル、今後の機能追加に備えて挿入箇所で先に Preparatory Refactoring を行うかどうか、大規模な移行を Branch by Abstraction の背後に置くかどうか。
成果物: docs/TECH-DEBT.md の ## Smell Inventory(コードスメル | 場所 | リファクタリング | ステータス)を拡張し、適用した名前付きリファクタリングとそのステータスをコードスメルごとに 1 行記録します。トラッカーを更新します。
完了条件: 対象のコードスメルに名前付きリファクタリングが適用され、ステータスが done / ticketed となっていること、テストが成功すること、構造の変更がすべて構造変更のみのコミットに含まれていること。
フェーズ 3 — コードに触れる箇所の可読性を高める(clean-code)
目的: すでに変更している部分で、名前、小さく単一の目的を持つ関数、安全なエラー処理を重視し、読み手にとってのコードのわかりやすさを高めます。
概要(フォールバック): コードは書くよりも読む機会のほうがはるかに多いものです(10:1 以上)。名前で意図を明確にします(d ではなく elapsedTimeInDays)。ブール値は述語として読めるようにし、同じ概念には同じ単語を使います。関数は一つの抽象度で一つのことだけを行い、引数は 0~2 個にします(フラグ引数は、実質的に 2 つの関数を意味します)。コマンド・クエリ分離:状態を変更するか値を返すかのどちらかにし、両方は行いません。レガシーコードに起因するインシデントはエラー処理に潜んでいます。戻り値コードより例外を優先し、具体的な型を捕捉し、null を返したり渡したりしないでください(空のコレクション、Optional、Null Object を使います)。扱いにくいサードパーティ API はアダプターでラップし、すべてのエラーに処理内容と状態を含めます。ボーイスカウト・ルールに従い、コードを見つけたときよりもきれいにして残します。
実行方法: 対象モジュールを指定して、clean-code スキルを実行します。6 つの規律それぞれについて 0~10 点で評価し、優先度順に上位 10 件の修正を挙げ、エラー処理を監査するよう依頼します(素の catch、null の返却、コンテキストのないエラー、ラップされていないサードパーティ SDK)。
ユーザーと決めること: 今すぐ適用する修正と技術的負債として記録する修正、そしてチームが今後採用する命名規則とエラー処理規約。
成果物: docs/TECH-DEBT.md を拡張します。名前・関数・エラーに関するコードスメルごとに ## Smell Inventory の行を追加し、合意したルールを ## Adopted Conventions に記録します。トラッカーを更新します。
完了条件: モジュールの評価が 8 点以上であること。または、8 点未満の項目がすべて修正内容付きの Smell Inventory 行として記録されていること。規約が記録され、フェーズ 1 のテストが引き続き成功すること。
フェーズ 4 — 深いモジュールで複雑さを軽減する(software-design-philosophy)
目的: 複雑さそのものに取り組みます。監督のないエージェントが生み出すクラス過多ではなく、実際の仕組みをシンプルなインターフェースの背後に隠します。
概要(フォールバック): 複雑さは敵です。変更のたびに、全体的な複雑さが増すか減るかを基準に判断します。兆候として、変更の波及、認知負荷、未知の未知があります。モジュールの深さ = 機能 ÷ インターフェースの複雑さです。深いモジュールは小さなインターフェースの背後に仕組みを隠しますが、浅いモジュールはそうしません(クラス過多)。常に一緒に扱われる浅いクラスは統合します。情報漏洩(1 つの設計判断が複数のモジュールに反映されること)と、時間的分割(知識ではなく実行順序に基づいて構成すること)に注意します。これは戦術から戦略への転換です。設計をクリーンに保つために 10~20%を投資します。
呼び出し: これまでに変更したモジュール群を対象に、software-design-philosophy スキルを使用します。どのクラスが浅いか、設計判断がどこでモジュール間に漏れているか、よりシンプルなインターフェースを備えた深いモジュールへどのように統合できるかを尋ね、各変更に複雑さを増すか減らすかを明記します。
ユーザーと決定する内容: 今回統合するものと先送りするものを決め、関係のない懸念事項まで統合しすぎないようにします。
成果物: docs/TECH-DEBT.md の ## Smell Inventory に、クラス過多/浅いモジュール/情報漏洩の項目と、適用した統合を追記します。トラッカーを更新します。
完了条件: 浅いモジュールの各クラスターが統合されているか、修正内容とともに記録されており、「モジュール性」のためだけにインターフェース数が増えておらず、テストがすべて成功していること。
フェーズ 5 — 依存関係の境界を定める(clean-architecture)
目的: フレームワークとデータベースが、逆ではなく、モジュールごとにビジネスルールへ依存するようにします。
概要(フォールバック): 依存関係ルールでは、ソースコードの依存関係は内側を向きます。エンティティ、ユースケース、インターフェースアダプター、フレームワーク/ドライバーの順です。内側の要素から外側の要素を参照してはいけません。データベースとウェブは詳細にすぎず、ルールに対するプラグインです。依存関係逆転を適用してこれを徹底します。ユースケースがリポジトリーインターフェースを所有し、Postgres/Stripe の実装は外側のアダプターに配置します。SOLID は中レベルのツールであり、共通閉包の原則と非巡回依存関係の原則によって、実際の境界を見つけます。1 つのデータモデルを共有するマイクロサービスは分散モノリスです。まずモノリス内部にこのルールを適用します。
呼び出し: 現在のモジュールマップとインテークで確認した技術スタックを使い、clean-architecture スキルを使用します。依存関係グラフをマッピングし、ビジネスロジックが ORM またはフレームワークをインポートしている違反をすべて列挙し、最も変更頻度の高いモジュールから着手して、所有するインターフェースの背後にフレームワークに依存しないユースケースを抽出する方法を提示するよう依頼します。
ユーザーと決定する内容: 今回どこまで境界を推し進めるか、どのベンダー(決済、ストレージ)を最初にラップするか、提案されたサービス分割が本当の境界なのか、単に分散モノリスを追加するだけなのかを決めます。
成果物: docs/ARCHITECTURE.md の ## Layer Map & Dependency Rule にレイヤーと違反を記録します(違反 | 場所 | 修正 | ステータス)。境界に関する選択肢は ## Decision Log に記録します。トラッカーを更新します。
完了条件: 依存関係ルールへの違反がすべて、修正内容を含む追跡対象の行として記録されており、リスクが最も高いベンダーを少なくとも 1 つラップし、ビジネスルールのテストをフレームワークなしで実行でき、テストがすべて成功していること。
フェーズ 6 — 習慣を定着させる(pragmatic-programmer)
目的: この取り組みが終わった後、コードベースに技術的負債が再びひそかに蓄積するのを防ぐメタ原則を定めます。
概要(フォールバック): 割れ窓理論:修正されていないハックがひとつあると、次のハックに対する基準が下がります。すぐに修正するか、追跡可能なチケットを作って覆い隠します。追跡されない // TODO は決して残しません。DRY が対象とするのはテキストではなく知識です。同じルールが2か所にある場合(クライアントとサーバーでのバリデーションなど)は重複をなくし、偶然似ているだけのものはそのままにします。直交性:あるコンポーネントを変更しても、ほかのコンポーネントに影響しないようにします。可逆性:ベンダーは独自のインターフェースの背後にラップします。契約による設計と早期クラッシュ:堅牢化された境界で前提条件と不変条件をチェックし、無効な状態が発生源で明確に失敗するようにします。
実行: コードベース全体で pragmatic-programmer スキルを使用します。偶然の重複は除外して重複した知識を洗い出し、覆い隠すべき割れ窓と追跡されていない TODO、および契約による設計のガード節が必要な境界を特定するよう依頼します。
ユーザーと決めること: 各イテレーションの負債予算と割れ窓への対応方針(すぐ修正するものとチケット化するもの)。
成果物: docs/TECH-DEBT.md を拡張します。## Debt Ledger に重複した知識と割れ窓の項目を記録し、合意した方針を ## Debt Budget & Broken-Windows Policy と ## Adopted Conventions に記載します。トラッカーを更新します。
完了条件: 重複した知識が台帳に記録されているか修正済みであり、追跡されていないハックが残っておらず、負債予算の方針が文書化されていること。
フェーズ 7 — 統合ポイントを堅牢化する(release-it)
目的: すべての統合ポイントを適切に縮退させ、依存関係の遅延や障害によってシステム全体が停止しないようにします。
概要(フォールバック): QA を通過したソフトウェアが、そのまま本番環境で生き残るとは限りません。統合ポイントは最も大きな障害要因です。応答が遅いのは、応答がないよりも悪影響を及ぼします。必須事項:すべての外向きの呼び出しに接続タイムアウトと読み取りタイムアウトを設定します。障害が発生する依存関係にはサーキットブレーカーを設けます(オープン状態に移行し、即時に失敗させ、ハーフオープン状態で復旧を試みます)。バルクヘッドでリソースプールを分離します。指数バックオフとジッターを使ってリトライします。蓄積する不要物を定常状態でクリーンアップします。すべてのクエリに上限を設けます。上限のない結果セットは大規模環境でクラッシュするため、LIMIT とページネーションを追加します。フィーチャーフラグと拡張・縮小マイグレーションを使ってデプロイとリリースを分離し、詳細なヘルスチェック、RED メトリクス、症状ベースのアラートを追加します。
実行: インテークで特定した外部依存関係を使って release-it スキルを実行します。タイムアウトのない呼び出し、上限のないクエリとリストエンドポイント、サーキットブレーカーとバルクヘッドの配置、リスクの高いスキーマに対する拡張・縮小マイグレーション計画、詳細なヘルスチェックと RED メトリクス、アラート設計を監査するよう依頼します。
ユーザーと決めること: サーキットブレーカーのしきい値、専用プールを割り当てる依存関係、アラート対象の症状としきい値(エラー率、レイテンシ)。
成果物: docs/RELIABILITY.md を拡張し、## Integration-Point Audit(依存関係 | タイムアウト | サーキットブレーカー | バルクヘッド | リトライポリシー | ステータス)、## Query & Resource Findings、## Health Checks & Metrics、## Deploy vs Release を追加します。トラッカーを更新します。
完了条件: すべての外向きの呼び出しにタイムアウトが設定され、重要な依存関係にサーキットブレーカーとバルクヘッドがあり、上限のないクエリに上限が設定され、詳細なヘルスチェックと RED メトリクス、症状ベースのアラートが用意され、重要なパスに監査上の未解決項目がないこと。
フェーズ 8 — 境界づけられたコンテキストに分割する(domain-driven-design)
目的: 大きなスパゲッティ状態を、チームが所有し、最終的に切り出せるコンテキストに分解します。書き直しは不要です。
概要(代替案): モデルはコードそのものです。まず、ユビキタス言語から始めます。技術的な名前しか付いていないもの(DataManager、Helper)をドメイン用語に変更します。名前を付けにくい概念は、モデルが間違っているサインです。既存のものを起点に、チームの境界に合わせて境界づけられたコンテキスト(同じ言葉でも、コンテキストによって意味が異なることがあります)をマッピングします。腐敗防止層を使うと、クリーンな新しいコンテキストが、古いモデルを持ち込まずにレガシーコアと連携できます。これがストラングラーフィグの基盤です。コンテキスト内では、単一のルートを持つ小さな集約を作り、他の集約は ID で参照し、不変の値オブジェクトと過去形のドメインイベントを使います。コアドメインに最も力を注ぎます。
実行: 絡み合ったモジュールとフェーズ 5 で定めた境界を使って、domain-driven-design スキルを実行します。ユビキタス言語の作成、現在および目標の境界づけられたコンテキストのマッピング、新しいクリーンなコンテキスト向けの腐敗防止層の設計、そして巨大な集約があれば本来の整合性境界まで縮小するよう依頼します。
ユーザーと一緒に決定すること: 最初に分割するコンテキスト(価値が生まれるコアドメイン)、チーム構造に合わせる境界、コンテキスト間の呼び出しをドメインイベントにするかどうか。
成果物: docs/ARCHITECTURE.md を拡張します。マップを ## Bounded Contexts & Context Map に、用語を ## Domain Glossary (Ubiquitous Language) に、分解の判断を ## Decision Log に記録します。トラッカーを更新します。
完了条件: 現在のコンテキストマップが描かれ、最初の目標コンテキストとその腐敗防止層が定義され、主要なドメイン用語が用語集に記載され、再構成した集約があればテストがすべて成功していること。
オプションのフェーズ
| スキル | 追加する条件 | 成果物 |
|---|---|---|
| system-design | 負債の返済により、再アーキテクチャが必要な実際のスケーリング上の限界が明らかになった場合 | docs/ARCHITECTURE.md を拡張(## System Context) |
| ddia-systems | 分離、レプリケーション、ストレージの適合性など、データ層の判断が負債の一部である場合 | docs/ARCHITECTURE.md を拡張(## Data & Storage Decisions) |
| team-topologies | チームの境界がアーキテクチャと衝突する箇所に負債が集中している場合 | docs/OPERATIONS.md を拡張(## Team Structure) |
オプションのフェーズにも同じ運用ルールを適用します。各スキルを一覧どおりに読み込み、コアフェーズと同様に使用します。「追加する条件」が初めて満たされた時点で挿入します。スケーリングとデータのフェーズはフェーズ 5 の後に、チームトポロジーのフェーズはフェーズ 8 のコンテキスト境界と並行して実施します。
よくある間違い
| 間違い | 修正方法 |
|---|---|
| 一気にすべて書き直すことを提案する | その場で少しずつ負債を返済すると決めます。どのフェーズでも、システムを改善し、リリースを継続できる状態を保ちます。最悪の場合でも、すばやく元に戻せます。まず、小さく迅速に得られる成果を一つ示します。 |
| テストを一つも書かずにクリーンアップする | まずフェーズ 1(working-with-legacy-code)で特性テストを使って動作を固定します。カバレッジは実際に変更する経路に沿って追加します。 |
| すべてを一度に修正しようとする | 3 軸ヒューリスティックで優先順位を付けます。変更頻度が高く、コアドメインがある箇所に投資します。一度限りの箇所には、小さな修正を加えるにとどめ、リファクタリングはしません。 |
| エージェントに細かすぎるクラスへの「モジュール化」を許す | software-design-philosophy の深いモジュールのルールを守らせます。一緒に使われる浅いクラスは統合します。それはクラス過多であり、アーキテクチャではありません。 |
| タイムアウトなしで外部サービスを呼び出す | フェーズ 7(release-it)では、すべての外部への呼び出しに接続タイムアウトと読み取りタイムアウトを追加し、重要な依存関係にはサーキットブレーカーも追加します。 |
| マイクロサービスをアーキテクチャと取り違える | まず依存関係ルールを適用し、モノリス内にある本当の境界づけられたコンテキストを見つけます(clean-architecture、domain-driven-design)。1 つのデータベースを共有するサービスは分散モノリスです。 |
旅の完了
終了チェックリスト — 各項目は成果物に紐づいています。
- 変更したモジュールには、正常に実行できる特性テストがあります(対象としたパスについて、TESTING.md の Safety Net Map が完成しています)。
- すべての外部呼び出しにタイムアウトが設定され、重要な依存関係にはサーキットブレーカーとバルクヘッドがあります(RELIABILITY.md の Integration-Point Audit に問題がありません)。
- 再設計したモジュールでは依存性ルールが守られています — ビジネスロジックはフレームワークや ORM をインポートしません(ARCHITECTURE.md の Layer Map で違反が解消されています)。
- モノリスには最新のコンテキストマップがあり、アンチコラプションレイヤーの背後に少なくとも 1 つのクリーンなコンテキストがあります(ARCHITECTURE.md の Bounded Contexts & Context Map)。
- 未追跡の応急処置は残っていません。負債予算と割れ窓ポリシーが文書化されています(TECH-DEBT.md の Debt Budget & Broken-Windows Policy)。
トラッカーを締めくくります。各フェーズを done または skipped: reason にし、残りの Next Actions を TECH-DEBT.md の Debt Ledger に引き継いで、何も失われないようにします。その後、次の作業に進みます。出発点がまだ新しいプロトタイプで、この道のりを本番環境への対応版として進めたい場合は、improve-code-quality スキルを続けてください。コードの健全性が回復し、次は製品体験に取り組む場合は、improve-app スキルを続けてください。