iT邦幫忙

2025 iThome 鐵人賽

DAY 29
0
Software Development

Go Clean Architecture API 開發全攻略系列 第 29

[Day 29] 程式碼品質守護者:使用 golangci-lint, gofumpt 統一團隊風格

  • 分享至 

  • xImage
  •  

軟體工程不僅僅是讓程式碼能夠運行,更重要的是保證其品質:可讀性、可維護性、以及潛在錯誤的預防。
如果單純依賴人工 Code Review 來檢查程式碼風格、未處理的錯誤等問題,效率低下且容易遺漏。

靜態分析(Static Analysis)工具 或稱 Linter,能在不實際運行程式碼的情況下對其進行掃描,自動找出潛在的問題。在 Go 生態中,golangci-lint 是這個領域的王者。
格式化工具 則確保程式碼風格的一致性,gofumpt 是一個比 gofmt 更嚴格的格式化工具,能幫助團隊維持統一的程式碼風格。

golangci-lint 的優勢

golangci-lint 是一個 Go Linter 的聚合器,它將數十個社群中廣受好評的 Linter(如 go vet, errcheck, staticcheck)整合到一個工具中,並提供了無與倫比的執行速度(透過平行化和快取)。它讓我們可以用一個設定檔,管理所有的 Linter 規則。

第一步: 安裝 golangci-lint

# binary will be $(go env GOPATH)/bin/golangci-lint
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.5.0

golangci-lint --version

第二步:設定 .golangci.yaml

在專案根目錄下建立 .golangci.yaml,這個檔案用來定義你想啟用的 Linter 以及相關規則:

## 設定 golangci-lint 說明文件
## https://golangci-lint.run/usage/configuration/
version: "2"
run:
  # 執行緒數,0 代表使用所有 CPU 核心
  concurrency: 0
  # 超時設定
  timeout: 5m
  # 在測試檔案中也執行 lint
  tests: true

linters:
  default: none
  enable: # 啟用的 linters 列表
    - govet       # Go 官方的程式碼分析工具
    - errcheck    # 檢查未處理的 error
    - staticcheck # 一套強大的靜態分析檢查
    - unused      # 檢查未使用的變數或函式
    - ineffassign # 檢查無效的賦值
    - unconvert   # 檢查不必要的類型轉換
    - nolintlint  # 檢查 nolint 註解的使用
    - bodyclose   # 確保 HTTP 回應的 Body 被正確關閉
    - cyclop      # 檢查函式的圈複雜度

  settings: # 各個 linter 的設定
    cyclop:
      max-complexity: 30  # 函式最大圈複雜度
      package-average: 10 # 套件平均圈複雜度
    errcheck:
      check-type-assertions: true
      check-blank: true

issues:
  max-issues-per-linter: 10 # 每個 linter 最多顯示 10 個問題
  max-same-issues: 5        # 同一個問題最多顯示 5 次

formatters:
  enable:
    - goimports # 使用 goimports 格式化輸出
    - gofmt     # 使用 gofmt 格式化輸出

這份設定檔啟用了多個常用的 Linter,並對一些 Linter 進行了基本設定。你可以根據團隊需求調整這些設定。

詳細的設定選項可以參考官方文件:Configuration

第三步:本地整合

1. 命令列執行

在本地開發環境中,我們可以直接在命令列執行 golangci-lint 來檢查程式碼:

golangci-lint run

2. Makefile

我們可以將它整合到 Makefile 中,方便團隊成員使用:

.PHONY: lint
lint: ## Run linter
	@golangci-lint run ./...

3. VSCode

我們可以將其設定在 VSCode 的工作區設定中,讓每次儲存檔案時自動執行 Lint:

{
    "go.lintTool": "golangci-lint",
    "go.lintOnSave": "file",
    "go.lintFlags": [
        "--config=./.golangci.yml"
    ]
}

這段設定告訴 VS Code 的 Go 擴充套件:

  • 使用 golangci-lint 作為 lint 工具。
  • 在每次儲存檔案時觸發。
  • 使用我們專案根目錄下的 .golangci.yaml 設定檔。

4. Git Hook

我們可以將其設定在 Git pre-commit hook 中,確保每次提交前都會進行檢查:

#!/bin/sh
golangci-lint run ./...
if [ $? -ne 0 ]; then
  echo "Linting failed, please fix the issues before committing."
  exit 1
fi

將這段腳本儲存為 .git/hooks/pre-commit,並賦予執行權限:

chmod +x .git/hooks/pre-commit

這樣,每次嘗試提交程式碼時,Git 都會先執行 golangci-lint,如果有任何問題,提交將被阻止,並提示開發者修正問題。

第四步:CI/CD 整合

在 CI/CD 流程中,我們可以加入 golangci-lint 的檢查步驟,確保所有合併到主分支的程式碼都符合團隊的品質標準。

基礎上就是使用 golangci-lint 官方提供的 Docker Image 來執行 lint 檢查。

各家 CI/CD 平台的設定方式略有不同,就不一一展開說明。

gofumpt 的介紹與整合

gofumpt 是一個比 gofmt 更嚴格的格式化工具,能幫助團隊維持統一的程式碼風格。它在 gofmt 的基礎上增加了一些額外的格式化規則,確保程式碼更加整潔和一致。

第一步: 安裝 gofumpt

go install mvdan.cc/gofumpt@latest

第二步:本地整合

1. 命令列執行

在本地開發環境中,我們可以直接在命令列執行 gofumpt 來格式化程式碼:

gofumpt -w .

2. Makefile

我們可以將它整合到 Makefile 中,方便團隊成員使用:

.PHONY: fmt
fmt: ## Run gofumpt
  @gofumpt -w .

3. VSCode

我們可以將其設定在 VSCode 的工作區設定中,讓每次儲存檔案時自動執行格式化:

{
    "go.useLanguageServer": true,
    "gopls": {
      "formatting.gofumpt": true
    }
}

這段設定告訴 VS Code 的 Go 擴充套件:

  • 使用 gofumpt 作為格式化工具。
  • 在每次儲存檔案時自動格式化。

4. golangci-lint 整合

我們可以將 gofumpt 整合到 golangci-lint 中,確保每次執行 lint 時也會進行格式化檢查。

.golangci.yaml 中加入 gofumpt

formatters:
  enable:
    - gofumpt # 使用 gofumpt 格式化輸出 // 新增這一行

這樣,每次執行 golangci-lint 時,也會檢查程式碼是否符合 gofumpt 的格式要求。
然而 golangci-lint 並不會自動修正格式問題,只會報告違規的地方。開發者仍需手動執行 gofumpt -w . 來修正格式。
但如果有整合在 VSCode 或 Makefile 中,這個步驟會變得非常簡單,或者是根本不需要特別去執行。

總結

透過 golangci-lintgofumpt 的整合,我們可以大幅提升團隊的程式碼品質和一致性。
這不僅減少了人工 Code Review 的負擔,還能及早發現潛在的問題,讓我們的專案更加健壯和易於維護。


上一篇
[Day 28] 當業務邏輯變得複雜時如何調整架構
下一篇
[Day 30] 監控:為你的 Go 服務加上眼睛和耳朵
系列文
Go Clean Architecture API 開發全攻略30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言