
Summary
この文書の要点
- 安全を『触らないこと』ではなく、変更の失敗を早く検知し、復元できることとして定義します。
- 最初にテスト、監視、段階リリース、ロールバックを整え、変更の影響範囲を小さくします。
- ボーイスカウト・ルールは、触った範囲だけを小さく改善し、無関係な整理を差分へ混ぜないことが要点です。
- 放置による余計な工数と復旧時間を計測し、負債返済を日々の開発計画へ組み込みます。
『触らない』ことも、時間とともにリスクへ変わります
稼働中のシステムに大きな変更を加えるのが怖いのは、自然な反応です。テストが少ない、依存関係が見えない、手動作業が多い、障害時にどこまで戻せるかわからない。この状態で大規模なリファクタリングを始めれば、変更そのものが障害の原因になり得ます。だから最初に、変更を止めるのではなく、変更の失敗を小さくする仕組みへ投資します。
現状維持が安全なのは、周辺の条件が変わらない場合だけです。依存ライブラリのサポート期限、OSや実行環境の更新、データ量、利用者の増加、担当者の交代は、コードを触らなくても進みます。変更しない判断はリスクを消すのではなく、見えにくい場所へ移し、後から一度に支払う形へ変えていることがあります。
ここでいう技術的負債は、コードが古いという意味に限りません。変更の影響を予測しにくい、失敗を検知しにくい、復元手順を実行できない、判断の根拠を説明できない、といった保守上の制約も含みます。4つのステップでは、この制約を一度に解消せず、失敗時の被害と調査時間を順番に下げます。
- 触らない判断が成立する条件と、時間経過で変わる条件を分ける。
- コードの品質だけでなく、検知・復元・説明のしやすさを負債として扱う。
- 大規模な刷新より、変更の失敗を小さくする土台から作る。
Step 1|コードより先に『壊れても大丈夫な仕組み』を作る
最初から全機能のテストを網羅する必要はありません。まずは、利用者にとって重要な入口と、過去に壊れた経路を数本選びます。注文や申請の登録、ログイン、検索、外部連携など、失敗した時の影響が大きく、結果を機械的に確認できる経路から、再現テストを置きます。テストが難しい箇所は、入力と出力の記録、データの件数、外部呼び出しの回数など、観測できる事実を先に固定します。
CIでは、速いチェックと重いチェックを分けます。フォーマット、型チェック、単体テストは変更のたびに実行し、契約テストや重要な画面のE2Eテストは段階的に加えます。全量テストが長すぎて誰も待たない状態なら、変更影響に応じた選択テストを高速経路にし、mainや定期実行では全量テストを続けます。速さのために検証を捨てるのではなく、検証を目的別に分ける設計です。
配信も一度に全利用者へ広げません。カナリアリリースやフィーチャーフラグを使い、少数のトラフィックで新旧の挙動を比較します。エラー率、レイテンシ、処理件数、重要な業務結果を観測し、決めた条件を超えたら自動または手動で旧版へ戻します。『戻せる』とは、前のコンテナを用意することだけではありません。戻す判断の指標、担当者、実行手順、確認方法まで含みます。
- 重要な経路から再現テストと観測項目を作る。
- CIを速いチェック、影響範囲のテスト、全量テストに分ける。
- カナリアの拡大条件とロールバック条件を、変更前に決める。
- DB変更は後方互換な追加、データ移行、不要項目の削除を分ける。
before_change:
observe: [error_rate, latency_p95, key_business_result]
verify: [typecheck, unit_test, critical_path_smoke]
rollout: [canary, feature_flag]
rollback_when:
- error_rate exceeds baseline + threshold
- latency_p95 exceeds agreed limit
- critical_business_result becomes invalid
owner: named_operator
evidence: saved_logs_and_test_reportStep 2|ボーイスカウト・ルールを、差分の境界つきで続ける
全ファイルを一気にきれいにする計画は、技術的には正しくても、運用上は失敗しやすい計画です。変更範囲が広がるほど、レビューの判断材料が増え、競合が起き、何が原因で挙動が変わったのか追いにくくなります。ボーイスカウト・ルールの要点は、触った場所を少し改善して戻すことです。『ついでに全部直す』とは違います。
実務では、差分の予算を先に決めます。たとえば、対象は作業で触るファイルに限定し、命名の修正、重複した小さな処理の抽出、失敗時のログ追加、テストの追加を一つ選びます。整形だけの大量差分は別のコミットへ分け、動作変更と混ぜません。レビューで『この変更で何が安全になったか』を説明できる粒度を保つことが重要です。
改善の順番は、境界の外へ影響しにくいものから始めます。publicな入出力を変えずに内部の責務を分ける、暗黙の設定を明示する、例外を握りつぶさず記録する、という作業は比較的安全です。一方、データモデル、認証、権限、外部連携の契約を同時に変える作業は、ボーイスカウト・ルールだけで済ませず、別の設計・移行計画として扱います。
- 触ったファイルだけを対象にし、改善項目を一つか二つに絞る。
- 動作変更、整形、名前変更はコミットを分ける。
- 公開契約、認証、権限、DB削除は別の設計変更として扱う。
- レビューで安全になった点と、まだ残るリスクを説明する。
touched_files_only = true
choose_one:
- add_characterization_test
- make_hidden_dependency_explicit
- separate_one_responsibility
- improve_failure_observability
- remove_one_duplicate_branch
keep_out_of_this_change:
- unrelated_formatting
- public_contract_change
- database_destructive_migration
- broad_directory_renameStep 3|『直さないリスク』を負債の利息として可視化する
技術的負債の返済が後回しになるのは、改善の価値がないからではありません。多くの場合、放置した時に毎回発生している余計な作業が、別々の出来事として記録されているからです。調査にかかった時間、レビューで確認した時間、手動で復旧した時間、担当者に聞き直した時間を、障害だけでなく通常の開発でも記録します。
指標は、コードの美しさを直接測るものより、意思決定に使えるものを選びます。変更1件あたりの調査時間、レビューの往復回数、リリース後の手動確認、ロールバックにかかる時間、同じ箇所で発生した不具合、担当者が不在の時に止まった時間などです。これらを月単位で集計すると、負債がどれだけ利息を生んでいるかを説明できます。
たとえば、変更ごとの追加確認時間に件数を掛け、障害時の復旧時間と手動作業の時間を加えます。正確な金額を最初から出せなくても、まずは時間で構いません。『触らない方が安全』という意見に対して、『触らないことで毎月何時間を使い続けているか』『どの条件なら小さな改善の方が安いか』を同じ尺度で話せるようにします。
- 調査、レビュー、配信、復旧、引き継ぎの余計な時間を分けて記録する。
- 平均だけでなく、繁忙期や障害時の最大値も見る。
- 負債の大きさではなく、利息と失敗時の影響で優先順位を決める。
- 精密な金額より、継続して比較できる同じ計測方法を優先する。
monthly_interest =
extra_investigation_hours
+ review_rework_hours
+ manual_release_hours
+ recovery_hours
+ handover_hours
decide_priority_by:
- monthly_interest
- failure_impact
- change_frequency
- reversibility_after_improvementStep 4|安全の定義を『慎重さ』から『回復力』へ更新する
安全を『壊さないこと』だけで定義すると、変更を避けるほど安全に見えます。しかし、複雑なシステムでは、すべての失敗を事前に予測することはできません。安全の基準を、失敗しないことに加えて、失敗を早く見つけ、影響を閉じ込め、前の状態へ戻し、次の改善へ反映できることへ広げます。
回復力は、監視画面を置くだけでは生まれません。タイムアウトと再試行の上限、外部連携の切断、キューの滞留、起動と終了の確認、データの整合性、手動切り戻しの手順を、実際に試します。復元手順を一度も実行していないなら、それは手順書であって、まだ回復能力ではありません。
特にDB変更は、アプリケーションのロールバックと同じようには戻せません。新しい列を先に追加し、旧版でも読める状態を保ち、データを段階的に埋め、利用状況を確認してから旧列を廃止します。前方互換と後方互換を分けて設計することで、アプリケーションだけを戻してもデータを壊さない境界を作れます。
- 監視、ログ、アラート、切り戻しを一つの運用手順としてつなぐ。
- タイムアウト、再試行、キュー、外部連携の失敗時に被害を閉じ込める。
- 復元訓練や障害時の演習で、手順が実行可能か確認する。
- DB変更は追加・移行・削除を分離し、アプリの切り戻しと両立させる。
4つのステップを、日常の開発へ組み込みます
4つのステップは、先に大きな改善プロジェクトを完成させてから開発へ戻る手順ではありません。まず一つの重要な経路で、失敗を検知するテスト、配信後に見る指標、戻す手順を作ります。次の変更でその経路に触れた人が、小さな改善を一つ加えます。その後、余計にかかった時間を計測し、次に整える境界を決めます。
最初の1か月は、壊れやすい経路の棚卸しと最小のCI、ロールバック手順の確認に集中します。次の1か月は、変更頻度の高いファイルへボーイスカウト・ルールを適用し、負債利息を記録します。その後は、カナリアの対象を広げ、復元訓練と指標の見直しを定例化します。期間は現場の規模に合わせて変えますが、毎週『どの失敗を早く見つけられるようになったか』を確認します。
対象を選ぶ時は、危険そうという印象だけでなく、変更頻度、利用者への影響、現在の観測可能性、戻しやすさを並べます。重要だが観測できない箇所は、いきなり整理せず、まず記録とテストから始めます。変更頻度が低く影響も限定的な箇所は、無理に今すぐ直さず、条件が変わった時に見直せるよう判断理由を残します。
- 最初は一つの重要経路で、検知・切り戻し・記録をそろえる。
- 日常の変更に小さな改善を混ぜ、別の大型刷新計画へ依存しない。
- 変更頻度、影響、計測可能性、可逆性で対象を選ぶ。
- 毎週、早く検知できるようになった失敗と残る不確実性を確認する。
priority =
change_frequency
* failure_impact
* investigation_cost
/ observability_and_reversibility
start_with:
- high change frequency
- high impact when broken
- measurable behavior
- small reversible boundaryこの進め方にも、適用しない方がよい場面があります
テストも監視もなく、データのバックアップや復元確認もできない状態で、いきなり本番のコードを小刻みに変更するのは安全ではありません。最初に読み取り専用の棚卸し、ログの保存、バックアップの復元確認、手動の切り戻し手順を整えます。小さな差分は、失敗時の調査範囲を小さくしますが、失敗の可能性をゼロにはしません。
また、要件の解釈が変わる作業や、認証・権限、金銭計算、破壊的なデータ移行は、単純な『触った場所を少しきれいにする』対象ではありません。設計レビュー、段階移行、二重記録、承認者の明確化など、別の安全策が必要です。カナリアリリースも、データを書き換える処理の影響を自動で元に戻してくれる仕組みではありません。
ボーイスカウト・ルールを、開発者の善意だけに頼る運用にも注意が必要です。差分の予算、レビュー基準、優先順位、負債利息の計測方法をチームで合意し、忙しい時に中断しても再開できる形にします。安全に触れる仕組みは、個人の慎重さではなく、誰が担当しても同じ判断ができる境界として残します。
- 観測・バックアップ・復元がない場合は、まず安全弁を作る。
- 認証、権限、金銭、破壊的移行は、別の設計と承認の対象にする。
- 小さな差分でも、失敗の可能性とデータの不可逆性は別に評価する。
- 個人の注意力ではなく、チームで再利用できる判断基準にする。
『壊さない』から『壊れても戻せる』へ
技術的負債が大きい現場で、最初から全体を作り直す必要はありません。変更前に検知と復元を用意し、触った範囲だけを少し改善し、放置による余計な時間を記録し、復元の訓練を続けます。この4つを回すと、改善は特別なイベントではなく、日常の開発に組み込まれます。
安全側に倒すことは、触らないことと同義ではありません。安全とは、変更の影響範囲がわかり、失敗を早く見つけられ、被害を閉じ込め、必要なら前の状態へ戻せることです。『壊さないように慎重に触らない』から、『壊れても大丈夫な状態を作って、範囲を決めて触る』へ定義を更新することが、負債を増やさず前進するための判断軸になります。


