前幾天同事問我:「可以給我你那個功能的資料庫結構嗎?我要對接。」
我老實回答:「我沒有仔細研究 AI 幫我生了什麼資料庫結構。」
放在五年前,技術主管講這種話可能會被檢討。但這句話背後,其實不是我不在意資料庫,也不是我不在意系統設計。剛好相反。它反而讓我更清楚感受到:AI 時代的程式設計,正在從「手寫實作細節」往「定義行為、約束邊界、驗證結果」移動。
AI 不是外送機車,換了不會自動快三倍
最近公司在推 Claude Code,再之前是 Cursor。我常被問同樣的問題:「用了 AI,效率應該快三五倍吧?沒有十倍也要有吧?」
老實說,這種問題很難回答。因為軟體開發不是換一台比較快的機車,外送速度就自動翻倍。
開發速度牽涉到很多層:
- 需求是否真的清楚
- 系統邊界是否被定義
- 測試是否能描述預期行為
- 架構是否能承受變更
- 資料模型是否與業務概念對齊
- 權限、資安、稽核、維運是否被納入
- 團隊知識是否能被下一個人接手
真正拖慢軟體開發的,常常不是打字速度,而是問題沒有被想清楚。如果需求本身模糊,AI 只會更快地把模糊變成程式碼。這不是加速。這是把問題提早變成技術債。
我重新找回了 TDD 的心流
我已經五年以上沒正式寫程式了。之前裝 Cursor,坦白說沒有太大感覺,甚至覺得不如以前用 IntelliJ 順手。但最近用 Claude Code,竟然又找回一點當年寫測試驅動開發(TDD)的心流。
以前的節奏是:
- 寫一個失敗測試
- 補一點實作
- 重構
- 測試轉綠
- 再下一題
一抬頭就中午了,午休後再回來,一不小心就下班了。
最近那種心流又出現了,但內容已經不太一樣。以前的程式設計,是我親手寫每一行程式碼。現在更像是:
- 我定義問題
- 我拆解任務
- 我描述限制
- 我補測試
- 我檢查結果
- 我修正規格
- AI 在可控範圍內生成實作
換句話說,我做的事情更像是把 AI 的活動空間畫出來。不是放它自由奔跑,而是先把跑道、邊界、終點線、違規條件都標清楚。
那個「資料庫結構」的故事
回到開頭那個故事。同事要對接我的功能,所以問我能不能提供資料庫結構。這是合理需求。
但我那時候真正關注的是:
- 使用情境能不能跑對?
- 流程是否符合預期?
- 畫面狀態有沒有正確變化?
- 錯誤情境有沒有被處理?
- 權限與資料狀態是否一致?
- 這個功能是否真的符合使用者故事?
所以我不是先打開資料表慢慢研究,而是叫 Claude Code 搭配 Playwright,把我想要的情境跑對。我當時關注的不是底層先長什麼樣,而是「行為是否符合規格」。
這裡有一個關鍵轉換:以前我們常常從資料庫進入系統。現在 AI 輔助開發裡,我越來越常從「行為」進入系統。也就是:
- 先定義使用者會怎麼操作
- 再定義系統應該怎麼反應
- 接著用測試把這些反應固定下來
- 最後才檢查實作細節是否合理
隔天,我請 Claude Code 整理出一份完整的資料庫結構異動報告交給同事,但報告整理出來後,我心裡反而冒出另一個問題:你是真的要自己讀這份結構,還是其實也要餵進去給 AI 讀?
這件事讓我意識到:重要性的順序變了。
以前:人先理解資料庫 → 寫程式碼 → 補測試 → 補文件。
現在可能變成:人先描述情境 → AI 生成實作 → Playwright 驗證行為 → AI 反向整理資料庫、API、測試與文件。
這不是說資料庫結構不重要。而是「進入系統的入口」變了。資料庫仍然重要,API 仍然重要,架構仍然重要。但它們不再一定是第一個入口。第一個入口可能是可驗證的使用者情境。
程式設計典範,是人類如何控制複雜度的歷史
我想用中年人的方式,講個古。我們常講函數式、物件導向、切面導向,這些看起來像技術名詞。但背後真正的問題其實很樸素:軟體越來越複雜,人類到底要用什麼方式來理解它、組織它、修改它?
所謂程式設計典範(programming paradigm),就是不同時代面對複雜度時,發展出來的思考框架。
① 機器導向:跟 CPU 講悄悄話
組合語言年代,工程師要知道 CPU 暫存器在哪、記憶體位址怎麼算、跳躍指令往哪跳、堆疊怎麼進出。那時候寫程式很像在跟機器講悄悄話,而且不能講錯半個字。
關注點不是「我要解決什麼業務問題」,而是「我要怎麼讓這台機器照我的意思動」。這是最接近硬體的程式設計。它的抽象層次很低,但控制力很強。
② 結構化:讓程式不要像迷宮
後來 Fortran、COBOL、C、Pascal 等高階語言出現,人類終於不用每天貼著 CPU 呼吸。
1968 年是關鍵的一年。Edsger Dijkstra 發表〈Go To Statement Considered Harmful〉,後來常被視為結構化程式設計的重要象徵。同一年,NATO 軟體工程會議讓「軟體工程」這個詞正式被提出討論。
背景很簡單:軟體危機。系統變大、時程失控、品質不穩、維護困難。聽起來是不是跟現在很像?
軟體工程不是因為大家愛流程才出現,而是因為軟體已經大到一個人腦袋裝不下。結構化程式設計要控制的是流程複雜度。少一點 goto,多一點清楚的條件、迴圈、函式與區塊。
③ 物件導向:把世界拆成可以協作的物件
物件導向的根可以追到 1960 年代 Ole-Johan Dahl 與 Kristen Nygaard 開發的 Simula。後來 Alan Kay 在 Smalltalk 把這個想法發揚光大。
Alan Kay 看重的其實不只是 class,而是「物件之間用訊息協作」。物件導向最迷人的地方是,它讓我們可以用接近人類理解世界的方式寫系統。
訂單是物件。帳戶是物件。使用者是物件。交易是物件。每個物件有自己的狀態、行為、責任、邊界。
物件導向真正的精神,不是到處開 class,也不是生出一堆 Manager、Helper、Util、Service 讓新同事考古。它的核心是:
- 封裝變化
- 分配責任
- 定義邊界
- 讓大型系統可以被團隊共同理解
但物件導向也走過一段過度設計的時代。繼承樹越長越深,設計模式變成儀式,程式還沒解問題,架構先長出一座迷宮。所以後來大家又開始說「組合優於繼承」(composition over inheritance),開始重視 DDD、限界上下文(bounded context)、介面契約(interface contract)。
物件導向沒有死。只是它從「類別崇拜」變成「邊界設計」。
④ 函數式:把世界看成資料轉換
函數式的根可以追到 Alonzo Church 1930 年代的 λ 演算(lambda calculus)。實務上,John McCarthy 在 1958 年發展出 Lisp,讓函數、遞迴、符號處理成為程式設計的重要語言。
到了 1977 年,John Backus 在圖靈獎演講〈Can Programming Be Liberated from the von Neumann Style?〉中,批判傳統程式設計過度依賴狀態與指派,進一步把函數式風格推向主流視野。
函數式的核心不是「寫得很數學、看起來很難」。而是:
- 同樣輸入,得到同樣輸出
- 少一點副作用
- 少一點共享可變狀態
- 多一點可測試、可推理、可組合
這件事在 AI 時代會變得更重要。因為 AI 很會生成程式碼,但 AI 也很會生成「看起來合理,但副作用亂飛」的程式碼。純函數、不可變資料(immutability)、清楚的輸入輸出,會讓 AI 產出的程式更容易被測試、被審查、被人類接手。
函數式不只是語言偏好。它是一種讓程式可驗證的思維。
⑤ 切面導向:把橫切關注點抽離
切面導向(AOP)在 1997 年由 Gregor Kiczales 等人提出,要解決一個很實務的問題:有些功能不是單一模組的責任,而是橫跨整個系統。
例如日誌、安全、交易、監控、稽核。這些就叫橫切關注點(cross-cutting concerns)。
很多 Java / Spring 開發者其實每天都在用 AOP,只是不一定這樣稱呼。例如:
@Transactional
你寫的是商業邏輯,但交易開啟、commit、rollback 是框架在方法前後幫你織進去的。
核心精神是:商業邏輯不該被日誌、權限、交易、監控塞滿。橫切政策該集中治理,不該散落在每個方法裡。
但 AOP 也提醒我們:抽象越高,隱形魔法越多。好的 AOP 把穩定、共通、技術性的政策抽出去。壞的 AOP 把商業邏輯藏起來,讓 debug 變成靈異節目。
⑥ 宣告式、反應式、Actor:抽象層次一路往上爬
再後來,SQL 讓我們說「我要什麼資料」,不必一步步描述資料庫怎麼找。反應式程式設計(Reactive Programming)讓我們思考事件流。Actor 模型讓我們思考訊息傳遞與獨立執行單元。資料流(Dataflow)讓我們思考資料怎麼流動。
基礎設施即程式碼(Infrastructure as Code, IaC)讓我們用宣告式語言描述整個環境。Kubernetes YAML、Terraform、OpenAPI、GraphQL 這些工具看似各做各的,其實是同一條路線上的不同分支。
這條路線是:人類越來越少直接控制每個步驟,越來越多在描述意圖、邊界、規則與結果。
把這六個階段串起來,你會發現一條清楚的軌跡:
- 組合語言時代:我們控制機器
- 結構化時代:我們控制流程
- 物件導向時代:我們控制規模與責任
- 函數式時代:我們控制狀態與副作用
- 切面導向時代:我們控制橫切關注點
- 反應式 / Actor 時代:我們控制事件、併發、資料流
每一次典範轉移,都不是因為大家愛追潮流,而是因為軟體越來越大,人類腦袋快裝不下了。
那 AI 時代要控制什麼?
黃仁勳說,未來的程式語言是人類自然語言。我同意:自然語言會成為新的程式設計介面。
但我不太相信,未來只是「提示詞導向程式設計」(Prompt-Oriented Programming)。
因為「幫我做一個會員系統」雖然是自然語言,但這句話裡沒有:
- 權限模型
- 狀態轉換
- API 介面契約
- 錯誤處理
- 資安要求
- 測試案例
- 稽核紀錄
- 資料保留政策
- 併發情境
- 失敗補償機制
- 觀測與告警需求
AI 可以很快生出一個「會動的東西」。但「會動」和「可維護、可稽核、可擴充、可上線」之間,隔著一整片軟體工程的森林。
所以我認為下一個時代,會是規格導向程式設計。
規格導向程式設計(Spec-Oriented Programming)
規格導向程式設計的重心,不是「我親手寫每一行程式碼」。而是:
我能不能把需求描述成 AI 和團隊都看得懂、能驗證、能追蹤、能演進的規格。
它聽起來很像以前的需求文件,但其實不一樣。文件常常是寫給人看的。寫完丟進 Confluence,半年後沒人記得。
規格不一樣。
真正好的規格,應該同時具備三種特性:
- 人看得懂:PM、SA、工程師、QA 都能對齊語意
- AI 用得上:能作為生成程式碼、測試、文件的上下文
- 系統驗得出來:能透過自動化測試、契約測試、靜態檢查或監控指標持續驗證
所以規格導向不是一份文件,而是一套可執行、可驗證、可演進的描述。它包含:
- 使用者故事(User Story)
- 驗收條件(Acceptance Criteria)
- 範例對應(Example Mapping)
- API 介面契約(API Contract)
- 狀態機(State Machine)
- 資料模型(Data Model)
- 權限矩陣(Permission Matrix)
- 錯誤處理(Error Handling)
- 測試案例(Test Case)
- 架構邊界(Architecture Boundary)
- 安全限制(Security Constraint)
- 可觀測性需求(Observability Requirement)
- 部署政策(Deployment Policy)
這些其實都不是新發明。DDD、BDD、TDD、Design by Contract、OpenAPI、ADR、契約測試、架構決策紀錄,過去 20 年我們已經累積了很多工具。只是在沒有 AI 的時代,它們常被視為「進階工程師的素養」。
在 AI 時代,它們會變成最低門檻。
為什麼規格能力會變成核心能力?
因為 AI 不會自動幫你把問題想清楚。
以前需求模糊,工程師在打字過程中會「邊寫邊想」。很多問題會在實作過程中浮現。
現在 AI 不一定會幫你浮現問題。它更可能用很高的自信,把錯誤需求快速落地。
所以:
- 需求不清楚,AI 只會更快地做錯
- 邊界不清楚,AI 只會更快地改壞
- 測試不清楚,AI 只會更快地產生自信滿滿的 bug
- 架構不清楚,AI 只會更快地蓋出漂亮但危險的紙城堡
- 權限不清楚,AI 可能讓內部狀態外洩
- 交易不清楚,AI 可能把 rollback 與補償流程漏掉
- 稽核不清楚,AI 可能讓系統能跑,但事後查不到責任軌跡
這就是為什麼規格能力會變成新的核心能力。
我自己最近的工作流:Example Mapping + Playwright + Claude Code
回到開頭那個資料庫結構的故事。
我現在在叫 Claude Code 動手之前,會先做一件事:Example Mapping。
這是 Matt Wynne 在 BDD 社群提出的需求釐清技巧。原本是用四種顏色的便利貼,我自己改成直接寫 markdown。
一個 user story 會被拆成四種東西:
🟡 Story(故事):這次要解決誰的什麼問題。
🔵 Rules(規則):這個故事必須遵守的業務規則。
🟢 Examples(具體例子):每條規則至少配一個成功案例和一個失敗案例。
🔴 Questions(問號):討論過程中浮現、但現在還答不出來的疑問。
整個 session 大概 20 到 30 分鐘就能跑完一個 story。
它的厲害之處在於:綠色 examples 寫得越具體,測試就越好寫。紅色 questions 越多,代表這個故事還不該進開發。
我現在的 AI 開發流程大概是這樣
Step 1:先做 Example Mapping,把問號清掉
把 Story / Rules / Examples / Questions 寫成 markdown 檔。
紅色問號還沒清完之前,不進下一步。
這一步常常就替我省掉一輪「AI 寫得超快,但方向是錯的」循環。
Step 2:請 AI 用 Playwright 證明它真的做對
接著,我會請 Claude Code 用 Playwright 跑出那些具體情境。
重點不是「把 examples 轉成測試檔」這件事本身。
重點是:你說你做完了,那請你用瀏覽器操作流程證明給我看。
例如:
- 使用者從哪個入口進來
- 點了哪些按鈕
- 填了哪些欄位
- 畫面狀態應該怎麼變
- 成功時應該看到什麼
- 失敗時應該出現什麼錯誤
- 權限不足時應該被擋在哪裡
這時候 Playwright 對我來說,不只是測試工具。它比較像是 AI 的驗收錄影機。
我不是只聽 AI 說「我已經完成了」。我要求它透過可重跑、可截圖、可追蹤的操作情境,證明這個功能真的符合我前面定義的規格。
如果跑不過,就代表不是它沒寫完,就是我的規則還不夠清楚。兩種都很好。因為問題被提早攤在桌面上,而不是等到 UAT 或上線後才爆炸。
Step 3:讓 AI 修實作,直到情境真的跑對
請 Claude Code 根據規則和測試寫實作,直到所有 examples 都跑過。
跑不過,就改實作。如果規則本身有破口,就回頭改 Step 1 的藍卡。
Step 4:補契約與邊界檢查
這一步我覺得很重要,也是很多 AI coding 容易漏掉的地方。
功能跑對,不代表系統設計是安全的。
所以我會要求 AI 再檢查:
- API request / response 是否穩定
- 是否有不該暴露的欄位
- 資料庫 migration 是否可回滾
- 是否有破壞既有相容性
- 權限檢查是否在正確層級
- 交易邊界是否清楚
- 是否需要 idempotency
- 錯誤處理是否可被前端與維運理解
這一層不是單純「讓測試過」。而是把功能從 demo code 推向 production-ready code。
Step 5:反向產出文件與接手資訊
等行為跑對了,我再請 Claude Code 整理:
- 目前的資料庫結構長怎樣
- 對外 API 介面契約
- 狀態轉換表
- 已知限制
- 哪些地方有副作用
- 哪些地方有外部依賴
- 後續維運要注意什麼
這些是給下一棒的人用的。下一棒可能是同事,也可能是下一個 AI session。
這裡其實很關鍵。AI 時代的文件,不只是人類閱讀材料。它也是下一次 AI 工作的上下文燃料。
注意這個順序的變化
- 規格在前
- 程式在中
- 文件在後
- 驗證貫穿全程
以前很多專案是反過來的:先設計資料庫 → 再寫程式 → 最後補測試和文件。而且通常都沒補。
Example Mapping + AI 反而把順序掰回它本來該有的位置。因為當生成成本下降,你會清楚地發現:把 examples 寫對,比把程式寫快重要得多。
AI 時代,軟體工程不是變少,而是變前面
所以 AI 不代表軟體工程不重要。
剛好相反。
AI 會把軟體工程往前推。
以前可以靠資深工程師邊寫邊想、邊做邊補。現在如果沒有事先定義規格、測試與邊界,AI 會把錯誤實作得非常有效率。
當程式碼生成成本下降,真正昂貴的會變成:
- 問題定義錯誤
- 需求理解錯誤
- 架構邊界錯誤
- 測試假設錯誤
- 風險判斷錯誤
- 權限模型錯誤
- 資料模型錯誤
- 團隊知識沒有沉澱
未來的工程師,不一定是打字最快的人。而是能把混亂需求,整理成可執行、可驗證、可維護、可演進系統的人。
我現在不會急著要求同仁立刻相信 AI 能提升十倍效率。我比較想問的是:
我們有沒有開始學會,把需求寫成 AI 看得懂、團隊追得動、測試驗得出來、未來維護得下去的規格?
如果有,AI 才真的不只是一個比較聰明的自動補完。它會是我們重新設計軟體工程工作的契機。
結論與反思
你的團隊在導入 AI coding 的時候,卡在哪個環節?
- 是需求不清楚?
- 測試補不起來?
- AI 產出的程式碼不好 review?
- 還是不知道怎麼把規格餵給 AI?
歡迎留言聊聊。