統一規范篇
合理規劃目錄
本篇主要描述了公司內部同事都必須遵守的一些開發規矩,如統一開發空間,既使用統一的開發工具來保證代碼最后的格式的統一,開發中對文件和代碼長度的控制,必須經過Go語言自帶的檢測機制等。
【原則1.1】合理規劃目錄,一個目錄中只包含一個包(實現一個模塊的功能),如果模塊功能復雜考慮拆分子模塊,或者拆分目錄。
GOPATH設置
【建議1.2】使用單一的 GOPATH
import 規范
【規則1.3.1】在非測試文件(*_test.go)中,禁止使用 . 來簡化導入包的對象調用。
【規則1.3.2】禁止使用相對路徑導入(./subpackage),所有導入路徑必須符合 go get 標準。
【建議1.3.3】建議使用goimports工具或者IDE工具來管理多行import
代碼風格
【規則1.4.1】提交代碼時,必須使用gofmt對代碼進行格式化。
【規則1.4.2】提交代碼時,必須使用golint對代碼進行檢查。
【建議1.4.3】提交代碼前,必須使用go vet對代碼進行檢查。
大小約定
【建議1.5.1】單個文件長度不超過500行。
【建議1.5.2】單個函數長度不超過50行。
【規則1.5.3】單個函數圈復雜度最好不要超過10,禁止超過15。
【規則1.5.4】單行語句不能過長,如不能拆分需要分行寫。一行最多120個字符。
【建議1.5.5】函數中縮進嵌套必須小于等于3層。
【原則1.5.6】保持函數內部實現的組織粒度是相近的。
命名篇
本篇以開發時從上往下的順序既:開發前約定的基本命名規范、包、常量、變量、結構體、參數、返回值的順序講解了開發中各個環節的命名規范。
基本命令規范
【規則2.1.1】需要注釋來補充的命名就不算是好命名。
【規則2.1.2】使用可搜索的名稱
【規則2.1.3】做有意義的區分
項目目錄名
【規則2.2.1】目錄名必須為全小寫單詞,允許加中劃線‘-’組合方式,但是頭尾不能為中劃線。
【建議2.2.2】雖然允許出現中劃線,但是盡量避免或少加中劃線。
包名
【原則2.3.1】取名盡量采取有意義的包名,簡單和可閱讀。
【規則2.3.2】包名必須全部為小寫單詞,無下劃線,越短越好。盡量不要與標準庫重名。
【規則2.3.3】禁止通過中劃線連接多個單詞的方式來命名包名。
【建議2.3.4】包名盡量與所在目錄名一致,引用時比較方便。
文件名
【規則2.4.1】文件名必須為小寫單詞,允許加下劃線‘_’組合方式,但是頭尾不能為下劃線
【建議2.4.2】雖然允許出現下劃線,但是盡量避免。
【建議2.4.3】文件名以功能為指引,名字中不需再出現模塊名或者組件名。
常量
【規則2.5.1】常量&枚舉名采用大小寫混排的駝峰模式(Golang官方要求),不允許出現下劃線
【建議2.5.2】按照功能來區分,而不是將所有類型都分在一組,并建議將公共常量置于私有常量之前
【規則2.2.3】如果是枚舉類型的常量,需要先創建相應類型
【建議2.5.4】如果模塊的功能較為復雜、常量名稱容易混淆的情況下,為了更好地區分枚舉類型,可以使用完整的前綴
變量
變量申明
【規則2.6.1】變量命名基本上遵循相應的英文表達或簡寫,在相對簡單的環境(對象數量少、針對性強)中,可以將一些名稱由完整單詞簡寫為單個字母
變量命名慣例
【規則2.6.2】變量名稱一般遵循駝峰法,并且不允許出現下劃線,當遇到特有名詞時,需要遵循以下規則:
- 如果變量為私有,且特有名詞為首個單詞,則使用小寫,如:apiClient
- 其它情況都應當使用該名詞原有的寫法,如 APIClient、repoID、UserID
【規則2.6.3】不要使用_來命名變量名,多個變量申明放在一起
【規則2.6.4】在函數外部申明必須使用var,不要采用:=,容易踩到變量的作用域的問題。
全局變量名
【規則2.6.5】全局變量必須為大小寫混排的駝峰模式,不允許出現下劃線。首字母根據作為范圍確定大小寫。
【建議2.6.6】盡量避免跨package使用全局變量,盡量減少全局變量的使用。
局部變量名
【規則2.6.7】局部變量名必須為大小寫混排,且首字母小寫,不能有下劃線。
循環變量
【建議2.6.8】for循環變量可以使用單字母。
結構體(struct)
【規則2.7.1】struct申明和初始化格式采用多行
【規則2.7.2】結構體名必須為大小寫混排的駝峰模式,不允許出現下劃線,可被包外部引用則首字母大寫;如僅包內使用,則首字母小寫。
【建議2.7.3】結構名建議采用名詞、動名詞為好。
接口名
【規則2.8.1】接口名必須為大小寫混排,支持包外引用則首字母大寫,僅包內使用則首字母小寫。不能有下劃線,整體必須為名詞。
【建議2.8.2】單個函數的接口名以”er”作為后綴。
函數和方法名
【規則2.9.1】函數名必須為大小寫混排的駝峰模式
【建議2.9.2】函數名力求精簡準確,并采用用動詞或動詞短
【規則2.9.3】方法接收名必須為大小寫混排,首字母小寫。方法接收者命名要能夠體現接收者對象。
【建議2.9.4】接收者名通常1個或者2個字母就夠,最長不能超過4個字母。
【建議2.9.5】接收者名不要使用me,this 或者 self 這種泛指的名字。
【建議2.9.6】定義方法時,如果方法內不會直接引用接收者,則省略掉接收者名。
參數名
【規則2.10】參數名必須為大小寫混排,且首字母小寫,不能有下劃線。
返回值
【規則2.11.1】返回值如果是命名的,則必須大小寫混排,首字母小寫。
【建議2.11.2】 函數的返回值應避免使用命名的參數。
開發篇
本篇主要是講解開發中各個環節的開發規范和對一些代碼的優化寫法。在本文中有一些特別標黃的建議,我真的建議你好好看看那些代碼,因為那可能對你提高代碼開發會很有幫助。
包
【建議3.1.1】項目倉庫中包含全量的代碼
【建議3.1.2】建議采用 Glide 來管理第三方包
魔鬼數字
【規則3.2】代碼中禁止使用魔鬼數字。
常量 & 枚舉
【建議3.3.1】 為整數常量添加 String() 方法
【建議3.3.2】讓 iota 從 a +1 開始增量
結構體
【規則3.4.1】對于要使用json轉換的結構體代碼,變量名必須為大寫,否則你只會得到一個為空的對象
【建議3.4.2】 在初始化結構體時使用帶有標簽的語法
【建議3.4.3】將結構體的初始化拆分到多行
運算符
【規則3.5】運算符前后、逗號后面、if后面等需有單空格隔開。
函數
【原則3.6.1】保持函數內部實現的組織粒度是相近的。
【建議3.6.2】 返回函數調用
【建議3.6.3】 withContext 封裝函數
參數
【建議3.7.1】參數按邏輯緊密程度安排位置, 同種類型的參數放在相鄰位置。
【建議3.7.2】避免使用標識參數來控制函數的執行邏輯。
【建議3.7.3】參數個數不要超過5個
返回值
【規則3.8.1】函數返回值個數不要超過3個。
【建議3.8.2】如果函數的返回值超過3個,建議將其中關系密切的返回值參數封裝成一個結構體。
注釋
【原則3.9.1】編寫代碼首先考慮如何代碼自我解釋,然后才是添加注釋進行補充說明
【原則3.9.2】注釋的內容要清楚、明了,含義準確,防止注釋二義性。
【原則3.9.3】在代碼的功能、意圖層次上進行注釋,即注釋用于解釋代碼難以直接表達的意圖,而不是重復描述代碼。
【規則3.9.4】所有導出對象都需要注釋說明其用途;非導出對象根據情況進行注釋。必須時,應該說明值的取值范圍,及默認值。
【規則3.9.5】注釋的單行長度不能超過 80 個字符。
【規則3.9.6】注釋需要緊貼對應的包聲明和函數之前,不能有空行、
【規則3.9.7】非跨度很長的注釋,盡量使用 // 方式。
【規則3.9.8】避免多余的空格,兩句注釋之間保持一個空格。
【原則3.9.9】注釋第一條語句應該為一條概括語句,并且使用被聲明的名字作為開頭。
【建議3.9.10】//與注釋的文檔之間空一格。
【規則3.9.11】每個程序包都應該有一個包注釋,一個位于package子句之前的塊注釋。
【規則3.9.12】不要依靠用空格進行對齊。
【建議3.27】類型定義一般都以單數信息描述。
【建議3.9.13】函數聲明處注釋描述函數功能、性能及用法,包括輸入和輸出參數、函數返回值、可重入的要求等;定義處詳細描述函數功能和實現要點,如實現的簡要步驟、實現的理由、設計約束等
【建議3.9.14】如果函數或者方法為判斷類型(返回值主要為bool類型),則以 returns true if 開頭。
錯誤
【原則3.10.1】錯誤處理的原則就是不能丟棄任何有返回err的調用,不要采用_丟棄,必須全部處理。接收到錯誤,要么返回err,要么實在不行就panic,或者使用log記錄下來
【規則3.10.2】error的信息不要采用大寫字母,盡量保持你的錯誤簡短,但是要足夠表達你的錯誤的意思。
【規則3.10.3】導出的錯誤變量的命名,以Err開始,如ErrSomething,無需導出的錯誤變量命名,以Error作為后綴,如specificError
【規則3.10.4】公共包內禁止使用panic,如果有panic需要內部recover并返回error。
其他
【建議3.11.1】在代碼中編寫字符串形式的json時,使用反單引號,而不是雙引號。
【規則3.11.2】相對獨立的程序塊之間、變量說明之后必須加空行,而邏輯緊密相關的代碼則放在一起。
【規則3.11.3】盡早return:一旦有錯誤發生,馬上返回。
【建議3.11.4】禁止出現2處及以上的重復代碼。
【建議3.11.5】if條件判斷, 同時使用超過3個表達式以上的時候, 使用switch替代。
【建議3.11.6】定義bool變量時,要避免判斷時出現雙重否定,應使用肯定形式的表達式。
【建議3.11.7】for循環初始值從0開始,判斷條件使用<無等號的方式。
【建議3.11.8】長句子打印或者調用,使用參數進行格式化分行
【建議3.11.9】 將 for-select 封裝到函數中
【建議3.11.10】把 slice、map 等定義為自定義類型
【建議3.11.11】 為訪問 map 增加 setter,getters
參數傳遞
【建議3.11.12】 對于少量數據,不要傳遞指針
【建議3.11.13】 對于大量數據的 struct 可以考慮使用指針
【建議3.11.14】 傳入的參數是 map,slice,chan 不要傳遞指針,因為 map,slice,chan 是引用類型,不需要傳遞指針的指針
注意閉包的調用
【原則3.11.15】在循環中調用函數或者goroutine方法,一定要采用顯示的變量調用,不要再閉包函數里面調用循環的參數
優化篇
本篇的意義是為開發提供一些經過驗證的開發規則和建議,讓開發在開發過程中避免低級錯誤,從而提高代碼的質量保證和性能效率
質量保證
代碼質量保證優先原則
【原則4.1.1】代碼質量保證優先原則:
(1)正確性,指程序要實現設計要求的功能。
(2)簡潔性,指程序易于理解并且易于實現。
(3)可維護性,指程序被修改的能力,包括糾錯、改進、新需求或功能規格變化的適應能力。
(4)可靠性,指程序在給定時間間隔和環境條件下,按設計要求成功運行程序的概率。
(5)代碼可測試性,指軟件發現故障并隔離、定位故障的能力,以及在一定的時間和成本前提下,進行測試設計、測試執行的能力。
(6)代碼性能高效,指是盡可能少地占用系統資源,包括內存和執行時間。
(7)可移植性,指為了在原來設計的特定環境之外運行,對系統進行修改的能力。
對外接口原則
【原則4.1.2】對于主要功能模塊抽象模塊接口,通過interface提供對外功能。
值與指針(T/*T)的使用原則
【建議4.1.3.1】基本類型傳遞時,盡量使用值傳遞。
【建議4.1.3.2】如果傳遞字符串或者接口對象時,建議直接實例傳遞而不是指針傳遞。
【建議4.1.3.3】如果是map、func、chan,那么直接用T。
【建議4.1.3.4】如果是slice,method里面不重新reslice之類的就用T。
【建議4.1.3.5】如果想通過method改變里面的屬性,那么請使用*T。
【建議4.1.3.6】如果是struct,并且里面包含了sync.Mutex之類的同步原語,那么請使用*T,避免copy。
【建議4.1.3.7】如果是一個大型的struct或者array,那么使用*T會比較輕量,效率更高。
【建議4.1.3.8】如果是struct、slice、array里面的元素是一個指針類型,然后調用函數又會改變這個數據,那么對于讀者來說采用*T比較容易懂。
【建議4.1.3.9】其它情況下,建議采用*T。
init的使用原則
【規則4.1.4.1】一個文件只定義一個init函數。
【規則4.1.4.2】一個包內的如果存在多個init函數,不能有任何的依賴關系。
defer的使用原則
【建議4.1.5.1】如果函數存在多個返回的地方,則采用defer來完成如關閉資源、解鎖等清理操作。
【建議4.1.5.2】defer會消耗更多的系統資源,不建議用于頻繁調用的方法中。
【建議4.1.5.3】避免在for循環中使用defer。
Goroutine使用原則
【規則4.1.6.1】確保每個goroutine都能退出。
【規則4.1.6.2】禁止在閉包中直接引用閉包外部的循環變量。
Channel使用原則
【規則4.1.7.1】傳遞channel類型的參數時應該區分其職責。
【規則4.1.7.2】確保對channel是否關閉做檢查。
【規則4.1.7.3】禁止重復釋放channel。
其它
【建議4.1.8.1】使用go vet --shadow檢查變量覆蓋,以避免無意的變量覆蓋。
【建議4.1.8.2】GO的結構體中控制使用Slice和Map。
【規則4.1.8.3】避免在循環引用調用 runtime.SetFinalizer。
【規則4.1.8.4】避免在for循環中使用time.Tick()函數。
性能效率
Memory優化
【建議4.2.1.1】將多次分配小對象組合為一次分配大對象。
【建議4.2.1.2】將多個不同的小對象綁成一個大結構,可以減少內存分配的次數。
【建議4.2.1.3】組合內存分配的一個特殊情形是對分片數組進行預分配。
【建議4.2.1.4】盡可能使用小數據類型,并盡可能滿足硬件流水線(Pipeline)的操作,如對齊數據預取邊界。
【建議4.2.1.5】使用對象池來重用臨時對象,減少內存分配。
GC 優化
【建議4.2.2.1】設置GOMAXPROCS為CPU的核心數目,或者稍高的數值。
【建議4.2.2.2】避免頻繁創建對象導致GC處理性能問題。
其它優化建議
【建議4.2.3.1】減少[]byte和string之間的轉換,盡量使用[]byte來處理字符。
【建議4.2.3.2】make申請slice/map時,根據預估大小來申請合適內存。
【建議4.2.3.3】字符串拼接優先考慮bytes.Buffer。
【建議4.2.3.4】避免使用CGO或者減少跨CGO調用次數。
【建議4.2.3.5】避免高并發調用同步系統接口。
【建議4.2.3.6】高并發時避免共享對象互斥。
【建議4.2.3.7】長調用鏈或在函數中避免申明較多較大臨時變量。
【建議4.2.3.8】為高并發的輕量級任務處理創建routine池。
【建議4.2.3.9】建議版本提供性能/內存監控的功能,并動態開啟關閉,但不要長期開啟pprof提供的CPU與MEM profile功能。