mcpField
I
improve-code-quality

improve-code-quality

github.com/wondelai開発2.5k2.3k 回インストール7 週間前に更新
リポジトリ

improve-code-quality について

動作するプロトタイプの改善を、テストから本番の信頼性やデータ設計まで9段階で案内する、対話型で再開可能なスキルです。決定事項と成果物をプロジェクトの `docs/` に記録します。

主な機能

  1. 01順序に沿った9段階の改善
  2. 02ユーザー確認を伴う対話型の決定
  3. 03再開可能な計画と進捗管理
  4. 04プロジェクトの `docs/` に記録
  5. 05テストによる安全網を必須化
  6. 06構造変更と動作変更を分離

活用シーン

  1. 01レガシーコードの変更前に特性テストを追加
  2. 02リファクタリングと設計改善を計画
  3. 03プロトタイプを本番運用に備える
  4. 04複数セッションにわたり改善計画を継続

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wondelai/skills --skill improve-code-quality

インストール後、Claude Code で/improve-code-qualityを入力して読み込まれたか確認します。

同梱ファイルreferences/artifact-templates.md

詳細

コード品質の改善

動作はするもののテストされていない、雰囲気で書かれたプロトタイプを、リリースして運用できるプロダクトへと仕上げます。これは9つのフェーズからなる、対話形式でいつでも再開できる取り組みです。エージェントはあらゆる判断の前に確認し、結果をプロジェクトの docs/ フォルダーに記録するため、どのフェーズで止めても後から再開できます。1週間前のプロトタイプも、すでにレガシーコードです。最初に取り組むべきはセーフティネットの整備であり、そのおかげで以降のすべてのフェーズを検証できます。

基本原則

テストされていない1週間前のプロトタイプは、すでにレガシーコードです。場当たり的な対応から戦略的な取り組みへと切り替え、まずセーフティネットを整備し、その後に可読性、構造、本番環境への対応の順に進めます。 このスキルはフェーズの順序を定め、判断のための質問を行い、すべての決定を docs/ に記録します。各フェーズを構成するスキルが手法を担うため、独自に枠組みを考えるのではなく、それらを呼び出してください。先に進みすぎること(テストの前にリファクタリングする、規模を見積もる前にスケールさせる)は、この順序によって防ごうとしている、まさにその失敗パターンです。

取り組みの全体像

フェーズスキル解決する問い成果物
1working-with-legacy-code気づかないうちに壊すことなく、このコードを変更できますか?docs/TESTING.md + docs/TECH-DEBT.md を作成 — ゲート
2clean-code次の人(そしてエージェント)にも読みやすいコードですか?docs/TECH-DEBT.md を拡充
3refactoring-patterns振る舞いを変えずに構造を組み替えられますか?docs/TECH-DEBT.md を拡充
4software-design-philosophy複雑さは、奥行きのあるモジュールの背後に隠されていますか?docs/TECH-DEBT.md を拡充
5clean-architectureビジネスルールはフレームワークに依存していますか、それともその逆ですか?docs/ARCHITECTURE.md を拡充
6pragmatic-programmer取り組みを終えた後も、コードをクリーンに保つにはどんな習慣が必要ですか?docs/TECH-DEBT.md を拡充
7release-it過酷な本番環境に耐えられますか?docs/RELIABILITY.md を作成
8system-design実際の負荷に見合った規模になっていますか?docs/ARCHITECTURE.md + docs/RELIABILITY.md を拡充
9ddia-systemsデータ層は、並行処理下でも正しく、永続性がありますか?docs/ARCHITECTURE.md を拡充

運用ルール

  1. まず再開します。 何よりも先に、docs/IMPROVE-CODE-QUALITY-PLAN.md と Journey Map 内のすべての成果物を読みます。トラッカーが存在する場合は、ジャーニーの状態を3~5行で要約し、どのフェーズに進むか尋ねます。ユーザーが開始地点を確認したら完了です。トラッカーがあるジャーニーは再開し、最初からやり直すことはありません。
  2. 初回のみインテークを行います。 トラッカーがない場合は、以下のインテークを実施してから、各フェーズのステータスを pending | in-progress | awaiting-evidence | done | deferred: reason | skipped: reason のいずれかにした docs/IMPROVE-CODE-QUALITY-PLAN.md を作成します。トラッカーが存在し、ユーザーがフェーズ計画を確認したら完了です。
  3. フェーズへの着手。 フェーズで行うこと、必要となる判断、作成する成果物、おおよその作業量を伝えます。続行/スキップ/延期の選択肢を提示します。GATE と記されたフェーズは延期できますが、スキップはできません。続行する場合は、フェーズを in-progress にします。ユーザーが選択したら完了です。
  4. スキルの呼び出しと代替手段。 フェーズのスキルを読み込み、使用します。各フェーズの Invoke 行にはスラッグでスキルが指定されています。そのスキルを使ってフェーズを実行します。利用できない場合は、npx skills add wondelai/skills/<slug> --global を提示します。ユーザーが断った場合は、Brief に基づいてフェーズを実行します。どちらの方法で進めているかを伝えます。
  5. フェーズ内の判断。 「Decide with the user」の下にあるすべての質問を、具体的な選択肢と推奨案とともに尋ねます。選択内容をトラッカーの Key Decisions に記録します。ユーザーに確認せず決定することは不備です。
  6. フェーズの終了。 書き込む前に、成果物のドラフト内容を提示して承認を得ます。承認後、docs/ ファイルを作成または更新し、トラッカーのステータス、Key Decisions、Next Actions を更新します。ファイルが書き込まれ、フェーズの行が done になったら完了です。
  7. 成果物の取り扱い。 書き込む前に読み込みます。ファイルがない場合にのみ作成し、既存の場合は他の人の内容を保持しながら、自分のセクションを追加または更新します。ファイル名は docs/ 内で大文字にします。すべての推奨事項は、担当者と優先度を含むチェックボックスまたは表の行として記録します。docs/ ファイルを初めて作成する際は、references/artifact-templates.md を参照し、すべてのセクション見出しを含む完全なひな形からファイルを作成してから、フェーズで指定されたセクションに記入します。
  8. フェーズ1はゲートです。コミットは単一目的にします。 Safety Net Map に記載されていないコードには、どのフェーズでも触れません。まず対象を登録してください。Safety Net Map の Pinned behaviors に記載されていないものは未登録とみなします。Gaps 列の項目も変更対象外です。構造の変更と動作の変更を同じコミットに含めてはいけません。テストがすべて成功する状態で、構造のみを変更するリファクタリングをコミットしてから、動作の変更を別のコミットにします。リファクタリング中にテストが失敗したら、デバッグするのではなく、変更を元に戻して、より小さな手順でやり直します。Safety Net のテスト追加と docs/ の更新も、それぞれ単一目的のコミットにします。

受付時の確認事項

トラッカーを作成する前に、以下を確認します。

  1. アプリは何をするもので、壊れた場合に起こり得る最悪の事態は何ですか?(リスクを把握し、フェーズの優先順位を決めます)
  2. 次に変更するモジュールはどれですか。また、変更頻度(git log)が最も高いモジュール、またはドメインの中核となるモジュールはどれですか?(フェーズ 1 の開始モジュールを選ぶための三軸ヒューリスティックです)
  3. 現在、自動テストはありますか。また、テストコマンドは成功しますか?(フェーズ 1 のセーフティネットの範囲を決めます)
  4. 技術スタックは何ですか?フレームワーク、ORM、データベースを教えてください。(フェーズ 5 と 9 の実施可否を判断します。境界とデータに関する判断に必要です)
  5. 実運用中で、実際のユーザーデータを扱っていますか。また、アクティブユーザー数やリクエスト数はおよそどのくらいですか?(フェーズ 7〜9 の実施可否を判断します。レジリエンスは要件に基づいて決めます)
  6. 呼び出している外部依存先は何ですか?サードパーティ API、決済、メール、キューなどを教えてください。(フェーズ 7 の実施可否を判断します。統合ポイントを監査します)
  7. 今はどこまで進めたいですか?(実ユーザーを迎える前にフェーズ 1〜3、ローンチ前にフェーズ 7、フェーズ 8〜9 は実際の成長に合わせて進めます)

フェーズをスキップする際のヒューリスティック:実際の負荷があらゆるスケーリングのしきい値を大きく下回っている場合は、フェーズ 8〜9 をスキップします(解決策ではなく要件から始めます。ユーザー数が 50 人の段階で 5 万人向けに構築しないでください)。実ユーザーがいる場合、フェーズ 7 は任意ではありません。トラフィックが少なくても、タイムアウトとサーキットブレーカーは最低限必要です(deferred: reason のままにすることはできますが、skipped にはしないでください)。フェーズ 1 は決してスキップしないでください。ここがゲートです。その後、テンプレートからトラッカーを作成し、計画を確認します。

docs/IMPROVE-CODE-QUALITY-PLAN.md が存在し、すべてのフェーズのステータスが設定され、ユーザーが計画を確認したら完了です。

フェーズ

フェーズは記載順に進めます。各フェーズは、前のフェーズの成果物が存在することを前提とします。運用ルールに従い、どのフェーズも開始、スキップ、または延期できますが、フェーズ 1 はすべてのゲートです。下流のフェーズでは、ピン留めされていないコードには触れません。

フェーズ 1 — セーフティネットを構築する(working-with-legacy-code)— GATE

目的: 変更箇所における現在の動作をピン留めし、後続のすべてのフェーズで検証できるようにします。セーフティネットマップに記載されていないコードには、どのフェーズでも触れてはいけません。

概要(代替手順): レガシーコードとはテストのないコードのことなので、1 週間前に作ったプロトタイプも該当します。テストでカバーしてから変更し、編集して祈るだけにしてはいけません。変更箇所を特定し、最小限の侵襲で継ぎ目を作ってインライン依存関係を断ちます(本番環境向けのデフォルト値を指定したコンストラクターのパラメーター化、または埋め込まれた呼び出し 1 つに対する抽出とオーバーライド)。次に、実際の動作を写し取る特性テストを書きます。誤った値をアサートし、失敗を確認して、実際の値をピン留めします。時間内に十分なカバレッジを確保できない場合は、新しいコードを Sprout/Wrap で作成し、テストされていないホストコードを負債として記録します。

実行: 受付時に選んだ開始モジュールを使い、working-with-legacy-code スキルを使用します。エントリーメソッドからの影響の概要、継ぎ目、そして現在のサインアップ/課金/中核的な動作をピン留めするための最小限の特性テスト一式を求めます。

ユーザーと決めること: (1) 三軸ヒューリスティック(次に変更するもの、変更頻度の高さ、ドメインの中核)に基づいて、開始モジュールを確認します。(2) 特性テスト中に見つかったバグは、誤った動作をピン留めして負債台帳に記録します。黙って修正してはいけません。呼び出し元がその癖に依存している可能性があります。ユーザーがこれを了承するか確認します。

成果物: ## Test Strategy、## Safety Net Map(モジュール | ピン留めした動作 | テストファイル | 不足箇所)、## Characterization Backlog を含む docs/TESTING.md を作成します。## Debt Ledger(項目 | 場所 | 種類 | リスク | 工数 | 優先度 | ステータス)と ## Sprout / Wrap Register を含む docs/TECH-DEBT.md を作成します。トラッカーを更新します。

完了条件: 対象モジュールの動作がピン留めされ、テストスイートが成功し、両方のファイルが存在し、フェーズ 1 が done になったら完了です。後続のフェーズに進めるのは、その後に限ります。

フェーズ 2 — コードを読みやすくする(clean-code)

目的: 変更を検証できるようになったところで、読み手を意識して最適化します。名前、小さく単一の目的を持つ関数、安全なエラー処理を重視します。

概要(代替案): コードは書くよりも読む機会のほうがはるかに多いものです。名前で意図を明確にします(d ではなく elapsedTimeInDays)。ブール値は述語として読めるようにします。関数は、0〜2 個の引数で、抽象度を同じレベルに保ちながら 1 つのことだけを行います(フラグ引数は、2 つの関数に分けるべきです)。コマンド・クエリ分離:状態を変更するか値を返すかのどちらか一方にし、両方を行うことは避けます。エラー処理:戻り値のコードより例外を優先し、具体的な型を捕捉し、null を返したり渡したりしないでください(空のコレクション、Optional、または Null Object を使用します)。スローするすべてのエラーには、操作と状態のコンテキストを含めます。

実行: 対象モジュールを指定して clean-code スキルを使用します。6 つの分野について 0〜10 のスコアを付け、優先順位順に上位 10 件の修正を提示するよう依頼します。また、エラー処理(素の catch、null の返却、コンテキストのないエラー)を監査するよう依頼します。

ユーザーと決定する事項: 今すぐ適用する修正と技術的負債として記録する修正の区分、チームが今後採用する命名規則とエラー処理の規約、clean-code のスコアを CI ゲートにするかどうかを決めます。

成果物: docs/TECH-DEBT.md を拡張します。名前、関数、エラーに関する各スメルについて、## Smell Inventory に行(smell | location | refactoring | status)を追加し、合意したルールを ## Adopted Conventions に記録します。トラッカーを更新します。

完了条件: モジュールのスコアが 8 以上であるか、8 未満の各項目が修正内容とともに Smell Inventory に記録され、規約が記録され、フェーズ 1 のテストが引き続きパスすること。

フェーズ 3 — 名前付きリファクタリングを適用する(refactoring-patterns)

目的: 「きれいにする」を、動作を維持しながら小さな手順で実行する、名前の付いた変換に置き換えます。

概要(代替案): 各スメルを、名前の付いたリファクタリングに対応付けます。Extract Method は基本となる手法です。あるブロックを説明するコメントを書きたくなったら、そのブロックを抽出し、コメントに沿った名前を付けます。また、Replace Magic Number with Symbolic Constant、Replace Nested Conditional with Guard Clauses、Replace Conditional with Polymorphism、Introduce Parameter Object を使用します。ワークフローは、テストがパスした状態から始め、変換を 1 つ行い、テストをパスさせてコミットします。テストが失敗したら、デバッグするのではなく、変更を取り消します。Preparatory Refactoring(変更を容易にしてから、容易になった変更を行う)と Rule of Three によって、時期尚早な抽象化を防ぎます。

実行: スメルのある関数とフェーズ 1 のテストを使って refactoring-patterns スキルを使用します。各スメルに名前を付け、適用する変換を示し、各変換の間にテストを実行しながら、1 つずつ適用するよう依頼します。

ユーザーと決定する事項: 今回対応するスメルの範囲、今後追加予定の機能に備えて、挿入箇所で先に Preparatory Refactoring を行うかどうか、リファクタリング済みモジュールを TESTING.md の CI ゲート一覧に加えるかどうかを決めます。

成果物: docs/TECH-DEBT.md の ## Smell Inventory を拡張し、各スメルについて適用した名前付きリファクタリングとそのステータスを記録します。トラッカーを更新します。

完了条件: 対象のスメルに名前付きリファクタリングが記録され、ステータスが done または ticketed であり、テストがパスし、構造上の変更が構造のみのコミットとして取り込まれていること。

フェーズ 4 — ディープモジュールで複雑さを減らす(software-design-philosophy)

目的: 監督なしのエージェントが生み出すクラス過多に対処します。浅いクラスを増やすのではなく、シンプルなインターフェースの背後に実際の仕組みを隠します。

概要(フォールバック): 複雑さは敵です。モジュールがシステムの他の部分に課すものを最小限に抑えます。モジュールの深さ = 機能 ÷ インターフェースの複雑さです。ディープモジュールは小さなインターフェースの背後に仕組みを隠しますが、シャローモジュールはそうしません(クラス過多)。常に一緒に使われ、状態を共有するシャロークラスは統合します。情報漏洩(1 つの判断が複数のモジュールに反映されること)や、知識ではなく実行順序に基づいて構成する時間的分解に注意します。これは戦術から戦略への転換です。設計をクリーンに保つため、10〜20%の時間を投資します。

実行方法: これまでに扱ったモジュール群を対象に、software-design-philosophy スキルを使用します。どのクラスがシャローなのか、境界を越えて情報が漏れている箇所はどこか、よりシンプルなインターフェースを持つディープモジュールに統合する方法を尋ねます。

ユーザーと決定すること: 無関係な関心事を過度に統合しないよう注意しながら、どの統合を今行い、どれを先送りするかを決定します。

成果物: docs/TECH-DEBT.md の ## Smell Inventory に、クラス過多/シャローモジュール/情報漏洩の項目と、適用した統合内容を追記します。トラッカーを更新します。

完了条件: シャローモジュールの各クラスターが統合されるか、修正内容とともに記録されていること。「モジュール性」のためだけにインターフェース数を増やしていないこと。テストがすべて成功すること。

フェーズ 5 — アーキテクチャの境界を引く(clean-architecture)

目的: フレームワークとデータベースがビジネスルールに依存するようにし、その逆にならないようにします。

概要(フォールバック): 依存関係ルールでは、ソースコードの依存関係は内側を向きます。エンティティ、ユースケース、インターフェースアダプター、フレームワーク/ドライバーの順です。内側のものが外側のものの名前を参照することはありません。データベースと Web は詳細にすぎず、ルールに対するプラグインです。依存関係逆転によってこれを徹底します。ユースケースがリポジトリーインターフェースを所有し、Postgres/Stripe の実装は外側のアダプターに置きます。SOLID は中間レベルのツールです。同じデータモデルを共有するマイクロサービスは分散モノリスです。まずサービス内部にこのルールを適用します。

実行方法: 現在のモジュールマップとインテークで確認したスタックを使い、clean-architecture スキルを使用します。依存関係グラフを作成し、ビジネスロジックが ORM またはフレームワークをインポートしているすべての違反を列挙し、所有するインターフェースの背後にフレームワーク非依存のユースケースを抽出する方法を示すよう依頼します。

ユーザーと決定すること: 今回どこまで境界を強化するか、どのベンダー(決済、ストレージ)を最初に所有するインターフェースの背後に置くか、また、サービス内の境界が確立されるまで予定しているサービス分割を待つかどうかを決定します(分散モノリスを避けます)。

成果物: docs/ARCHITECTURE.md にレイヤーと現在の違反を ## Layer Map & Dependency Rule の下に記録します(違反 | 場所 | 修正 | ステータス)。境界に関する決定は ## Decision Log の下に記録します。トラッカーを更新します。

完了条件: 依存関係ルールへの違反がすべて、修正方法とともに追跡表に記録されていること。少なくとも最もリスクの高いベンダーがラップされていること。ビジネスルールのテストがフレームワークなしで実行でき、テストがすべて成功すること。

フェーズ 6 — 習慣を定着させる(pragmatic-programmer)

目的: この取り組みが終わった後もコードベースを変更可能な状態に保つ、メタ原則を定めます。

概要(代替案): DRY とはテキストではなく知識に関するものです。同じルールが 2 か所(クライアントとサーバーでの検証など)にあれば重複をなくし、たまたま似ているだけのものはそのままにします。直交性:あるコンポーネントの変更が別のコンポーネントに影響してはいけません。割れ窓理論:ハックはすぐに修正するか、追跡可能なチケットを作成して塞ぎます。追跡されない // TODO は決して残しません。可逆性:サードパーティベンダーは独自のインターフェースの背後に配置します。トレーサーバレット:次の機能はレイヤーごとにではなく、実際に動作する薄いエンドツーエンドのスライスとして構築します。

実行: コードベース全体で pragmatic-programmer スキルを使用します。たまたま重複しているものは除外して、重複した知識と、塞ぐ必要のある割れ窓や追跡されていない TODO を指摘するよう依頼します。

ユーザーと決定する事項: イテレーションごとの負債予算と割れ窓への対応方針。今すぐ修正するものとチケット化するものを決めます。

成果物: docs/TECH-DEBT.md を拡張します。## Debt Ledger に重複した知識と割れ窓の項目を記録し、合意した方針を ## Debt Budget & Broken-Windows Policy と ## Adopted Conventions に記載します。トラッカーを更新します。

完了条件: 重複した知識の検出事項が台帳に記録されるか修正済みで、追跡されていないハックが残っておらず、負債予算の方針が文書化されています。

フェーズ 7 — 本番環境での稼働に備える(release-it)

目的: すべての統合ポイントを強化し、依存関係が遅延したり失敗したりしても、アプリ全体を停止させずに適切に劣化するようにします。

概要(代替案): QA に合格したソフトウェアが、そのまま本番環境で生き残るとは限りません。統合ポイントは最大の障害要因です。応答が遅いより、応答がないほうがましです。必須事項:すべての外部呼び出しに接続タイムアウトと読み取りタイムアウトを設定すること。失敗する依存関係にはサーキットブレーカーを設けること(オープン状態に移行して迅速に失敗し、ハーフオープン状態で復旧を試みます)。リソースプールを分離するバルクヘッドを設けること。指数バックオフとジッターを伴うリトライを行うこと。蓄積する不要物を定常状態でクリーンアップすること。フィーチャーフラグと拡張・縮小マイグレーションを用いて、デプロイとリリースを分離します。詳細なヘルスチェック、RED メトリクス、症状ベースのアラートを追加します。

実行: 受付時に洗い出した外部依存関係を使って release-it スキルを実行します。タイムアウトのない呼び出し、サーキットブレーカーとバルクヘッドの配置、詳細なヘルスチェックと RED メトリクス、アラート設計の監査を依頼します。

ユーザーと決定する事項: ブレーカーのしきい値、専用プールを割り当てる依存関係、アラートの対象となる症状としきい値(エラー率、レイテンシー)。

成果物: docs/RELIABILITY.md を作成し、## Integration-Point Audit(dependency | timeout | circuit breaker | bulkhead | retry policy | status)、## Health Checks & Metrics、## Deploy vs Release を記載します。トラッカーを更新します。

完了条件: すべての外部呼び出しにタイムアウトが設定され、重要な依存関係にブレーカーとバルクヘッドが設けられ、詳細なヘルスチェック、RED メトリクス、症状ベースのアラートが存在し、蓄積する不要物の定常状態でのクリーンアップが予定され、再デプロイせずにリリースをロールバックでき、監査テーブルに重要な経路の未対応項目がない状態です。

フェーズ 8 — 実際の負荷に合わせて規模を設計する(system-design)

目的: 要件と数値に基づいて計画的にスケールし、場当たり的に対応しないことです。見積もりで正当化できる場合にのみ、仕組みを追加します。

概要(フォールバック): 解決策ではなく、要件から始めます。概算では、QPS = 1 日のアクティブユーザー数 × 1 日あたりのアクション数 ÷ 86,400、ピーク時は平均の 2~5 倍です。ストレージ容量 = 1 日あたりのレコード数 × サイズ × 保持期間です。スケールアップは、まず垂直スケーリングから始め、次に読み取り負荷の高いパスにキャッシュ(TTL と明示的な無効化を伴うキャッシュアサイド)を導入し、その後に読み取りレプリカを追加します。シャーディングは最後の手段にします。メッセージキューを使って、時間のかかる処理をリクエスト経路から切り離します。実績のある設計を活用します(レートリミッター → 429 Retry-After を返すトークンバケット)。

実行: インテークで得た負荷の実態をもとに、system-design スキルを使います。平均およびピーク時の QPS、年間のストレージ容量、最初にボトルネックとなるコンポーネント、そして過剰設計を避けた、キャッシュ/キュー/レプリカの優先順位付き導入案を確認します。

ユーザーと決定すること: 実際の数値に基づいて、今すぐ実施するスケーリング施策と先送りする施策を決めます(ユーザー数が 50 人の段階で 5 万人向けに構築しないでください)。また、該当する場合は、メッセージキューを介した処理に最初に移す負荷の高いワークロードを特定します。

成果物: docs/ARCHITECTURE.md の ## System Context に負荷の実態と概算値を追記します。docs/RELIABILITY.md の ## Query & Resource Findings に、上限のないクエリとボトルネックを追記します。トラッカーを更新します。

完了条件: QPS とストレージ容量の数値が記録され、最初のボトルネックが特定され、各スケーリング施策について、実施または先送りの判断と、その判断を切り替える数値が記録されていることです。

フェーズ 9 — データレイヤーを適切に設計する(ddia-systems)

目的: コードより長く存続するデータを守ることです。並行処理における正しさを確保し、慣例ではなく要件に基づいてデータストアを選択します。

概要(フォールバック): データはコードより長く存続します。ほとんどのデータベースのデフォルトは、直列化可能分離レベルではなく、読み取りコミット済みまたはスナップショットです。読み取り後に書き込む素朴な処理では、書き込みスキューが発生します(2 つのリクエストがどちらも最後の 1 個を販売する場合など)。SELECT ... FOR UPDATE または直列化可能トランザクションを明示的に使って修正し、実際のデフォルト設定を把握します。レプリカが存在する場合、レプリケーションの遅延があるため、書き込み後読み取りと単調読み取りを意図的に実現する必要があります。データモデルはアクセスパターンに合わせます。ポリグロットパーシステンスが適切な場合も多くあります。システムオブレコードと派生データは、二重書き込みではなく、CDC/イベントソーシングを使って分離します。

実行: インテークで把握したデータベースとフェーズ 8 のレプリカ計画をもとに、ddia-systems スキルを使います。書き込みスキューが起きやすい読み取り後書き込みのパスを特定し、実際のデフォルトの分離レベルと、それによって起きる異常を確認して、リスクのあるパスを修正するよう依頼します。

ユーザーと決定すること: ロックが必要なパスと、直列化可能トランザクションが必要なパスを決めます。また、新しいワークロード(検索、フィードなど)に、CDC で同期する 2 つ目のデータストアを導入する価値があるか判断します。

成果物: docs/ARCHITECTURE.md の ## Data & Storage Decisions に、分離レベル、ロック対象のパス、ポリグロットまたは派生データに関する選択を追記し、その判断理由を ## Decision Log に記録します。トラッカーを更新します。

完了条件: デフォルトの分離レベルが文書化され、書き込みスキューが起きやすいすべてのパスがロックされているか、直列化可能に設定されており、新しいデータストアがある場合は同期方法が定義されていることです。

オプションのフェーズ

スキル追加する条件成果物
domain-driven-designコードにドメイン言語がないために、ビジネスロジックが絡み合っているdocs/ARCHITECTURE.md を拡張します(## Bounded Contexts & Context Map、## Domain Glossary (Ubiquitous Language))

オプションのフェーズにも、同じ運用ルールが適用されます。記載された各スキルをコアフェーズと同様に読み込んで使用し、「追加する条件」が初めて満たされた時点で挿入します。この場合は、境界ができた後、フェーズ 5 の直後です。

よくある間違い

間違い修正方法
テストを1つも書かずにクリーンアップするまず Phase 1(working-with-legacy-code)で特性テストを使って動作を固定します。安全網が後続のすべてのフェーズの前提となります。
エージェントに「モジュール化」させ、細かなクラスを大量に作るsoftware-design-philosophy のディープモジュールの原則を適用します。一緒に使われる浅いクラスを統合し、インターフェースを増やすのではなく減らします。
タイムアウトなしで外部 API を呼び出すPhase 7(release-it)で、すべてのアウトバウンド呼び出しに接続タイムアウトと読み取りタイムアウトを設定し、重要な依存関係にはサーキットブレーカーを追加します。
規模を見積もる前にスケールさせるまず概算見積もりを行います(system-design)。通常、キャッシュと読み取りレプリカだけで何年も対応できると数値が示します。
マイクロサービスをアーキテクチャと取り違えるまずサービス内で依存関係ルールを適用します(clean-architecture)。1つのデータモデルを共有するサービスは分散モノリスです。
データベースがシリアライズ可能だと思い込む実際のデフォルト分離レベルを確認し、読み取り後に書き込む経路をロックします(ddia-systems)。書き込みスキューは、単一ユーザーのテストではすべて見逃されます。

道のりを完了する

一度にすべてではなく、順番にリリースします。Phase 1~3(net、readability、refactoring)は、実ユーザーが来る前に実施します。Phase 5 の境界は、コードベースが再び倍増する前に最も効果を発揮します。リリース前に Phase 7 を強化します(トラフィックが少なくても、タイムアウトとサーキットブレーカーは必須です)。Phase 8~9 は、実際の成長数値に合わせて進めます。

完了チェックリスト — 各項目は成果物に紐付いています。

  • ビジネスルールには、データベースやフレームワークなしで実行できるテストがあります(変更したモジュールについて、TESTING.md の Safety Net Map が完成していること)。
  • すべてのアウトバウンド呼び出しにタイムアウトがあり、重要な依存関係にはサーキットブレーカーがあり、症状ベースのアラートにディープヘルスチェックと RED メトリクスが接続されています(RELIABILITY.md の Integration-Point Audit に未解決事項がなく、Health Checks & Metrics が記入されていること)。
  • 依存関係ルールが守られています。ビジネスロジックはフレームワークや ORM をインポートしません(ARCHITECTURE.md の Layer Map が完成し、違反が解消されていること)。
  • データベースの分離レベルが把握され、読み取り後に書き込む経路がロックされています(ARCHITECTURE.md の Data & Storage Decisions)。
  • 未追跡のハックは残っていません。先送りした項目はすべて、優先度を付けて Debt Ledger に記録されています(TECH-DEBT.md)。

トラッカーを締めくくります。すべてのフェーズを done または skipped: reason とし、残りの Next Actions は TECH-DEBT.md の Debt Ledger に引き継いで、何も失われないようにします。その後、次に進みます。コードベースがまだ若く雑然としている段階ではなく、古く大規模になっている場合は、remove-technical-debt スキルを続けて使用します。次のシステムで最初から意図的に構造を設計する必要がある場合は、design-code-architecture スキルを続けて使用します。

コメント