重點摘要
- AI 協作做逆向工程,最大的坑不是技術,而是 AI 「選擇性閱讀」原始碼
- 根治方法:強制 Source Inventory 協議 — 動手前先列清單、附行號、用戶確認邏輯後才寫程式
- 用戶不需要知道原版長什麼樣,清單上的每一項都要有原始碼行號出處
- 這套協議已寫入 CLAUDE.md,適用於任何 legacy code 逆向重建專案
最近在做一個系統重建專案:把一套已上線多年的 Ionic + PHP 系統,逐頁逐模組一比一重建為 Flutter 應用。聽起來直白,實際做起來卻踩了一個讓人很沮喪的坑——不是技術難度的問題,而是 AI 協作流程設計的問題。
這篇文章記錄我如何診斷這個問題,並建立一套可複用的 AI 逆向工程協作協議。
問題:一比一複製,卻一直落東掉西
我明確告訴 AI:「這是舊版原始碼,你幫我一比一複製到 Flutter。」但每次交付,都會發現缺少功能。最典型的例子:登入後的登出功能不見了。
登出不是什麼複雜功能,但它不在主流程頁面——它藏在 header component 的某個角落。AI 在讀原始碼的時候,只讀了主頁面,沒讀共用元件,自然就沒看到登出按鈕的存在。
更麻煩的是:我自己也不知道原版長什麼樣(這是別人開發的系統),所以我也沒辦法直接指出「你少了這個」。每次都要在用起來的時候才發現缺少什麼,然後重來。
根本原因:AI 的「選擇性閱讀」
深入想了一下,問題的根源在於 AI 讀原始碼的方式:
| 錯誤的讀法 | 正確的讀法 |
|---|---|
| 找「我預期會有的東西」 | 逐行掃描,列出「所有看到的東西」 |
| 只讀主要頁面檔案 | 讀所有相關檔案(service、routing、共用元件) |
| 憑印象補齊功能 | 每一項都有原始碼出處(檔案 + 行號) |
| 「讀完」就開始寫 | 產出清單、確認後才開始寫 |
用一句話說:AI 是在「確認自己的預期」,不是在「完整盤點原始碼」。這種差距在簡單頁面感覺不出來,在有大量共用邏輯的真實系統裡,每個模組都會漏東西。
解法:Source Inventory 協議
我和 AI 一起設計了一套強制執行的協議,並寫入專案的 CLAUDE.md,讓每個接手這個專案的 AI session 都必須遵守。
Step 0 — 列出所有要讀的檔案(動手前)
在讀任何內容之前,先列出這個模組涉及的所有檔案,包含:
- 頁面本體(template + logic)
- 所有被引用的 service
- 共用元件(header、footer、tab-bar、nav)
- 路由設定
- 後端 handler
每個檔案讀完打勾,沒打勾的不算讀過。這一步強迫 AI 在開始之前就意識到「這個模組牽涉哪些檔案」,避免只讀主檔就動手。
Step 1 — 產出 Source Inventory(每項附行號)
讀完所有檔案後,產出功能清單。格式固定:
- [ ] 登出按鈕
來源:home.page.html:142 <ion-button (click)="logout()">
- [ ] 下拉刷新
來源:ticket.page.html:38 <ion-refresher (ionRefresh)="doRefresh($event)">
- [ ] 空狀態提示
來源:ticket.page.html:91 <div *ngIf="tickets.length === 0">目前沒有票券</div>
關鍵點:每一項都必須有原始碼的檔案和行號。
這解決了「用戶不知道原版長什麼樣,所以沒辦法驗證清單正確性」的問題。用戶不需要看過原版,只需要問:「這一項你是從哪行讀到的?」答不出來,就代表是 AI 憑印象補的,不是真的在原始碼裡。
Step 2 — 逐項實作,逐項打勾
對照清單一項一項做,每完成一項就打勾。不允許「大概實作了」或「之後再補」。
Step 3 — Self-Verify 後才交付
交付前,AI 自己對照清單跑一遍,確認每項都存在於程式碼中。如果 Step 3 發現漏項,必須補完再說「完成」。漏項不能留給用戶發現。
一條關鍵規則:禁止在原始碼存在的情況下向用戶索取截圖
另一個常見的壞模式是:AI 讀不懂某個行為,就跟用戶要截圖或圖片。
這在逆向工程的情境下尤其荒謬。用戶拿到舊系統原始碼,通常自己也沒有截圖,甚至從來沒用過那個系統。要求用戶提供截圖,是把 AI 本來應該做的工作(讀原始碼)轉嫁給用戶。
正確的做法是:原始碼讀不懂的地方,標記 [?],說明不確定的點,讓用戶確認業務邏輯——而不是要求視覺確認。
這套協議適用於任何逆向升級工程
雖然這次的情境是 Ionic → Flutter,但這套協議的核心邏輯對任何 legacy 重建都適用:
- jQuery / AngularJS → React / Vue
- PHP monolith → 現代後端(Go、Node、Python)
- 原生 Android/iOS → Flutter / React Native
- 桌面應用 → Web 應用
只要是「拿到舊系統 code,重寫到新技術棧」,用戶通常都不知道原版的全貌,也無法驗證 AI 是否讀完了所有東西。Source Inventory + 行號出處 是讓這個過程可稽核的最低門檻。
如何在你的專案裡執行這套協議
把以下內容加入你的專案 CLAUDE.md(或任何 AI 工作指引文件):
## 一比一複製協議(MANDATORY)
> 原始碼是唯一的 ground truth。用戶不應該被要求提供截圖。
### Step 0 — 列出所有要讀的檔案
(所有頁面、service、共用元件、routing、後端 handler)
每個檔案讀完打勾,沒打勾不算讀過。
### Step 1 — Source Inventory(每項附檔案:行號)
格式:
- [ ] 功能描述
來源:filename.html:LINE_NUMBER <原始碼片段>
### Step 2 — 逐項實作,逐項打勾
### Step 3 — Self-Verify 後才交付
### 禁止行為
- 在原始碼存在的情況下向用戶索取截圖
- 沒有產出 Source Inventory 就開始寫程式
- 只讀主頁面,不讀 service / routing / 共用元件
結語
逆向升級工程的難點不在技術轉換,而在於如何保證「沒有遺漏」。AI 的天性是預測下一個 token,這讓它容易「補全期望的樣子」,而不是「如實記錄看到的東西」。
Source Inventory 協議的作用,就是把 AI 的工作模式從「自由發揮」強制切換成「逐行比對」。這不是在限制 AI,而是在讓 AI 的輸出變得可以被信任。
如果你也在做類似的逆向重建專案,歡迎把這套協議複製進你的 CLAUDE.md,看看效果如何。
發佈留言