軟件工程實踐系列文章, 會着重講述實際的工程項目中是如何協做開發軟件的。 本文主要介紹如何使用 Git 來支撐整個開發流。html
本文包括如下內容:git
operation: 團隊保持一致的操做github
tool chain: 搭建自洽的工具鏈web
conclusiondocker
Git 提供了一套自由而又強大的 api, 咱們能夠經過它以各類姿式來完成代碼協做。 但俗話說得好, 選擇越多人越懵。並無這句俗話 因此在團隊協做時, 保持一致的操做是很重要的。api
Git 的最小單元就是一個 commit。 咱們團隊遵循的最佳規範是保持每一個 commit 的原子性: 一個 commit 只作一件事情。markdown
好比一個關於缺陷修復的 commit 能夠很是簡單, 它只有兩行: 一行修復了代碼邏輯, 一行加了單元測試。框架
另外一個關於重構的 commit 可能會修改 200+ 個文件, 但也由於只作了一件事情, 因此不會給 code review 帶來很大負擔。工具
原子性的 commit 不只能很好地支持 revert/cherry-pick/bisect 等一系列 Git 的原生命令, 並且在保持線性乾淨的歷史這一點上, 也是相當重要的。oop
隨着時間的推移, Git 的每個 commit 會成長爲一個枝葉繁茂的歷史樹。 基於 fast forward 的合併能讓 Git 的歷史樹保持乾淨與線性。
這是咱們項目 git log 翻到四個月之前的一張截圖, 能夠看到歷史依舊是線性乾淨的。
線性的歷史意味着在每一個人提交代碼前須要 rebase。 一個例外是在發佈分支(master)上提交 hotfix 後, 合併回開發分支(dev)須要視狀況關閉 fast forward (–no-ff)。 並且要求你們 rebase 則對團隊成員的 Git 水平以及合併習慣提出了必定要求。
乾淨的歷史則須要你們都嚴格遵循 commit 的原子性, 以及要按照標準撰寫 commit message。 關於 commit message 的撰寫, 阮老師有一篇《commit message 編寫指南》講的夠好。 咱們也能夠在 git hook 中開啓對 commit message 的校驗, 以確保格式的整潔統一。
經過fast forward merge + 統一的 commit message, 咱們就能維護一個不斷成長、但又幹淨線性的歷史樹, 能最大程度地給各類 git 的版本操做提供便利。
咱們使用 git tag 來做爲版本發佈的標誌。
由於咱們的項目是一個 web 服務端項目, 咱們同一時刻基本上只須要維護最新的版本, 因此咱們使用了日期型的版本號數字(v2019.9.9
)。 在大部分開源工具裏, 都會使用語義化的版本號(semver: v2.0.0)。
基於原子的 commit、線性的歷史, 在每次版本發佈時, 咱們都會自動化地生成 tag/changelog。
一個遵循了良好規範的 tag 能最大程度地利用工具鏈的集成功能, 給開發、測試、上線、監控提供完備的功能。
在注重團隊協做、開發流程、發佈質量的軟件工程中, 會有一系列開發工具圍繞 Git 的代碼歷史樹, 提供了自洽的工具鏈。
咱們使用 GitLab
來管理代碼項目, 其中重度使用的是 Merge Request
來做爲 code review 的載體。 不過咱們跟着 GitHub 的設定,管它叫 PR (Pull Request)
爲了維持 PR 的原子性, 大部分狀況咱們遵循單個 commit 對應單個 PR, 這樣既方便 review 也方便分支管理。 不過這樣的開發流會產生很是多的 PR, 須要團隊開發者都保持在一個更積極的開發狀態下。
現代的開發平臺還會集成更多工做流上的功能, 咱們還重度使用的是把 GitLab Runner 做爲咱們 ci/cd 的工具載體。
在提完 PR 之後, 系統會自動化地啓用一系列的 Python/Django 檢查。
經過 git tag 發佈版本後, 系統會自動化地跑構建、灰度等一系列部署任務。
除了 GitLab Runner, 其它像 Jenkins/Travis CI/GitHub Actions 也能夠相似的 ci/cd 功能。
集成 ci/cd 能讓整個開發流變得更加柔順, 讓每個改動的影響均可以即時地經過數據展現出來。 ci/cd 加上 code review, 能最大程度地讓代碼庫保持活性, 長遠地能避免「往屎山上堆屎」的發生。
前面提到咱們的 PR/release 頻率都會比較高, 因此 ci/cd 也須要在更短的時間內跑完, 以免龜速檢測致使的各類心態爆炸、skip ci。 目前咱們項目平均能在 5min 內跑完包括 96% 覆蓋率的單元測試的多項檢查, 也能在 5min 內跑完從構建、灰度到全量的整個發佈流程。
整個 ci/cd 的流程其實也跟寫接口這樣的業務相似, 是須要不斷迭代、不斷優化、不斷適應更好的開發流的。
在開發的流程走完之後, 軟件工程還關心發佈的流程、質量把控的流程。
咱們綁定了 docker image tag 與 git tag, 最終發佈部署在 k8s 上的每個版本也跟 git tag 的版本強關聯。 這樣的設定之下, 好比像 kubectl rollout
的一系列操做就會跟 Git 歷史樹關聯上。
> 咱們也使用了 sentry 來檢測管理代碼的線上問題。
全部的外部系統都使用着統一的 tag version 來關聯問題, 這樣咱們就給 debug, 歷史溯源,分鍋都提供了統一的工程語言。
基於 Git 的開發流不是一個一成不變的框架, 它會由於項目特質、團隊成員習慣、工具鏈的不同而有着不一樣的表現形式。
咱們團隊在軟件工程的實踐中, 保持並維護着這麼一套開發標準:
這套開發流也給咱們提出了這些挑戰:
這樣的 Git 開發流,最終帶來了這些效果:
軟件工程的實踐中, 好的 Git 開發流是不可或缺的一部分。 之後咱們再以不一樣的視角來分享更多軟件工程的實踐。
(完)