"As an evolving program is continually modified, its complexity, reflecting deteriorating structure, increases unless work is done to maintain or reduce it."
—— Meir M. Lehman, Programs, Life Cycles, and Laws of Software Evolution (1980)
一、非黑即白之外的灰色地帶
測試與 CI 面對邊界清晰的二元世界:測試要麼紅要麼綠,斷言要麼真要麼假,Exit Code 要麼是 0 要麼是 1。對錯分明,可以直接交給機器自動熔斷。
但軟件工程中還有大量問題無關「對錯」,只關乎「好壞」。
數據庫設計、模塊依賴與代碼重複度都處於這片灰色地帶,沒有單一的客觀標準,只有工程權衡(Trade-offs)與難以量化的「代碼壞味道(Code Smells)」:
- 代碼重複:遵循 DRY 原則固然重要,但強行抽象往往比適度拷貝更難維護(Wrong abstraction is worse than duplication);
- 數據庫模型:三範式(3NF)能保證數據一致,但在讀多寫少的場景下,適度反範式冗餘才是常態;
- 依賴邊界:分層清晰易於維護,但偶爾跨層調用究竟是務實變通還是結構腐爛,需要結合上下文判斷。
手寫代碼時代,這片灰色地帶靠資深工程師的經驗與品味把關。到了 AI Coding 時代,這裏成了腐敗最快的重災區。
二、AI 的盲區:只有局部最優,缺乏全局視野
Agent 擅長局部邏輯推導,卻缺乏全局架構視野。
面對只有「壞味道」、沒有「報錯信息」的灰色地帶,Agent 常走向兩個極端:
- 無底線複製貼上:遇到相近需求,直接把現有文件複製一份改幾行邏輯。測試全綠,功能正常,重複代碼卻悄然翻倍;
- 機械式過度抽象:缺乏全局背景時,把兩段僅屬巧合相似的業務代碼強行抽出公共基類,令兩個無關模塊緊密耦合。
更常見的是依賴穿透。為了解決眼前的編譯報錯,Agent 會直接從底層領域模型跨層 import 上層適配器。代碼能跑通,架構分層卻被破壞了。
這類問題不會觸發測試失敗或編譯報警。單靠人工在長篇 Diff 裏逐行排查,很容易漏掉。
三、靜態分析:暴露問題而非一刀切阻斷
依賴關係、重複度與 Schema 設計缺乏絕對的對錯標準,靜態分析工具就不宜設為「超標即熔斷」的硬門禁——合理的代碼拷貝有時正是為了模塊解耦。
靜態分析工具的作用是把隱蔽的結構問題可視化,交給工程師判斷。
文字描述是線性的,數據關係則是網狀的。自動生成的 ER 圖能把散落在多個 ORM 模型裏的外鍵、多對多關聯和冗餘字段直觀展開,讓工程師一眼看出核心表是否因隨意擴展而變得臃腫。
依賴分析工具(如 dependency-cruiser)將模塊間的調用方向繪製成圖。即使不強制阻斷構建,可視化的循環依賴(Cyclic Dependencies)和跨層引用也能在 Review 時提醒我們:新增的這條引用是合理變通,還是破壞了分層邊界?
結構化重複掃描工具(如 jscpd)的價值不在於強求「零重複」,而在於維護**「重複率基線(Duplication Baseline)」**。Agent 提交代碼時,工具會標出結構重合的片段,防止重複代碼在不知不覺中蔓延。
四、找回結構能見度
Lehman 軟件演化定律指出:系統隨着持續修改,結構必然趨向腐化,複雜度只會單向攀升,除非主動投入成本去抑制。
AI 產能過剩的時代,工程師面臨的最大問題是喪失了對代碼結構的能見度(Visibility)。
二元的正確性靠測試與 CI 把關;灰度的架構質量,則要靠 ER 圖、依賴拓撲和重複檢測提供全局視圖。這些工具把隱蔽的結構變化直接標示出來,省去逐行肉眼排查的精力,讓工程師把注意力集中在關鍵的架構取捨上。
五、小結
測試與 CI 卡住客觀的真假,領域文檔錨定業務語義,靜態分析與可視化則呈現灰度的結構變化。這三項構成了傳統軟件工程留下的基礎防線。
不過,傳統確定性工具終究有其邊界。面對懂得在合法語法裏「吞掉異常」、在測試斷言裏自我欺騙的模型,單靠固定規則已經不夠用了。
接下來,我們看 AI 時代的新工具。