Go 的高效開發套路
作者:EdwardQ
來源:SegmentFault 思否社區
背景
當前在公司進行 Go 服務端研發工作時,發現缺少 Go 開發的最佳實踐,而導致以下現象
-
用 Go 開發時會比較迷茫,不知如何下手,怎麼開展工作比較高效。
-
重複造輪子比較嚴重。
-
項目的代碼質量參差不齊,導致交付的產品質量參差不齊。
-
產品運行黑盒,可觀測性差,能跑就行。
-
代碼實現考驗研發人員水平,但頂尖的畢竟是少數,往往比較差,而且頂尖也說不準會犯錯。
-
一個人負責整個功能開發,一旦人員離職,代碼維護就會難上艱難。
而我在進行產品研發工作時,意識到後續產品的功能會愈加複雜,不注重這些問題的話,後續恐怕出現代碼愈加難以維護、產品功能難以改動、用戶需求無法快速滿足的多米諾效應。
爲了儘早制止該現象的發生,探索一套可以在 Go 領域進行最佳實踐的範式,從而達到以下效果:
-
代碼易維護,起碼有兩個人熟知代碼實現目標,也容易進行代碼迭代更新。
-
代碼設計合理,避免過於奇葩、過度以及不全面的實現。
-
產品質量保證,交付產品可觀測性強、Bug 率低。
-
降低重複造輪子現象,專注於業務邏輯開發,提升研發效率。
故此從研發效能方面研究解法,探索從個人開發行爲到團隊協作上的提升,最終在研發代碼質控以及研發框架兩個方面去實踐各種方法,最終總結出當前的高效開發的模式,並持續優化沉澱中,希望通過實踐驗證該模式的效果,推廣至所有使用 Go 進行服務端開發的場景實現,提高整個團隊研發效能。
模式簡介
本模式有三個部分組成,分別爲標準化研發規範、契約協作以及統一的開發框架。
-
標準化研發規範旨在研發人員在進行代碼開發時能夠從代碼編程風格、Git 提交規範、Review 機制能達成共識契約並嚴格遵守,爲產品的質量負責。
-
契約協作旨在研發人員與外部需要調用其服務的合作伙伴,形成以契約爲準,雙方工作開展不互相強依賴具體實現的協作模式,降低雙方溝通成本。
-
統一的開發框架旨在約束研發人員使用統一的框架開發,沉澱各種場景實現用於優化並演進框架,達到開發時可快速複用場景、持續迭代優化各場景實現以及爲未來更多場景做堅實的積累。
模式實踐 - Go 服務端開發
當前在實際開發業務中都有相應的實踐
標準化研發規範
代碼編程規範
在接手一些組件的開發時,看到代碼的實現上存在風格迥異,閱讀理解上比較困難,所以定下約束風格的編程規範,可以提升代碼的可閱讀性,代碼的易維護性。
在探索 Go 代碼編程規範中,業界有 EffectiveGo 和 UberGoGuide 等比較知名的編程規範。
-
EffectiveGo 作爲 Go 官方出品的編程規範,幾乎所有使用 Go 語言開發的程序員都有看過並從中獲益,普適性高。
-
UberGoGuide 是業界中使用 Go 語言開發的佼佼者,其開源了很多高質量的開源軟件,比如 Zap;它所開源的編程規範也是在 EffectiveGo 以及其他編程規範作爲基礎上去擴展。
從綜合考慮上,Uber 有中文翻譯版上手速度快,且其公司按照規範開源的軟件質量實現也側面證明了該規範的優越性,所以選擇 UberGoGuide 作爲指導編程規範,但並不作爲唯一參考標準,也考慮可以根據實際運行的最佳實踐作爲相應變動。
Git 提交規範
在團隊協作開發組件時,查看到 Git 的提交信息很隨意,僅僅簡單描述幾句,但完全不知道實際做了什麼。在回溯代碼問題時,不知道這個提交做了什麼,對代碼維護上造成一定阻礙。估計參考業界優秀統一規範 Conventional Commit 1.0 進行約束。
Conventional Commit 1.0,是一種規範提交信息的輕量級規約,它提供了一些易於理解的規則用於指導我們如何編寫 commit 信息,因此這也很容易被機器進行解讀,結合 SemVer 版本規範,可以很容易通過工具既能自動化管理版本號,也進一步約束開發人員對提交信息的嚴謹性,畢竟如果亂打 commit 信息會直接影響到版本的制定。
因此在實踐中上,嚴格遵守 Conventional Commit 1.0 來進行提交信息的規定,對於提交信息不明確者也可以拒絕其代碼提交。
Review 機制
在原先的團隊合作模式中,大部分是各自開發相應的模塊並直接提交到 Master 分支,然後就發佈了,這樣的做法雖然速度很快但交付質量卻很堪憂,畢竟它很考驗研發人員的綜合水平,但畢竟不是人人都是神,所以它無法避免到缺陷的數量增長,不僅對整體代碼質量堪憂,甚至對交付產品的質量也無法保證,更甚會影響到整個團隊的口碑。爲了解決這些問題,引入 CodeReview 機制來管制代碼合併到代碼交付的過程,從而管控整個代碼質量。
CodeView 機制參與的人員角色有代碼提交者(Commiter)以及代碼審覈者(Reviewer),代碼提交者每次提交代碼後,均需要由代碼審覈者進行代碼,審覈通過後才能合併代碼進主分支,從而達到可發佈的可能性。
這樣的機制實行下,需要可以做到以下方面效果:
-
使研發人員在每次提交中,有其他視角去驗證代碼實現方案、解決思路,保證一定的客觀性。
-
擔任 Reviewr 角色的人員,需要在過程做到識別 bugs、是否有邏輯問題還有是否有覆蓋全邊緣場景,這樣可以保證實現的思路足夠全面。
其好處列舉如下:
-
知識共享:團隊內部在進行 Review 就能知道代碼的實現、使用的設計等進行共享,有利於團隊代碼水平的提高。
-
可以更早的發現 bug:避免在功能推出後才發現 Bug,做到及時止損。
-
確保合規性:程序員的背景不同導致各自的代碼風格不同,但通過規範標準約束項目代碼的合規性。
-
增強安全性:當有安全技術背景的專業人員參加到 review 中時,安全性的等級是非常高的。
-
增強團隊合作意識:當團隊成員一起去解決一個問題,這樣有利於提升他們各自的 OwnerShip 思維以及團隊的歸屬感。
當前也需要做到以下約定:
-
Reviewr 和 Commiter 在一次提交上,嚴格意義上不能爲同一個人。
-
Comment 時需要是善意的就事論事,雙方均需要以案例和嚴謹的邏輯推敲去進行討論,而且只有 Reviewr 同意後才能終止 Comment。
-
提交代碼前需要自身已經經過編譯以及測試。
-
一次提交儘量做到核心代碼不超過 400 行的。
-
一次 Review 儘量低於 1 小時。
-
需要知會到 Reviewr 清晰知道這次提交的實現目的,最終期望是什麼。
-
如果害怕提交的代碼寫的不好,那就將代碼實現達到自身認可的程度先。
-
單次提交如果代碼量過大或邏輯複雜可以設置兩個以上的 Reviewer 進行 Review。
-
Reviewer 和 Commiter 存在連帶責任,Commiter 提交的代碼在生產出現問題並出現價值損失時,Reviewer 也需要承擔次要責任。
-
Review 機制很容易會被忽視並繞過,但其後果有時候是很慘痛的,所以需要團隊人員一致認可並遵守,才能做得好。
契****約協作
PB 文件既契約模式
在開發人員進行 API 開發到完成後,往往都需要提供文檔給別人去調用,這樣的做法有以下缺點:
-
接口文檔交付緩慢,接口需求方需要等待接口文檔給出才能進行開發,這樣對於需求方來說會更加開發風險會增加。
-
接口更新不及時,當開發人員在更改 API 時,90% 的概率會忘記更新文檔,導致第三方在調用時出現很多狀況需要溝通,這樣增加雙方的溝通成本,降低了雙方的開發效率。
爲了解決以上問題,推行 Protobuf 文件(以下簡稱 PB 文件)既契約的模式。
該模式遵循 Google API 指南,實現了對應通信協議支持,並且遵守了 gRPC API 使用 HTTP 映射功能進行 JSON/HTTP 的支持。因此可以做到通過定義 PB 文件即可定義出 REST 和 RPC API,通過類似 GoogleAPI 的倉庫方式進行 API Schema 的管理。
再者結合豐富的 Protoc 插件可以自動化 Swagger 文檔或生成不同語言類型的代碼,如下:
-
Go HTTP API:通過 protoc-gen-go-http 插件進行生成 Go 版的 Http 相關代碼
-
Go gRPC API:通過 protoc-gen-go-grpc 插件生成 Go 版的 gPRC 相關代碼
-
Swagger 文檔:通過 protoc-gen-openapiv2 插件生成
因此,研發人員通過 PB 文件即可清晰知道 API 的定義,參數、返回內容等 bin 並能依據 PB 文件生成相應的代碼(Server 和 Client)就可進行各自的開發,也可以直接生成 Swagger 文檔來查看使用,極大的降低了溝通的成本。
該模式也需要遵守以下規則:
-
新契約變更需要通知到對方
-
已運行一段時間的舊契約遇到 BreakChange 需求,應考慮新起契約,不應在原有基礎改動。
-
新契約定製時需先通過需求方 Review 後,再繼續開發,避免返工,也保證需求實現準確性。
-
契約儘量以清晰簡潔明瞭的結構體定義爲優先,對於特殊需求再使用 Map、PBValue 等。
統一的開發框架
對於框架的入門使用有另外一篇文章進行指導
開發框架選型
在過往的編程經驗中,對一個框架的積累沉澱有助於提升整體的開發效能,畢竟前人栽樹後人乘涼,前人已經摸清楚框架的使用,就有足夠的信心指導後人以此進行開發、優化框架代碼以及建立起整套的周邊生態,可以有餘力應付未來的複雜業務場景。
因當前團隊人力有限僅 4 人,即使有心也無力去做到自研框架這種需要大量腦力心力且需要積累沉澱的工作,於是放棄自研框架,選擇借力開源產品。
在從框架選型上,從當前團隊需求場景考量,團隊需要的是一套可以約束實現規範、生態完善、用戶自主性強、後臺性能要求不高以及支持微服務化的框架。
在 Go 這邊選型中預選了 GVA、Gin 和 Kratos 作爲比對並進行實踐,總結 GVA、Gin 以及 Kratos 三者比對如下
-
Gin, 爲一個基礎輕量級高性能的 Web 框架,實現場景案例較多,但代碼實現無約束規範需要靠開發者自己摸索,普適性較強。
-
GVA,是一個前後端配套的框架,主要解決是快速開發 web 應用,但框架代碼實現上並沒有很好的理論指導以及規範約束,整體使用後的代碼質量較差難維護,應對複雜業務需求能力差,最讓人詬病的是濫用的公共變量。
-
Kratos,bilibli 業務驗證出品,配套健全的微服務框架,設計上遵循整潔架構以及 DDD 思想,實現的模塊耦合度低,用戶自主性強,應對複雜的業務需求能力強,但總體設計理念上傾向於標準規範定製,整體性能方面中上,需自己擴展模塊實現更高性能。
最終採用 Kratos 作爲統一的開發框架,具體決策依據如下
-
多協議:默認支持 HTTP/gRPC,可自主按照規範定製擴展,支持採用 gin 來實現 transport 核心。
-
API 契約理念:支持 PB 文件定義,可生成服務端、客戶端代碼以及 Swagger 文檔,並提供在線實時文檔功能。
-
DDD 思想分層開發:指導開發人員進行開發時,能按照領域驅動的方式進行開發,讓功能更加聚焦,實現更加精煉,且耦合度低,比如將 ClickHouse 替換爲 SLS 時只需要改動基礎設施層實現即可。
-
框架結構模塊化清晰:幾乎涵蓋微服務框架開發中所涉及的模塊,比如認證、註冊、自監控等,並這些模塊均可自定義實現,適配性強。
-
高擴展性的中間件設計:極強擴展性的中間件設計,靈活擴展各種業務所需中間件模塊。
-
框架代碼質量:總體代碼質量上層,模塊依賴解耦分明,用戶閱讀上手容易,自主定製優化可行。
-
性能方面:現在業務暫時或將來 1~2 年均以 Web 後端應用以及微服務爲主,所以性能要求方面不苛刻,且如需高性能時也可自主實現擴展模塊實現。
開發框架沉澱
當前組內已在 Kratos 框架有一定的積累,爲後續提高後端開發提高很好的基礎,具體如下:
-
Layout 快速開發:依靠 Kratos 的 Layout 能力,定製符合自身的 Layout 模板,在後續開發中可以快速使用,當前 Layout 實現上已包含各層的寫法模板、Prom 指標集成、Opentelemetry 全鏈路集成、本地緩存實現等。
-
自定義 API 文檔信息的寫法指導:已充分踩坑如何豐富 API 文檔,對於任何自定義信息需求均能滿足。
-
快速使用 Kratos 開發的指南:配合 Layout,助力快速上手框架,快速進行業務開發。
-
通過在線 API 文檔生成前端 SDK:通過 Swagger-gen 快速生成前端 SDK,前端無需手寫 SDK,可維護強。
之後也會沉澱更多的場景到框架和文檔中,豐富框架的使用場景,以助力開發提效。
實踐效果
當前將該模式在內部項目中實踐,有以下效果提升
-
Bug 情況:在經過次實踐後代碼級別的 bug 數量從項目開始到現在低於 10 個,需求級別 Bug 數量低於 15 個。
-
前後端協作效率:前後端當前溝通協商基本只在需求協商、協議定義和實際聯調階段進行協商,整體交付功能速度均低於 2 個星期。
-
特性完成速度:後端服務交付實現特性,均低於 5 天一週期,項目進展卡點不在後端實現上。
-
團隊合作氛圍:review 機制的加入,讓團隊成員更加富有凝聚力以及更願意做知識分享,提升了團隊人員整體能力。
-
交付服務自監控完善:落地全鏈路監控,提升前後端全鏈路可觀測性,定位問題低於 5 分鐘內。
總體的效果是符合預期設計的,但還有許多優化的空間比如 API 管理、Error 規範、CICD 標準化,將繼續沉澱優化該模式,提升後續特性以及未來新項目的交付速率,提升開發人員幸福感,降低開發成本。
Reference
-
https://github.com/uber-go/guide/blob/master/style.md
-
https://go.dev/doc/effective_go
-
https://www.conventionalcommits.org/en/v1.0.0/
-
https://semver.org/
-
https://www.bookstack.cn/read/API-design-guide/API-design-guide-08.md
-
https://google.aip.dev/
-
https://developers.google.com/protocol-buffers/docs/style
-
https://developers.google.com/protocol-buffers/docs/proto3
-
https://colobu.com/2017/03/16/Protobuf3-language-guide/
-
https://go-kratos.dev/docs/
-
https://about.gitlab.com/topics/version-control/what-is-code-review/
-
https://www.perforce.com/blog/qac/9-best-practices-for-code-review
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/PVhVGelnv0XdjHc4EOAVJg