Go的高效開發(fā)套路
作者:EdwardQ
來源:SegmentFault 思否社區(qū)
背景
當前在公司進行Go服務端研發(fā)工作時,發(fā)現缺少Go開發(fā)的最佳實踐,而導致以下現象
用Go開發(fā)時會比較迷茫,不知如何下手,怎么開展工作比較高效。
重復造輪子比較嚴重。
項目的代碼質量參差不齊,導致交付的產品質量參差不齊。
產品運行黑盒,可觀測性差,能跑就行。
代碼實現考驗研發(fā)人員水平,但頂尖的畢竟是少數,往往比較差,而且頂尖也說不準會犯錯。
一個人負責整個功能開發(fā),一旦人員離職,代碼維護就會難上艱難。
而我在進行產品研發(fā)工作時,意識到后續(xù)產品的功能會愈加復雜,不注重這些問題的話,后續(xù)恐怕出現代碼愈加難以維護、產品功能難以改動、用戶需求無法快速滿足的多米諾效應。
為了盡早制止該現象的發(fā)生,探索一套可以在Go領域進行最佳實踐的范式,從而達到以下效果:
代碼易維護,起碼有兩個人熟知代碼實現目標,也容易進行代碼迭代更新。
代碼設計合理,避免過于奇葩、過度以及不全面的實現。
產品質量保證,交付產品可觀測性強、Bug率低。
降低重復造輪子現象,專注于業(yè)務邏輯開發(fā),提升研發(fā)效率。
故此從研發(fā)效能方面研究解法,探索從個人開發(fā)行為到團隊協(xié)作上的提升,最終在研發(fā)代碼質控以及研發(fā)框架兩個方面去實踐各種方法,最終總結出當前的高效開發(fā)的模式,并持續(xù)優(yōu)化沉淀中,希望通過實踐驗證該模式的效果,推廣至所有使用Go進行服務端開發(fā)的場景實現,提高整個團隊研發(fā)效能。
模式簡介
本模式有三個部分組成,分別為標準化研發(fā)規(guī)范、契約協(xié)作以及統(tǒng)一的開發(fā)框架。
標準化研發(fā)規(guī)范旨在研發(fā)人員在進行代碼開發(fā)時能夠從代碼編程風格、Git提交規(guī)范、Review機制能達成共識契約并嚴格遵守,為產品的質量負責。
契約協(xié)作旨在研發(fā)人員與外部需要調用其服務的合作伙伴,形成以契約為準,雙方工作開展不互相強依賴具體實現的協(xié)作模式,降低雙方溝通成本。
統(tǒng)一的開發(fā)框架旨在約束研發(fā)人員使用統(tǒng)一的框架開發(fā),沉淀各種場景實現用于優(yōu)化并演進框架,達到開發(fā)時可快速復用場景、持續(xù)迭代優(yōu)化各場景實現以及為未來更多場景做堅實的積累。
模式實踐-Go服務端開發(fā)
當前在實際開發(fā)業(yè)務中都有相應的實踐
標準化研發(fā)規(guī)范
代碼編程規(guī)范
在接手一些組件的開發(fā)時,看到代碼的實現上存在風格迥異,閱讀理解上比較困難,所以定下約束風格的編程規(guī)范,可以提升代碼的可閱讀性,代碼的易維護性。
在探索Go代碼編程規(guī)范中,業(yè)界有EffectiveGo和UberGoGuide等比較知名的編程規(guī)范。
EffectiveGo作為Go官方出品的編程規(guī)范,幾乎所有使用Go語言開發(fā)的程序員都有看過并從中獲益,普適性高。
UberGoGuide是業(yè)界中使用Go語言開發(fā)的佼佼者,其開源了很多高質量的開源軟件,比如Zap;它所開源的編程規(guī)范也是在EffectiveGo以及其他編程規(guī)范作為基礎上去擴展。
從綜合考慮上,Uber有中文翻譯版上手速度快,且其公司按照規(guī)范開源的軟件質量實現也側面證明了該規(guī)范的優(yōu)越性,所以選擇UberGoGuide作為指導編程規(guī)范,但并不作為唯一參考標準,也考慮可以根據實際運行的最佳實踐作為相應變動。
Git提交規(guī)范
在團隊協(xié)作開發(fā)組件時,查看到Git的提交信息很隨意,僅僅簡單描述幾句,但完全不知道實際做了什么。在回溯代碼問題時,不知道這個提交做了什么,對代碼維護上造成一定阻礙。估計參考業(yè)界優(yōu)秀統(tǒng)一規(guī)范Conventional Commit 1.0進行約束。
Conventional Commit 1.0,是一種規(guī)范提交信息的輕量級規(guī)約,它提供了一些易于理解的規(guī)則用于指導我們如何編寫commit信息,因此這也很容易被機器進行解讀,結合SemVer版本規(guī)范,可以很容易通過工具既能自動化管理版本號,也進一步約束開發(fā)人員對提交信息的嚴謹性,畢竟如果亂打commit信息會直接影響到版本的制定。
因此在實踐中上,嚴格遵守Conventional Commit 1.0來進行提交信息的規(guī)定,對于提交信息不明確者也可以拒絕其代碼提交。
Review機制
在原先的團隊合作模式中,大部分是各自開發(fā)相應的模塊并直接提交到Master分支,然后就發(fā)布了,這樣的做法雖然速度很快但交付質量卻很堪憂,畢竟它很考驗研發(fā)人員的綜合水平,但畢竟不是人人都是神,所以它無法避免到缺陷的數量增長,不僅對整體代碼質量堪憂,甚至對交付產品的質量也無法保證,更甚會影響到整個團隊的口碑。為了解決這些問題,引入CodeReview機制來管制代碼合并到代碼交付的過程,從而管控整個代碼質量。
CodeView機制參與的人員角色有代碼提交者(Commiter)以及代碼審核者(Reviewer),代碼提交者每次提交代碼后,均需要由代碼審核者進行代碼,審核通過后才能合并代碼進主分支,從而達到可發(fā)布的可能性。
這樣的機制實行下,需要可以做到以下方面效果:
使研發(fā)人員在每次提交中,有其他視角去驗證代碼實現方案、解決思路,保證一定的客觀性。
擔任Reviewr角色的人員,需要在過程做到識別bugs、是否有邏輯問題還有是否有覆蓋全邊緣場景,這樣可以保證實現的思路足夠全面。
其好處列舉如下:
知識共享:團隊內部在進行Review就能知道代碼的實現、使用的設計等進行共享,有利于團隊代碼水平的提高。
可以更早的發(fā)現bug:避免在功能推出后才發(fā)現Bug,做到及時止損。
確保合規(guī)性:程序員的背景不同導致各自的代碼風格不同,但通過規(guī)范標準約束項目代碼的合規(guī)性。
增強安全性:當有安全技術背景的專業(yè)人員參加到review中時,安全性的等級是非常高的。
增強團隊合作意識:當團隊成員一起去解決一個問題,這樣有利于提升他們各自的OwnerShip思維以及團隊的歸屬感。
當前也需要做到以下約定:
Reviewr和Commiter在一次提交上,嚴格意義上不能為同一個人。
Comment時需要是善意的就事論事,雙方均需要以案例和嚴謹的邏輯推敲去進行討論,而且只有Reviewr同意后才能終止Comment。
提交代碼前需要自身已經經過編譯以及測試。
一次提交盡量做到核心代碼不超過400行的。
一次Review盡量低于1小時。
需要知會到Reviewr清晰知道這次提交的實現目的,最終期望是什么。
如果害怕提交的代碼寫的不好,那就將代碼實現達到自身認可的程度先。
單次提交如果代碼量過大或邏輯復雜可以設置兩個以上的Reviewer進行Review。
Reviewer和Commiter存在連帶責任,Commiter提交的代碼在生產出現問題并出現價值損失時,Reviewer也需要承擔次要責任。
Review機制很容易會被忽視并繞過,但其后果有時候是很慘痛的,所以需要團隊人員一致認可并遵守,才能做得好。
契約協(xié)作
PB文件既契約模式
在開發(fā)人員進行API開發(fā)到完成后,往往都需要提供文檔給別人去調用,這樣的做法有以下缺點:
接口文檔交付緩慢,接口需求方需要等待接口文檔給出才能進行開發(fā),這樣對于需求方來說會更加開發(fā)風險會增加。
接口更新不及時,當開發(fā)人員在更改API時,90%的概率會忘記更新文檔,導致第三方在調用時出現很多狀況需要溝通,這樣增加雙方的溝通成本,降低了雙方的開發(fā)效率。
為了解決以上問題,推行Protobuf文件(以下簡稱PB文件)既契約的模式。
該模式遵循Google API 指南,實現了對應通信協(xié)議支持,并且遵守了 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插件生成
因此,研發(fā)人員通過PB文件即可清晰知道API的定義,參數、返回內容等bin并能依據PB文件生成相應的代碼(Server和Client)就可進行各自的開發(fā),也可以直接生成Swagger文檔來查看使用,極大的降低了溝通的成本。
該模式也需要遵守以下規(guī)則:
新契約變更需要通知到對方
已運行一段時間的舊契約遇到BreakChange需求,應考慮新起契約,不應在原有基礎改動。
新契約定制時需先通過需求方Review后,再繼續(xù)開發(fā),避免返工,也保證需求實現準確性。
契約盡量以清晰簡潔明了的結構體定義為優(yōu)先,對于特殊需求再使用Map、PBValue等。
統(tǒng)一的開發(fā)框架
對于框架的入門使用有另外一篇文章進行指導
開發(fā)框架選型
在過往的編程經驗中,對一個框架的積累沉淀有助于提升整體的開發(fā)效能,畢竟前人栽樹后人乘涼,前人已經摸清楚框架的使用,就有足夠的信心指導后人以此進行開發(fā)、優(yōu)化框架代碼以及建立起整套的周邊生態(tài),可以有余力應付未來的復雜業(yè)務場景。
因當前團隊人力有限僅4人,即使有心也無力去做到自研框架這種需要大量腦力心力且需要積累沉淀的工作,于是放棄自研框架,選擇借力開源產品。
在從框架選型上,從當前團隊需求場景考量,團隊需要的是一套可以約束實現規(guī)范、生態(tài)完善、用戶自主性強、后臺性能要求不高以及支持微服務化的框架。
在Go這邊選型中預選了GVA、Gin和Kratos作為比對并進行實踐,總結GVA、Gin以及Kratos三者比對如下
Gin, 為一個基礎輕量級高性能的Web框架,實現場景案例較多,但代碼實現無約束規(guī)范需要靠開發(fā)者自己摸索,普適性較強。
GVA,是一個前后端配套的框架,主要解決是快速開發(fā)web應用,但框架代碼實現上并沒有很好的理論指導以及規(guī)范約束,整體使用后的代碼質量較差難維護,應對復雜業(yè)務需求能力差,最讓人詬病的是濫用的公共變量。
Kratos,bilibli業(yè)務驗證出品,配套健全的微服務框架,設計上遵循整潔架構以及DDD思想,實現的模塊耦合度低,用戶自主性強,應對復雜的業(yè)務需求能力強,但總體設計理念上傾向于標準規(guī)范定制,整體性能方面中上,需自己擴展模塊實現更高性能。

最終采用Kratos作為統(tǒng)一的開發(fā)框架,具體決策依據如下
多協(xié)議:默認支持HTTP/gRPC,可自主按照規(guī)范定制擴展,支持采用gin來實現transport核心。
API契約理念:支持PB文件定義,可生成服務端、客戶端代碼以及Swagger文檔,并提供在線實時文檔功能。
DDD思想分層開發(fā):指導開發(fā)人員進行開發(fā)時,能按照領域驅動的方式進行開發(fā),讓功能更加聚焦,實現更加精煉,且耦合度低,比如將ClickHouse替換為SLS時只需要改動基礎設施層實現即可。
框架結構模塊化清晰:幾乎涵蓋微服務框架開發(fā)中所涉及的模塊,比如認證、注冊、自監(jiān)控等,并這些模塊均可自定義實現,適配性強。
高擴展性的中間件設計:極強擴展性的中間件設計,靈活擴展各種業(yè)務所需中間件模塊。
框架代碼質量:總體代碼質量上層,模塊依賴解耦分明,用戶閱讀上手容易,自主定制優(yōu)化可行。
性能方面:現在業(yè)務暫時或將來1~2年均以Web后端應用以及微服務為主,所以性能要求方面不苛刻,且如需高性能時也可自主實現擴展模塊實現。
開發(fā)框架沉淀
當前組內已在Kratos框架有一定的積累,為后續(xù)提高后端開發(fā)提高很好的基礎,具體如下:
Layout快速開發(fā):依靠Kratos的Layout能力,定制符合自身的Layout模板,在后續(xù)開發(fā)中可以快速使用,當前Layout實現上已包含各層的寫法模板、Prom指標集成、Opentelemetry全鏈路集成、本地緩存實現等。
自定義API文檔信息的寫法指導:已充分踩坑如何豐富API文檔,對于任何自定義信息需求均能滿足。
快速使用Kratos開發(fā)的指南:配合Layout,助力快速上手框架,快速進行業(yè)務開發(fā)。
通過在線API文檔生成前端SDK:通過Swagger-gen快速生成前端SDK,前端無需手寫SDK,可維護強。
之后也會沉淀更多的場景到框架和文檔中,豐富框架的使用場景,以助力開發(fā)提效。
實踐效果
當前將該模式在內部項目中實踐,有以下效果提升
Bug情況:在經過次實踐后代碼級別的bug數量從項目開始到現在低于10個,需求級別Bug數量低于15個。
前后端協(xié)作效率:前后端當前溝通協(xié)商基本只在需求協(xié)商、協(xié)議定義和實際聯(lián)調階段進行協(xié)商,整體交付功能速度均低于2個星期。
特性完成速度:后端服務交付實現特性,均低于5天一周期,項目進展卡點不在后端實現上。
團隊合作氛圍:review機制的加入,讓團隊成員更加富有凝聚力以及更愿意做知識分享,提升了團隊人員整體能力。
交付服務自監(jiān)控完善:落地全鏈路監(jiān)控,提升前后端全鏈路可觀測性,定位問題低于5分鐘內。
總體的效果是符合預期設計的,但還有許多優(yōu)化的空間比如API管理、Error規(guī)范、CICD標準化,將繼續(xù)沉淀優(yōu)化該模式,提升后續(xù)特性以及未來新項目的交付速率,提升開發(fā)人員幸福感,降低開發(fā)成本。
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

