iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
Vibe Coding

讓 AI Agent 維護一個 Open Source Project系列 第 16

Day 16:案例——一個真實功能開發的完整歷程(Pest 相容性偵測)

  • 分享至 

  • xImage
  •  

前言:昨天講「該不該做」,今天講「怎麼做」

昨天用 issue #427 到 PR #428 這個真實案例,講清楚「系統性盤點」怎麼幫功能開發收斂範圍。但一份 PR 描述裡的「同一分量級」「靜態標注」這些詞,具體落到程式碼裡是什麼樣子?今天要真的打開這份 PR,逐步走一次它的實作歷程——這篇比昨天更技術,聚焦在「怎麼做」而不是「該不該做」。

今日目標

  • 看懂 PR #428 實際改了哪些檔案、規模有多大
  • 理解「靜態標注」跟「執行期偵測」這兩種偵測方式的差異,以及為什麼這個功能選了前者
  • 看一個雙語法分析器(tree-sitter + php-parser)併行驗證的具體做法
  • 認識 TDD 紀律在這類功能開發裡具體怎麼落地

PR 的實際規模:319 行新增,9 個檔案

PR #428 的實際改動是 319 行新增、6 行刪除,涵蓋 9 個檔案。這個規模本身就呼應了昨天講的「同一分量級」——不是一個大重構,是在既有解析邏輯上,針對兩個新語法特性(todo(assignee:, issue:) 具名參數、skipOnCi()/skipLocally())做對稱的擴充。

改動的檔案分成兩組,對應這個擴充套件的兩層架構:

  • packages/phpunit/:真正的語法解析邏輯(TestExtractor.tsTestExtractorForPest.test.tsPestResolver.tstypes.ts),負責把原始碼解析成結構化的測試項目資訊
  • packages/extension/:VS Code 端的呈現邏輯(TestHierarchyBuilder.tsTestCollection.test.ts),負責把解析結果轉成 Test Explorer 看得懂的資料結構跟圖示

這個分層本身就是一種架構邊界的體現——解析邏輯不需要知道 VS Code Test Explorer 長什麼樣,呈現邏輯不需要知道底層用哪個語法分析器,兩層各自獨立測試、獨立驗證。

靜態標注 vs 執行期偵測:為什麼選前者

skipOnCi()/skipLocally() 這兩個方法,實際會不會跳過測試,要看執行當下的環境變數(是不是在 CI 環境跑)。理論上有兩種偵測方式:等測試真的執行過、看 TeamCity 協定回報的結果來判斷;或者在解析原始碼的當下,就靜態標注「這個測試呼叫鏈裡有條件式跳過邏輯」。

PR 描述裡明講選擇了後者——靜態標注成 annotations.conditionalSkip: 'onCi' | 'locally',用一個獨立的圖示($(question),問號)跟「已經確定會跳過」的圖示($(circle-slash),圓圈斜線)區分開來。這個圖示選擇本身傳達了一個誠實的訊息:這裡標記的是「可能會跳過」,不是「一定會跳過」——因為靜態解析階段,程式碼還沒真的執行,沒辦法知道當下環境變數的值。執行期真正的跳過判斷,仍然走既有的 TeamCity testIgnored 路徑處理,這部分完全沒有改動。

用一組對照來看這個設計決策:

❌ 如果選擇「假裝能確定」:
把 skipOnCi() 的測試直接標成「一定會跳過」
→ 在本機開發環境(不是 CI)執行時,這個標注是錯的,
  誤導使用者以為這個測試不會跑

✅ 實際採用的設計:
用獨立圖示標注「這裡有條件式跳過邏輯,但要看執行環境」,
真正的跳過判斷留給執行期的 TeamCity 回報
→ 靜態解析的角色是「提示可能性」,
  不假裝自己知道執行期才會決定的事

雙語法分析器併行驗證:同一個問題查兩次

昨天提到 issue #427 的盤點裡,每一條結論都「用兩套不同的語法分析器(tree-sitter 跟 php-parser)實際驗證過」。這個做法延續到了 PR #428 的實作裡——PestResolver.ts(對應其中一種解析路徑)跟 TestExtractor.ts(對應另一種)都各自加了對應的邏輯,兩邊的測試檔案(TestExtractorForPest.test.tsinterpret.test.ts)也都各自新增了測試案例。

為什麼要兩套語法分析器都驗證一次,而不是選一套信任到底? 因為這個專案要支援使用者環境裡可能存在、也可能不存在某個原生模組的情況——沒有這個模組時,退回另一套純 JavaScript 實作的解析器。如果只在一套分析器裡驗證過新語法的解析邏輯,另一套分析器沒有對應更新,會產生「在某些使用者的環境裡這個功能正常,在另一些環境裡完全沒反應」的落差,而且這種落差通常要等使用者回報才會被發現。雙路徑驗證,本質上是把「這個功能在所有支援環境下都一致」這件事,從查證變成了測試套件裡的具體斷言。

TDD 紀律:紅燈先於實作

PR 的 Test plan 清單裡第一條寫著「TDD: red before green for all new cases」——每一個新案例都先確認測試會失敗,再寫實作讓它通過。這正好呼應我另外在寫的《AI 時代的 TDD:讓 AI 寫 Code,但不要讓它決定品質》系列反覆強調的紀律:AI 一次生成「實作+測試」時,測試從沒有真的紅過就直接綠了,等於沒人驗證過這個測試真的有偵測能力。 這份 PR 明確把「紅燈先於實作」列成檢查項,而不是事後才補測試,是把這條紀律變成可以被檢查的具體流程步驟,而不只是原則性的呼籲。

Test plan 裡另外列出兩個套件各自的測試套件都要跑過(分別是 1046 個跟 365 個測試案例)、型別檢查要乾淨、pre-commit 檢查要過——這些都是在合併前就能被自動驗證的具體項目,不是「看起來應該沒問題」的主觀判斷。

今日思考題

回想你參與過的功能開發:這個功能有沒有兩種(或以上)可能的偵測/實作方式?當時是怎麼決定選哪一種的——是因為對使用者更誠實(像今天這個案例),還是純粹因為比較好實作?

今日重點回顧

  • PR #428 是一次收斂過的、規模對稱的功能擴充(319 行、9 個檔案),分層對應解析邏輯跟呈現邏輯兩層架構
  • 面對「執行期才能確定」的行為,選擇誠實標注「可能性」而不是假裝靜態解析能確定一切
  • 雙語法分析器併行驗證,把「所有支援環境下行為一致」從查證變成測試套件裡的斷言
  • TDD 的「紅燈先於實作」被寫進 PR 檢查清單,變成可驗證的流程步驟,不是原則性呼籲

明日預告

第二部到此告一段落。明天進入第三部:社群溝通——AI 能不能代替維護者回覆 issue?該不該?


上一篇
Day 15:Feature 開發——AI 從 issue 討論到實作一個新功能的完整過程
下一篇
Day 17:社群溝通——AI 能不能代替維護者回覆 issue?該不該?
系列文
讓 AI Agent 維護一個 Open Source Project21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言