画面の見た目は同じ、でも裏では違うテーブルを消していた話
移行画面の UI は元の VB 版と完璧に一致させたのに、削除ボタンが消すテーブルが違う、Oracle 関数の呼び出しが抜けて名称ではなくコードが表示される、権限制御が API 側で落ちている。「見た目は同じ」「業務は崩れている」バグを連続で踏んだ話です。
これは「レガシー移行で学んだこと」シリーズの第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駆動で速く走らせるほど、工程を仕組みで縛らないと事故が増える」 という一点です。
- Done Cycle: 完了の定義を動かし続ける
- Orchestrator + Sub Agent: 実装と検査の役割を分ける
- Claude Code + Codexの分業: 速さと精度を同じモデルに求めない
- Tab/blur依存バグ: パラダイムのズレはルール化で潰す
- 業務同等性(今回): UIではなく裏側までレビュー観点を広げる
これらはどれも「AIをどう使うか」の話であって、「AIがどれだけ賢いか」の話ではありません。モデルの進化を待っているだけでは、たぶんこの手の事故は減らないです。工程で縛るのが、今の時点では一番確実なブレーキだと感じています。
シリーズを読んでくださった方、ありがとうございました。
渡邊 賢
等差級数的Commit 運営 / ICD VIETNAM.LLC General Manager
AI駆動開発と段階的なレガシーモダン化をテーマに、日々の試行錯誤をこのブログに記録しています。
プロフィール詳細 arrow_forward似たような課題に困っている方、一緒に考えませんか。
AI駆動開発・Vibe Coding・レガシーマイグレーションに関するご相談を受け付けています。