[版本控制] 關於版本控制最佳實踐

在軟體開發的世界裡,版本控制(Version Control)幾乎每天都會碰到。然而,雖然大家天天都在使用,但每個人的習慣卻大相徑庭。有的人把版控當成雲端硬碟,提交極度隨興;有的人則是完美主義,不修到極致絕不提交。最近在職場上的觀察讓我感觸良多,於是決定留下當初編寫教育訓練的內容作為紀錄。

為什麼需要版本控制(Version Control)?

按照慣例,既然要傳教,那就得先討論為什麼得做這件事,畢竟那些亂提交似乎也沒有出事情啊?

這就要回歸到開發情境了,你們是一人作業?或是多人協作?是大公司?是小團隊?如果開發團隊尚處於較原始的狀態,我認為建立文化比較重要,先讓成員間明白版控的意義,之後即使規則緊縮,也通常自然而然能接受,太過於細節的規定反而無益初期的擴張。

然而若團隊是有點規模的,卻又缺乏共識,那麼每一筆提交都可能成為日後的未爆彈,因此隨著規模、文化的不同,各團隊各自都有一套規則。

以下幾點是我最常在開發中受益於版本控制的情境:

  • 錯誤追蹤以及復原
  • 並行開發
  • 誰要扛責(殺氣騰騰地用 blame 結果都是我自己 QQ)

可以想像它就是文字版的時光機,我可以很輕易地挑選要回去的時間,並且細細觀看當時究竟發生了什麼事情。至於更進階的用途,這邊先不多提,畢竟本篇目的是為了讓大家建立起基本的協作共識,並在日常開發中養成良好的習慣。只要能先掌握好基礎,未來無論是面對 CI/CD 自動化或是更複雜的分支模型,都能更輕鬆地銜接上去。


該不該納入版控系統?

當然不是每個東西都需要上版控,不然版控系統或是大家的空間絕對會在某刻拉取的時候爆炸。

通常可以以是否頻繁變動、是否須協作、是否有回溯版本需求等為考量,如果沒有強烈需求,其實你需要的是 NAS 或雲端空間,並且做好備援機制,而不是一味地放到版控系統。以下以軟體開發為例:

該放版本控制的檔案

  • source code 絕對是要放的!!
  • build script 與其他建置所需的檔案(e.g., Makefile, pro, .build.grandle,…)
  • 文件(e.g. README、LICENSE、使用者手冊)
  • 必要的設定檔
  • 程式所需要的檔案、圖片
  • 測試案例或是測試資料

不該上版控的檔案

  • 可再產生的檔案
  • 建置後的產物(e.g., 編譯過後的 binary 檔或是可執行檔)
  • IDE 設定檔或是使用者的個人設定檔
  • 暫存檔或是暫存的目錄、日誌(e.g., .tmp、.log)
  • 外部文件
  • 不屬於 source code 的一般大型二進位檔或是資料庫匯出檔
  • 敏感資訊,例如金鑰、密碼等等

提交頻率

說到提交次數和頻率,不太容易被標準化,因此不好武斷地說哪種好,哪種不好。尤其是當公司沒有規定時,大家都很隨性地照直覺走(?)但這同時也是很有趣的地方,可以藉此判斷同事和我頻率合不合,很像是什麼星座或是人格分析XD

但有一些大原則還是可以依循的:

  • 在檢查點進行提交,例如完成一個小功能、任務時
  • 提交前要確定專案可以成功建置
  • 經常執行拉取(或更新),尤其是要開始新任務或送出修改時

只要遵守好基本原則,基本上頻率是不太會差太多的。

不過提交頻率為什麼要特別拉出來討論呢?難道一切準備到位再上不是會更好嗎?這裡就要談回版本控制的意義了,記住我們是正在和別人合作專案。以上的原則,第一是為了避免有人偏離現實情況太久,造成閉門造車的狀況;再來是確保任何人來接手,都能根據 commit 紀錄快速進入狀況。

經常提交不代表盲目提交。

近期剛好遇到三十分鐘內在同一個項目提交四次以及提交二十次的狀況,原先以為前者天生神力,但仔細 code review 後,發現媽呀每一個提交都是災難,沒有一個能用的!後續 revert 這四版的可能性為零。而後者明顯是靠 AI 協作,每次提交的修改切得過份細且提交間隔不超過兩分鐘,雖然我不認為這是好的版控習慣,但某種程度上來說至少有遵守以上原則。

這局濫用版控系統的 AI 贏了,成本甚至還便宜許多。


如何寫出有意義的提交訊息

最近看到一個影片,內容在講述要怎麼訓練脫離媽媽的小牛喝水桶裡面的奶。訪問者跟牧場主人有以下對話:

:「為什麼這隻小牛不會自己喝牛奶?」

:「因為他不會自己喝牛奶。」

嘿謝謝解釋囉!

為什麼要寫?

好的提交訊息能幫助隊友(以及未來的你)快速理解代碼變更的原因

版控系統不是程式碼的備份空間,它是你專案的活歷史。

我們先來看看一些不好的案例,這些訊息在當下看起來可能無害,但一個月後,連你自己都無法理解當時發生了什麼事:

- Fixed bug on landing page(哪個 bug?)
- Changed style(改了什麼風格?)
- ooops(蛤?)
- I think I fixed it this time? (你確定?)
- (空白)(這是大忌中的大忌)

這些訊息無法提供任何洞察,會讓未來的除錯工作變得極其痛苦。
對比一下好的範例,好的訊息能讓維護者即使在一年後也能迅速進入狀況:

- Fix (landing-page): resolve layout shift issue on first render
- Style (button): unify padding and border-radius across components
- Fix: prevent crash when user logs in with invalid token
- Refactor (header): extract nav logic into separate component

實作指南

很簡單,只需要掌握以下原則即可:

  1. 原子化提交:將每個邏輯變更分開提交,不要把無關的修改打包在一起。
  2. 解釋「為什麼」:訊息應解釋改了什麼以及「為什麼」要這樣改。

常見的兩種格式

A. 結構化格式(Type (Scope): Subject

這能清晰地突顯開發意圖,必要時可加上詳細的內文 (Body)。

Fix (parser): handle missing offset in SutInstance config 

Previously, missing offset values caused a crash during parsing. Now it defaults to 0 and logs a warning instead. 

Issue: #456

Header: <type>(<scope>): <subject>

  • Type(必填):描述變更的類型(請參考後續的分類清單)。
  • Scope(選填):標注受影響的程式碼範圍(例如某個特定的模組或組件)。
  • Subject(必填):對變更內容的簡短描述,建議控制在 50 個字元以內。

Body建議

  • 解釋修改了什麼、為什麼修改,以及與修改前的行為差異。
  • 選填,但對於非瑣碎的重大變更,強烈建議撰寫。
  • 每行字數請限制在 72 個字元內。

Footer(選填)

  • 用於引用任務或議題編號(Issue IDs)。
  • 使用 BREAKING CHANGE 來描述不相容的變更以及對應的遷移步驟。

常用的提交類型:

標籤 (Type)說明
Feat新功能 (New Feature)
Fix修補 Bug
Docs文件變更
Style代碼格式、風格調整(不影響邏輯)
Refactor重構(既非功能新增也非 Bug 修復)
Test新增或更新測試案例
Chore例行雜事(建置腳本、CI/CD、依賴包更新)

B. 清單格式- Type: Description

適合快速記錄多個相關的小變動。

  • Add: support for pin assignment in UI
  • Fix (parser): correct crash when file path is missing

常用的提交類型:

  • Add
  • Modify
  • Fix
  • Remove

針對新功能或修補使用分支(Branch)

為什麼要這麼做?

避免將所有修改直接在主要開發線(例如:main、trunk)上進行。為每個新功能或修補使用獨立的分支,有助於隔離工作環境、減少衝突,並在整合前讓程式碼審核與測試變得更輕鬆。

分支開發有助於保持主要開發線的穩定與隨時可發佈的狀態,同時允許多項任務並行進展而不會互相干擾。

實作指南

  • 隨時開啟新分支:每當開始處理特定的新功能、改進或 Bug 修復時,就建立一個新分支。
  • 保持分支專注:讓分支內的修改專注於該任務本身,避免混入不相關的變動。
  • 驗證後再合併:分支必須在經過審核與驗證後,才能合併回主要開發線。
  • 清晰的命名:為分支取一個清晰且有意義的名稱(或 ID),以反映其開發目的。
  • 視分支為沙盒:分支是你的實驗場,你可以安全地嘗試、大膽地實驗,且事後極易清理。

提交前的程式碼檢查(Review Code)

為什麼要這麼做?

在進行提交前,務必再次檢查你的修改。這有助於防止不必要的錯誤、保持紀錄的乾淨,並避免隊友為了追蹤原本可以避開的 bug 而困擾,或是更糟……讓他們變得很暴躁。

如何實作

  • 審視你的程式碼變更
  • 檢查修改過的檔案清單
  • 驗證格式與命名規範
  • 執行基礎測試或建置
  • 撰寫清晰的提交訊息,規矩你懂的。(如果不懂,請回到前一個章節!!)

後記

版本控制不只是備份工具,它是你與團隊成員、以及未來的自己溝通的方式(尤其是未來的自己)。

每一個提交、每一個分支、每一份被追蹤的檔案,都在講述一個別人能理解的故事。當故事清晰時,協作就會變得輕鬆,問題也更容易解決。

多花一點時間思考如何實踐,以及讓 AI 的每次提交都有意義。Every Change Counts!

讓我知道你在想什麼!