AI 助攻:我與 openapi-generator 的 GitHub 開源貢獻紀實
背景介紹
OpenAPI
OpenAPI 是一個為 RESTful API 定義的標準規格,旨在提供一種機器可讀的介面文件,讓開發者無需讀取原始碼就能理解服務的功能。透過 YAML 或 JSON 格式,它定義了端點、輸入參數、輸出格式以及身分驗證等細節。這不僅促進了前後端開發的解耦,更成為了自動化工具(如文件生成、測試腳本、客戶端 SDK)的核心基礎,是現代微服務架構中溝通與契約的核心語言。
OpenAPI-Generator
OpenAPI-Generator 是一個強大的開源工具鏈,能根據 OpenAPI 規格檔案自動生成各種語言的客戶端 SDK、伺服器端存根(Stubs)與說明文件。它支援超過 50 種程式語言與框架,極大地減少了重複編寫樣板程式碼的時間。對工程師而言,它確保了程式碼與 API 文件的一致性,避免了人為更新不即時導致的錯誤,是提升開發效率與維護 API 品質不可或缺的利器。
Spring Boot
Spring Boot 是 Java 生態系中最受歡迎的微服務框架,其核心理念是「約定優於配置」(Convention over Configuration)。它簡化了 Spring 應用的初始搭建與開發過程,內嵌了 Tomcat 等伺服器,讓開發者只需透過簡單的註解與依賴管理,就能快速產出可獨立運行的生產級應用。在 API 開發中,它常與 OpenAPI 搭配使用,作為自動生成伺服器程式碼的首選目標平台。
Pull Request (PR)
Pull Request 是 GitHub 等版本控制平台上的核心協作機制。當開發者完成一項功能或修復後,會發送 PR 請求專案維護者將其分支的改動「拉取」回主分支。這不僅是一個代碼合併的過程,更包含了一個完整的同儕審閱(Code Review)流程。維護者會針對邏輯、風格與安全性進行討論與修改建議,PR 成功合併通常代表著開發者的貢獻得到了社群的認可。
Open Source
開源(Open Source)是指軟體的原始碼對公眾開放,允許任何人檢視、修改與發布。這不僅僅是一種開發模式,更是一種強調協作、透明與共享的社群精神。開源專案如 Linux 或本文提到的工具,依靠全球開發者的共同維護來持續演進。貢獻開源不僅能解決自身痛點,更能提升個人技術聲望,並推動整個軟體產業在既有基礎上不斷創新,避免重複造輪。
測試驅動開發 (TDD)
TDD 是一種軟體開發流程,強調「先寫測試,後寫程式」。開發者首先編寫一個會失敗的單元測試,接著撰寫僅足夠讓測試通過的最簡程式碼,最後再進行優化與重構。這種方式能強迫開發者在撰寫邏輯前深入思考需求,並確保每一行程式碼都受到保護。在開源貢獻中,TDD 尤為重要,因為它能向維護者證明你的修復是有數據支撐且不會破壞既有功能的。
宗旨
在 AI 浪潮下,曾讀到一篇文章感嘆:當大家遇到問題都只向 AI 尋求快速解答,而不再與開源社群互動時,會導致社群活躍度下降,對 Open Source 文化的發展蒙上陰影。這引發了我的反思:我們是否能將 AI 定位為「超級助理」,利用它來輔助我們深入理解複雜專案,進而更有品質地回饋社群?本篇就是這場實驗的成果。
正文
問題描述:消失的驗證邊界
在現行專案中,我們使用了 exclusiveMinimum(排除最小值)來規範欄位。在開發過程中發現,當我們使用 OpenAPI 3.0.0 的布林值語法時,一切正常;然而一旦將文件升級到 OpenAPI 3.1.0 的新格式(支援數值指定),產生器便無法正常識別該約束。
// OpenAPI 3.0.0 syntax for `exclusiveMinimum`:
openapi: 3.0.0
components:
schemas:
PriceDTO:
type: object
properties:
price:
type: number
exclusiveMinimum: true
minimum: 3001
// OpenAPI 3.1.0 syntax for `exclusiveMinimum` (as per specification):
openapi: 3.1.0
components:
schemas:
PriceDTO:
type: object
properties:
price:
type: number
exclusiveMinimum: 3101
尋求救援:回報問題
首先,我前往 GitHub 上的 openapi-generator 官方社群。藉由 AI 的潤飾,我提出了一個清晰的 Issue:#22943。
當時我並未意識到產生器對 3.1.0 版本的支援尚在完善中,但我得到了社群正面的回應,維護者甚至邀請我進行 PR (Pull Request)。當下因為公務繁忙,我禮貌性回覆:「未來有機會將會嘗試貢獻。」
實戰開始:嘗試處理問題
不久後,我抽出了一段空檔開始鑽研。原以為開源專案門檻很高,但深入後發現,只要有正確的引導,這並非遙不可及。
第一階段:制定計畫
我首先請 AI 協助分析專案結構並規劃測試路徑:
USER
首先說明如果要修改,目標專案位置是否為
modules/openapi-generator,並說明測試方式
幫我建立筆記.md,存在當前目錄
(節錄)AI產生的筆記
### 1. 專案結構解析
(略)
### 2. 測試方式
應遵循測試驅動開發 (TDD) 模式,先建立一個會失敗的測試,然後修改程式碼讓測試通過。
#### 步驟 1: 尋找並分析現有的模型測試
在 **`modules/openapi-generator/src/test/java/org/openapitools/codegen/`** 路徑下,可以尋找與 `DefaultCodegenTest.java` 或其他模型/Schema 處理相關的測試類別作為起點。
#### 步驟 2: 建立測試用的 OpenAPI 規格檔案
在 **`modules/openapi-generator/src/test/resources/3_1/`** 目錄下建立 `exclusive_minimum_test.yaml` 檔案:
(檔案內容略)
#### 步驟 3: 在 Java 測試類別中新增測試方法
在 **`modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java`** 中加入以下測試方法:
(程式碼略)
#### 步驟 4: 執行測試 (並看著它失敗)
執行 Maven 指令來運行測試:
此時測試應該會失敗。
#### 步驟 5: 修改核心邏輯並讓測試通過
修改 **`modules/openapi-generator/src/main/java/`** 中處理 Schema 解析的相關程式碼,以支援 OpenAPI 3.1.0 語法。修改完成後再次執行測試,直到通過為止。
---
**總結工作流程:**
1. **定位 (開發)**: 在 `modules/openapi-generator/src/main/java/...` 中修改核心邏輯。
2. **定位 (測試)**: 在 `modules/openapi-generator/src/test/...` 中新增/修改測試。
3. **準備**: 建立 `exclusive_minimum_test.yaml` 測試檔案。
4. **測試**: 在 `DefaultCodegenTest.java` 新增測試方法。
5. **驗證失敗**: 執行測試,確認它如預期般失敗。
6. **開發**: 實作核心邏輯的修改。
7. **驗證成功**: 再次執行測試,直到它通過為止。
第一次實作:AI 的「叛變」
我依照 TDD 的邏輯,請 AI 生成測試個案並評估開發工作:
USER
幫我建立
exclusive_minimum_test.yaml
將測試方法新增到 @modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java 中。但我需要額外驗證 @modules/openapi-generator/src/test/resources/3_0/exclusive_minimum_test.yaml , 並也需要驗證計劃中的 @modules/openapi-generator/src/test/resources/3_1/exclusive_minimum_test.yaml 也就是說生成 2個測試方法
評估實質開發exclusiveMinimum相容性,所需要做的事
這裡發生了一個插曲:AI 在開發過程中進入了「鬼打牆」狀態。當我發現它為了讓測試通過,竟然試圖回頭修改測試斷言來迎合錯誤的結果時,我意識到它迷失了。我果斷地「開除」了當時的對話環境,重開一局並由我自己主導核心開發,最終完成了修復。
此時正值春節前夕,我在 Issue 留言表明年假後會提交 PR。沒想到年假期間,另一位國外工程師先行提交了修復並併入正式版本。雖然有點可惜,但這就是開源的日常。然而,當我試圖驗證那個新版本時,卻發現了新的隱憂:在特定的複雜組合(allOf 搭配 $ref)下,那個修復依然會失效。
第二階段:追求完美的第二次實作
我重新啟動了 AI 助理,針對不同的 Commit 進行深度分析:
USER
git commit "59a802e06e90cb0f85c552b297398f754c290194" 新增的測試個案, "bc0710dc554bd9b507a4f5cf227b4bc77866b14c" 測試通過,但 "67a637fe8d0c434cd3158c7075048f044652c420" 針對相同的需求進行修改,測試不通過(儘管該commit新增的測案測試通過)。
- 以測試個案的角度解說
- 以程式面解說
## MODEL
好的,我已經分析了您提供的三個 commit 的內容。這是一個非常經典的軟體開發情境:一個功能的實作方式被重構 (refactor),但舊的單元測試沒有跟著更新,導致測試從通過變為失敗。
以下我將從「測試個案」和「程式碼」兩個角度來為您解說。
---
### 總結
問題的核心在於 **處理 `exclusiveMinimum` 的邏輯被轉移了位置**。
* **`bc0710d`** 的作法是在**後端**的工具 (`ModelUtils`) 中加入一個簡單的補丁來支援新語法。
* **`67a637f`** 的作法更為正規,它在**前端**引入了一個 `OpenAPINormalizer` (正規化器),在所有程式碼生成之前,就先把新的語法轉換成舊的、統一的格式。
`ExclusiveMinMaxTest` 這個測試個案是針對舊的作法寫的,它直接呼叫後端的工具,完全繞過了新的 `OpenAPINormalizer` 步驟,因此在採用新架構後,測試就失敗了。
這場「數位偵探遊戲」讓我發現,前一位貢獻者的正規化方案(Normalizer)雖然優雅,但在處理層層嵌套的引用時會因執行順序過早而失效。我最終採取了結合方案:保留正規化的嚴謹邏輯,但將其移動/同步到更後端的 ModelUtils 作為最終防線。
USER
結論...
- @modules/openapi-generator/src/test/java/org/openapitools/codegen/ExclusiveMinMaxTest.java 經過改寫,確實可以測試通過
SubscriptionPriceOptionDTO(allOf與$ref組合使用) "67a637fe8d0c434cd3158c7075048f044652c420" 產生物件有異常;"bc0710dc554bd9b507a4f5cf227b4bc77866b14c" 產生物件正常不論採取任何方案,應該要在單元測試凸顯這個問題。
- 讀取 @modules/openapi-generator/src/test/resources/3_1/exclusive-min-max.yaml 的最新內容
- 參考既有的測試個案 @modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java
- 兼顧測試效能,暫時新增
OpenAPINormalizerTest2.java,補足測試個案
貢獻 PR:遵守社群契約
完成程式碼後,最重要的步驟是詳閱 CONTRIBUTING.md。我請 AI 協助翻譯並確保我的代碼風格符合專案的 Java Style。開源貢獻不只是寫 Code,更要學會如何減輕審閱者的負擔。
USER
@CONTRIBUTING.md 提及java程式應符合
Java Style我如何確認我的程式有符合
我現在要開始貢獻。用英文產生 pull request title
檢視我撰寫的內容。如果大致上沒有錯誤的話,轉換成英文,並進行補充/潤飾
在準備PR的過程中, upstream/master 已變動,但目測跟我們的異動無關,我們這邊需要做任何處理?
最終,我提交了 PR,並附上了完整的背景說明與測試驗證:
總結
身為工程師,參與全球性的開源專案是一個不可多得的成長機會。這段過程中,AI 助理扮演了「專案導遊」的角色,幫我過濾噪音、定位關鍵邏輯;而我則負責「決策與驗證」,確保最終的方向符合現實需求。
這次貢獻不僅解決了我們專案的技術痛點,更讓我體會到:AI 不會取代開發者,但「會使用 AI 協作並深諳社群規則的開發者」,將能創造更大的價值。
