ai-driven-development legacy-migration workflow

画面の見た目は同じ、でも裏では違うテーブルを消していた話

移行画面の UI は元の VB 版と完璧に一致させたのに、削除ボタンが消すテーブルが違う、Oracle 関数の呼び出しが抜けて名称ではなくコードが表示される、権限制御が API 側で落ちている。「見た目は同じ」「業務は崩れている」バグを連続で踏んだ話です。

W
渡邊 賢

これは「レガシー移行で学んだこと」シリーズの第5回(最終回)です。前回は WinFormsをWebに移すときのTab/blur依存の罠 を参照ください。

こんにちは!地雷踏み抜きまくりの渡邊です。 シリーズ最終回は、UIは完全に一致しているのに、業務は崩れていた バグの話です。前回のTab/blur依存バグは「見た目のUX」の問題でしたが、今回は画面の裏側、SQLやAPI側の話です。

UIが同じでも安心できない

ある申請情報の画面をVB.NET版からBlazorに移行したときの話です。タブが複数あり、それぞれに削除ボタンが付いている画面でした。

Codexに棚卸しさせて、Claude Codeに実装させて、UIは元画面とほぼ完全に一致するところまで持ってきました。フォーム数、ラベル、編集可否、グリッド列、すべてOK。ビルドも通って、UnitTestも通って、画面操作も期待通り動く。

「これは完了だな」と思ってReviewer Sub Agentを走らせたら、P0(最優先)のblocker級指摘が返ってきました。

P0: Tab2 の削除ボタンが Tab1 の削除 API を呼んでいる。
     → 削除対象テーブルが不一致。
P1: Tab2 の削除に UI / API 両方の権限制御が不足している。
     → 一般ユーザーが高権限者専用操作を実行可能。

画面は完全に元通りなのに、削除ボタンを押したら消されるテーブルが元実装と違う。これはヤバい。非常にヤバい。

具体的に何がズレていたか

詳しく見ていくと、こんな感じでズレていました。

1. 削除範囲の不一致

元のVB実装では、Tab2の「削除」は関連マスタ3系統(本体 + 内訳 + 添付)を連動削除する処理でした。AIが書いた実装は、Tab1用の削除APIをそのまま流用していて、本体しか消していません

UIは一緒。ボタンを押すと一件消えたように見える。でも内訳と添付は残る。本番で使われ続ければ、孤立レコードが溜まり続けて、いずれデータ不整合で事故ります。

2. 権限制御が抜けている

元実装では、Tab2削除は「高権限ユーザーのみ表示・実行可能」でした。AIが書いた実装は、UI側のボタン表示条件を見落としていて、一般ユーザーでもボタンが押せる 状態になっていました。

さらに悪いことに、API側でもロールチェックをしていないので、ボタンを隠したところでAPIを直接叩けば誰でも削除できる。UIとAPIの両側で権限ゲートを持たないと、どちらかを突破されれば終わりです。

3. Oracle関数の呼び出し漏れ

これはもっと地味なバグですが、名称解決用のOracle関数の呼び出しが抜けていた ケースも同時に出ていました。元画面では関数を経由して「名称」として表示していた列が、AI実装では「コード」のまま表示されている。

UIが「まったく同じ項目を同じ位置に表示している」ように見えて、中身の表示値が違う。ユーザーから見ると読めないコード列が並んでいて、業務で使い物になりません。

なぜAIは見た目は合わせられるのに業務は外すのか

Claude Codeに元画面を見せて「同じように作って」と頼むと、画面の見える部分を模倣するのはすごく得意 です。項目名、配置、ラベル文言、グリッド列の順序、このあたりはほぼ完璧に揃えてきます。

けれど、「このボタンを押したときに、裏で何の業務を実行すべきか」は、画面の見える部分からは読み取れません

  • どのテーブルを触るべきか
  • どの権限が必要か
  • どの関数を呼ぶべきか
  • どのイベントで何を自動実行するか

これらは元のVBコードの *_Click ハンドラや、呼び出し先のモジュール、さらに裏のSQLまで追わないと出てきません。AIが見えているのは画面定義ファイルだけ、ということが多く、そこから先まで追っていないと上記のような事故が量産されます。

対策: Reviewerにサーバー側の観点を持たせる

前々回の Claude CodeとCodexを分業させた話 で、Codexに差分レビューをさせるフローを書きましたが、そこでのチェック観点は「UI項目が減ってないか」が中心でした。これに加えて、サーバー側の業務同等性 も固定観点に入れました。

追加したReviewerのチェックリストはこんな内容です。

  • 削除処理: 元実装が触っているテーブルを全部特定し、実装側で全部触っているか
  • 権限制御: 元実装が持っているUI側の表示条件、API側のロール検査を両方再現しているか
  • 関数呼び出し: 元実装が呼んでいるDB関数 / Oracle関数を、新実装側でも呼んでいるか
  • イベント連動: 画面イベントで呼ばれる裏の処理(ログ記録、他テーブル更新など)が抜けていないか

これらはUIを見ただけでは絶対に出てきません。元のVBコードの呼び出し先を追う必要があります。Codexに「元の画面を棚卸しする時に、各アクションの呼び出し先モジュールとSQLまで辿ってください」と明示することで、少しずつ拾えるようになりました。

わかったこと

  • UI一致 ≠ 業務同等性。画面が同じに見えても、裏のテーブル・権限・関数呼び出しが違えば、それは別のソフトウェア
  • AIは画面の「見える部分」を模倣するのは得意だが、業務の意味論までは見ない。モデル性能の問題というより、見えているコンテキストの問題
  • Reviewerに「サーバー側の観点」を明示的に持たせないと、UIレビューだけ回して安心してしまう罠がある
  • この話はVB → Blazor固有ではなく、たとえばJava Struts → Springのような他スタックのマイグレーションでも、API / DB側の意味論が崩れるのは共通だと思います

シリーズを通して気づいたこと

5回書いてきて、共通していたのは結局 「AI駆動で速く走らせるほど、工程を仕組みで縛らないと事故が増える」 という一点です。

これらはどれも「AIをどう使うか」の話であって、「AIがどれだけ賢いか」の話ではありません。モデルの進化を待っているだけでは、たぶんこの手の事故は減らないです。工程で縛るのが、今の時点では一番確実なブレーキだと感じています。

シリーズを読んでくださった方、ありがとうございました。

person

渡邊 賢

等差級数的Commit 運営 / ICD VIETNAM.LLC General Manager

AI駆動開発と段階的なレガシーモダン化をテーマに、日々の試行錯誤をこのブログに記録しています。

プロフィール詳細 arrow_forward

似たような課題に困っている方、一緒に考えませんか。

AI駆動開発・Vibe Coding・レガシーマイグレーションに関するご相談を受け付けています。