AIが頼んでもいない修正を勝手にしてしまう問題

ブログ記事

AIが頼んでもいない修正を勝手にしてしまう問題

16 分で読めます投稿者:Tomoki
タグ:AICopilotClaude実務プロンプト例テンプレート

AIに既存システム改修をさせると勝手な修正が増える理由と、防ぐための進め方

既存システムの改修や機能追加、バグ修正をAIに手伝わせると、こちらが意図していない修正まで入ってしまうことがあります。
仕様を読ませ、コードを読ませ、プランを立てさせてからレビューし、実装に入る。流れとしてはきれいに見えても、実際には「勝手な修正」や「不要な修正」が混ざるケースは少なくありません。

私も以下のような手順で進めていました。

  1. 仕様.md を読み込ませる
  2. ソースコードを読み込ませる
  3. プランニングさせる
  4. プランニングレビューを行う
  5. 実装させる

この流れ自体は間違っていません。
ただ、既存システムの改修という文脈では、これだけだとまだAIに自由度を与えすぎています。

この記事では、なぜAIが勝手な修正をしやすいのか、そしてどうすれば不要な修正を生まずに進めやすくなるのかを整理します。
前提として、利用環境は VS Code の Copilot(Claude Sonnet系)を実装側に使い、壁打ち役として Gemini Pro を併用する構成 を想定しています。


なぜAIは勝手な修正をしてしまうのか

AIはコードを書く能力自体は高くても、既存システム改修においては「変更してよい範囲」を人間ほど自然に理解してくれません。
そのため、次のようなことが起きます。

1. 仕様の穴を善意で埋めようとする

仕様に曖昧な部分があると、AIはそこを自分なりに補完して実装しようとします。
しかも多くの場合、それは「よりきれいな設計」「より自然な責務分離」「より統一的な命名」など、一見正しそうな方向に進みます。

しかし既存改修では、これが事故になります。
求めているのは理想的な再設計ではなく、既存挙動を崩さず、必要最小限で直すこと だからです。

2. 依存関係を見つけると周辺まで触りたくなる

AIは整合性を取ろうとします。
そのため、対象の関数を直すだけで済むはずなのに、関連クラスや共通部品、場合によっては命名や構成まで整えようとします。

人間なら「今回はそこ触らない」と判断できる場面でも、AIは明示しないと広げがちです。

3. 仕様理解と修正許可が混ざっている

仕様を読ませると、AIは「この仕様全体をよりよく成立させるために動いてよい」と解釈しがちです。
けれど実際に必要なのは、今回の改修要求に対して限定的な差分を作ること です。

4. プランレビューが抽象的だと抑止力にならない

「この方針でいきます」というレベルのレビューだけでは、実装時の暴走を止めにくいです。
本当に必要なのは、どのファイルのどの責務を、なぜ変えるのか を差分単位で固めることです。


問題の本質は「変更境界が曖昧」なこと

既存改修でAIが暴走する一番の原因は、モデルの性能よりも 変更境界が曖昧なまま実装に入っていること にあります。

言い換えると、AIにとって次の情報が不足しています。

  • 今回変更してよい範囲
  • 今回変更してはいけない範囲
  • 差分として合意済みの内容
  • 想定外の修正が必要になったときの振る舞い

これが曖昧だと、AIは「よかれと思って」補完し始めます。
なので対策は、AIをもっと賢く使うことよりも、AIが勝手に判断してよい余地を減らすこと です。


おすすめの進め方

私なら、既存改修をAIにやらせるときの流れを次のようにします。

  1. 仕様.md を読む
  2. 対象ソースを読む
  3. 今回の変更要求を再定義する
  4. 変更してよい範囲と変更してはいけない範囲を明示する
  5. 差分プランをファイル単位・関数単位で作らせる
  6. 差分プランをレビューする
  7. 制約付きで実装させる
  8. 仕様との対応関係を確認する

元の手順との違いは、プランニングの前後に「境界を固定する工程」を入れていることです。


まずやるべきは「今回の変更要求の再定義」

仕様を読ませた直後にいきなりプランニングさせるのではなく、まずは今回の改修要求だけを抜き出させます。

ここで重要なのは、仕様全体の要約ではありません。
欲しいのは、今回の差分に関係する内容だけを切り出した整理 です。

例えば、以下のような観点で整理させると精度が上がります。

  • 今回の改修目的
  • 期待する挙動
  • 対象画面 / API / バッチ
  • 変更が必要そうな箇所
  • 変更しない前提の箇所
  • 不明点
  • 仕様に書いてあること / 書いていないこと

この段階で「何を直したいのか」が1文で言えない場合、実装フェーズで余計な修正が混ざりやすくなります。


非変更条件を必ず明文化する

AIは「やってよいこと」よりも「やってはいけないこと」を明文化した方が安定します。
特に既存改修では、非変更条件がとても効きます。

例えば次のような条件です。

  • 公開インターフェースは変えない
  • DBスキーマは変えない
  • 共通部品は必須時のみ変更可
  • 命名変更だけの修正は禁止
  • リファクタリングは禁止
  • formattingのみの変更は禁止
  • コメント整理は禁止
  • lint対応のみの修正は禁止
  • テスト修正は差分に必要な最小限のみ

これを毎回書くのは手間に感じるかもしれませんが、実際にはこの一手間でかなり事故が減ります。


プランは差分単位で作らせる

「どう実装しますか?」では広すぎます。
AIのプランは、少なくとも次の粒度で出させるのが安全です。

  • 変更対象ファイル
  • 変更対象の関数 / クラス / メソッド
  • 変更理由
  • 具体的な変更内容
  • 変更しない箇所
  • 想定影響範囲
  • 不明点

ここで大事なのは、「このファイルを触る」だけで終わらせないことです。
どの責務をどこまで触るのかを細かく出させることで、レビューしやすくなります。


レビューは「方針」ではなく「差分契約」を見る

プランレビューの役割は、単に実装方針を確認することではありません。
今回の要件を満たすために必要最小限の差分かどうかを確定すること です。

レビューで見るべきポイントは次の通りです。

  • そのファイルは本当に触る必要があるか
  • その関数まで変更する必要があるか
  • その差分は仕様に直接紐づいているか
  • ついで修正が混ざっていないか
  • 非変更条件を破っていないか

つまり、レビュー時に見るべき問いはシンプルです。

この変更は、本当に今回要件を満たすために必要最小限か?


実装直前にもう一度制約を与える

レビューが終わったら、そのまま実装に入るのではなく、実装専用の制約をもう一度渡します。
ここを省くと、レビューで固めた内容からまた広がることがあります。

実装フェーズでは、例えば次のように伝えると安定します。

  • 承認済み差分のみ実装すること
  • 未承認ファイルは変更しないこと
  • 要件達成に不要な修正は禁止
  • リファクタリングは禁止
  • 命名変更は禁止
  • 想定外の追加修正が必要になったら止まること
  • 勝手に周辺コードを直さないこと

特に最後の「必要なら止まる」は重要です。
AIに必要なのは、賢く補完する能力だけではなく、勝手に進まない能力 です。


実装後は「何を変えたか」より「何を変えていないか」も確認する

AIの実装結果を見るとき、多くの場合は変更点の説明だけを見がちです。
ですが既存改修では、変更しなかった範囲の確認も同じくらい大切です。

例えば実装後の報告として、次の項目を出させるとレビューしやすくなります。

  1. 変更したファイル
  2. 各ファイルで変更した内容
  3. どの仕様記述に対応しているか
  4. 変更していない箇所
  5. 追加変更が必要かどうか

これを出させることで、「ついで修正」が混ざっていないかを確認しやすくなります。


CopilotとGeminiの役割分担

もし VS Code の Copilot 側で Claude Sonnet系を使い、壁打ちで Gemini Pro を使っているなら、役割を分けた方が安定します。

Gemini Pro に向いていること

  • 仕様の曖昧さ洗い出し
  • 改修要求の再定義
  • 変更範囲の検討
  • リスク列挙
  • レビュー観点出し
  • Claude に渡す制約の整理

Claude Sonnet系 に向いていること

  • 承認済みプランに従った局所実装
  • 差分説明
  • 実装後の自己点検
  • 変更範囲内での修正作業

つまり、Gemini = 監査・整理役、Claude = 実装役 と分けると扱いやすいです。
同じAIに「考える」「決める」「実装する」を全部やらせると、補完が強く出やすくなります。


実務で効きやすい運用上の工夫

差分仕様を別ファイルで持つ

既存の 仕様.md は情報量が多く、AIが全体最適を始めやすいです。
そのため、既存仕様とは別に 今回の改修専用の差分仕様 を用意するのが効果的です。

差分仕様には例えば以下を書きます。

  • 背景
  • 直したい事象
  • 期待結果
  • 対象範囲
  • 非対象範囲
  • 制約
  • 受け入れ条件
  • NG例

変更許可ファイルを明示する

これはかなり効きます。
最初から「今回触ってよいのはこのファイルだけ」と決めるやり方です。

例:

  • 変更可: UserService.ts, UserRepository.ts, userController.ts
  • 変更不可: 共通ユーティリティ、DBマイグレーション、UI共通部品

受け入れ条件を先に固定する

AIは途中で目的を見失うことがあるため、完了条件を先に書いておくと安定します。

例えば:

  • A画面で保存時にB項目が更新される
  • Cケースでは既存動作を変えない
  • APIレスポンス構造は変更しない
  • 異常系メッセージは現行のまま

仕様に紐づかない差分は禁止と明言する

これはレビューでも実装でも有効です。

各変更について、今回仕様のどの記述に対応するか説明できない差分は加えないこと。

この一文だけでも、なんとなくの改善提案が減ります。


既存改修で毎回意識したいこと

既存システム改修でAIを使うときに一番大事なのは、AIにきれいなコードを書かせることではありません。
既存挙動を壊さず、必要な差分だけを入れさせること です。

そのためには、モデルの性能よりも運用の設計が効きます。

特に効果が高いのは次の3つです。

  1. プラン前に「今回の変更要求」を再定義する
  2. 非変更条件を明文化する
  3. 想定外の追加修正が必要なら止まらせる

この3つだけでも、勝手な修正や不要な修正はかなり減らせます。


まとめ

AIに既存システム改修を任せるときに問題になるのは、仕様読解力そのものよりも、変更境界の曖昧さ です。
仕様を読ませるだけでは、AIはその仕様を「よりよく成立させるために動いてよい」と解釈しやすくなります。

だからこそ必要なのは、AIをもっと自由に考えさせることではなく、次のような制約を先に固定することです。

  • 今回何を直すのか
  • どこを変えてよいのか
  • どこは変えてはいけないのか
  • 差分として何が承認済みか
  • 想定外が出たらどう振る舞うか

既存改修でAIを安定して使いたいなら、
「設計相談モード」と「差分実装モード」を分けること」
そして
「勝手に判断してよい余地を減らすこと」
この2つがかなり重要です。


そのまま使えるテンプレート集

以下は、実際に使いやすいように整理したテンプレートです。
プロジェクトに合わせて調整してください。


1. 差分仕様テンプレート

md
# 差分仕様書

## 背景
- なぜこの改修を行うのか

## 対象事象
- 現在起きている問題
- 現行挙動

## 期待結果
- 改修後にどうなっていればよいか

## 対象範囲
- 対象画面:
- 対象API:
- 対象バッチ:
- 対象クラス/モジュール:

## 非対象範囲
- 今回変更しないもの
- 今回影響を与えない前提のもの

## 制約
- DBスキーマ変更禁止
- API契約変更禁止
- 公開インターフェース変更禁止
- リファクタリング禁止
- 命名変更禁止

## 受け入れ条件
- [ ] 条件1
- [ ] 条件2
- [ ] 条件3

## NG例
- ついでに共通化する
- 不要な命名修正
- formattingのみの変更

2. 解析フェーズ用テンプレート

md
あなたは既存システム改修の影響調査担当です。
目的は、今回要件を満たすために必要最小限の変更範囲を特定することです。

前提:
- 今回は新規設計ではなく既存改修
- 仕様に書かれていない改善提案は禁止
- 推測で補完しない
- 不明点は不明点として扱う
- 今回要件に直接関係しない変更は提案しない

タスク:
1. 差分仕様から今回の改修要求だけを抽出
2. 対象ソースから変更候補箇所を特定
3. 変更が必要な箇所と不要な箇所を分ける
4. 影響範囲を最小にする案を出す

出力形式:
- 改修目的
- 期待する挙動
- 変更対象候補
- 変更不要箇所
- 不明点
- 最小変更案

3. プランニング用テンプレート

md
これから実装プランを作成してください。
目的は、必要最小限の差分で要件を満たすことです。

制約:
- 未承認の設計改善は禁止
- リファクタリング禁止
- 命名変更禁止
- formatting変更禁止
- 共通部品の変更は必須時のみ
- DB変更禁止
- API契約変更禁止
- 変更対象外ファイルへの波及禁止

出力は以下に限定:
1. 変更対象ファイル
2. 変更対象メソッド/関数
3. 変更内容
4. 変更理由
5. 変更しない箇所
6. 想定影響範囲
7. 実装前確認事項

コードはまだ出力しないこと。

4. 差分レビュー用テンプレート

md
以下の観点で差分プランをレビューしてください。

レビュー観点:
- そのファイルは本当に変更が必要か
- その関数まで変更する必要があるか
- 各変更は仕様に直接紐づいているか
- ついで修正が含まれていないか
- 非変更条件を破っていないか
- 既存挙動への影響は最小か

出力形式:
- 妥当な変更
- 削るべき変更
- 追加確認が必要な点
- このまま実装してよいかの結論

5. 実装フェーズ用テンプレート

md
承認済みプランに従って実装してください。

厳守事項:
- 承認済みの変更のみ実施
- 変更対象外ファイルを触らない
- 要件達成に不要な修正は禁止
- ついでの修正は禁止
- リファクタリング禁止
- 命名変更禁止
- コメント整理禁止
- lint/formattingだけの修正禁止

もし要件達成に追加変更が必要だと判明した場合:
- 実装を続けず停止する
- 追加で必要な差分だけを報告する
- 勝手に修正しない

実装後の報告:
1. 変更したファイル
2. 各ファイルの変更内容
3. 仕様との対応関係
4. 変更していない箇所
5. 追加変更の必要有無

6. 変更許可リストテンプレート

md
# 今回の変更境界

## 変更を許可するファイル
- example/service/UserService.ts
- example/repository/UserRepository.ts
- example/controller/UserController.ts

## 変更を許可しないファイル
- 共通ユーティリティ全般
- DBマイグレーション
- UI共通部品
- 他機能のAPI
- 設定ファイル全般

## 備考
- 上記以外のファイル変更が必要になった場合は実装を停止し、追加差分案のみ提示すること

7. 受け入れ条件テンプレート

md
# 受け入れ条件

- [ ] 条件1: A画面で保存時、B項目が更新される
- [ ] 条件2: Cケースでは既存挙動を変更しない
- [ ] 条件3: APIレスポンス構造は変わらない
- [ ] 条件4: 異常系メッセージは現行仕様のまま
- [ ] 条件5: 承認済みファイル以外は変更しない

8. 壁打ち用テンプレート(Gemini向け)

md
以下は既存システム改修の差分仕様と対象コードの前提です。
今回やりたいのは設計改善ではなく、必要最小限の差分修正です。

あなたにしてほしいこと:
1. 仕様の曖昧な点を洗い出す
2. 変更範囲が広がりそうなポイントを指摘する
3. 非変更条件として書くべき項目を列挙する
4. 実装AIに渡す制約文を作る
5. レビュー観点を作る

注意:
- 実装案を広げすぎない
- より良い設計への誘導は不要
- 今回要件に直接関係しない提案は不要

9. 一文で効く制約テンプレート

md
今回の目的は最適化ではなく、既存挙動を極力維持したまま要件を満たす最小差分修正である。
より良い設計は今回の価値ではない。仕様達成に必須な差分のみ許可する。
仕様のどの記述に対応するか説明できない差分は加えないこと。

おわりに

AIに既存改修をやらせるときは、モデルの賢さだけで解決しようとすると限界があります。
むしろ大事なのは、AIに「どこまで考えてよいか」を明示すること です。

既存システムの改修では、自由度の高さは必ずしも武器になりません。
ときには、しっかり縛った方が結果的に速く、安全に進められます。

この記事の内容が、AIを使った既存改修の運用を少しでも安定させる参考になれば幸いです。