Go 精通技能
撰寫適用於任何用途、產品級且符合慣用寫法的 Go 程式碼。此技能編纂了截至 Go 1.22+(2024–2026 年)的 Go 生態系集體智慧。
Go 版本基準:1.22 | 功能最高版本:1.26 | 更新日期:2026 年 2 月
在撰寫任何 Go 程式碼之前,請先內化以下原則。如需深入探討特定主題,請閱讀參考文件。
參考文件 — 按需閱讀
| 文件 | 何時閱讀 |
|---|---|
references/concurrency.md | 任何 goroutine、通道、同步原語或並行工作 |
references/http-and-apis.md | HTTP 伺服器、路由器、中介軟體、REST/gRPC API、WebSocket |
references/database.md | SQL、ORM、連線池、遷移、交易 |
references/testing.md | 單元測試、整合測試、基準測試、模糊測試 |
references/errors-and-resilience.md | 錯誤處理、重試、斷路器、優雅降級 |
references/performance.md | 分析、減少配置、GC 調校、基準測試 |
references/cli-and-config.md | CLI 框架、配置、環境管理 |
references/project-patterns.md | 專案佈局、依賴注入、外掛系統、程式碼生成 |
references/platform-and-build.md | 跨平台編譯、Windows 特定事項、嵌入、分發 |
references/security.md | 認證、機密、輸入驗證、TLS、OWASP 模式 |
references/modern-go.md | Go 1.21–1.26 功能、已棄用模式、預訓練模型常犯的錯誤 |
references/advanced-patterns.md | 泛型、狀態機、串流、排程、AI/LLM 整合 |
references/advanced-resources.md | 策劃的外部資源:編譯器/執行時期內部、組合語言、unsafe、cgo、Raft、K8s 運算子、Wasm、密碼學、分析、書籍、會議、學術論文 |
references/observability.md | OpenTelemetry 設定、追蹤、指標、帶有追蹤關聯的結構化日誌、健康檢查、飛行記錄器、生產環境儀器檢查清單 |
references/ecosystem-and-tooling.md | 策劃清單(awesome-go)、框架比較(Web、ORM、CLI、DI、配置)、風格指南、CI/CD、Docker、版本管理、漏洞掃描、學習平台、Go 治理 |
references/internals.md | GMP 排程器、垃圾回收器、記憶體模型、堆疊管理、記憶體分配器、編譯器管線、unsafe、反射、編譯器指令 |
references/networking.md | TCP/UDP 伺服器、net.Conn 逾時、連線池、mTLS、Unix sockets、DNS 解析器、自訂二進位協定、gnet |
references/mcp-and-agents.md | MCP 伺服器(mcp-go、官方 SDK)、工具/資源/提示、代理框架(ADK、langchaingo、eino)、多代理模式、gopls MCP |
references/api-design.md | REST 命名、分頁(游標/偏移)、RFC 9457 錯誤、速率限制、API 版本控制、OpenAPI 程式碼生成(oapi-codegen)、向後相容性、棄用標頭 |
references/cgo-and-interop.md | CGo 基礎、C 類型映射、跨邊界記憶體管理、指標傳遞規則、回呼、靜態/動態連結、跨平台編譯、純 Go 替代方案 |
references/design-patterns.md | Go 中的 GoF 模式(工廠、建構者、策略、觀察者、裝飾者)、組合優於繼承、決策樹 |
references/distributed-systems.md | Raft、outbox 模式、sagas、分散式鎖、冪等性、CRDT、一致雜湊 |
references/cloud-native.md | Kubernetes client-go、controller-runtime 運算子、准入 webhook、領導者選舉、Helm/Kustomize |
references/debugging-and-diagnostics.md | Delve 除錯器、堆疊追蹤、GODEBUG 旗標、分析工作流程、症狀到診斷表 |
references/style-synthesis.md | 合併 Google + Uber + 社群風格規則、命名、格式化、程式碼組織 |
references/modules-and-dependencies.md | go.mod 操作、MVS、工作區、GOPROXY、GOAUTH、vendoring、多模組單一儲存庫 |
references/encoding-and-serialization.md | JSON(v1/v2)、Protocol Buffers、MessagePack、CBOR、CSV、YAML、TOML、格式決策表 |
references/wasm-and-embedded.md | WebAssembly(WASI、瀏覽器)、go:wasmexport、TinyGo、嵌入式/IoT 模式 |
references/migration-guides.md | 從 Python、Java、TypeScript、Rust、C++ 翻譯成慣用 Go — 概念對應與陷阱 |
references/testing-advanced.md | 基於屬性的測試(rapid)、契約測試(Pact)、負載測試(Vegeta)、混沌工程、synctest 進階 |
references/supply-chain-security.md | govulncheck、SBOM 生成、SLSA 框架、cosign、可重複建構、GODEBUG 相容性 |
references/ebpf.md | 從 Go 使用 eBPF(cilium/ebpf)、bpf2go 工作流程、追蹤、網路、分析核心層級 |
references/event-driven.md | Kafka、RabbitMQ、NATS 消費者/生產者、Watermill 框架、DLQ、恰好一次模式 |
references/data-structures-and-caching.md | container/heap、container/list、LRU 快取、singleflight、分片映射、布隆過濾器、弱引用快取、快取策略模式(aside/write-through/write-behind) |
references/ai-ml-beyond-llm.md | ONNX Runtime 推論、Gonum、向量資料庫客戶端、嵌入管線、從 Go 提供 ML 服務 |
references/file-io.md | 檔案操作、io.Reader/Writer 組合、緩衝 I/O、串流、路徑處理、os.Root、fs.FS 抽象、fsnotify |
核心哲學
Go 刻意保持簡單。請抵制從其他生態系引入複雜性的衝動。
- 清晰勝於巧妙。 程式碼被閱讀的次數是撰寫次數的十倍。明確勝過隱晦 — 未來的維護者(包括未來的你)會感謝你。
- 組合勝於繼承。 嵌入介面與結構;避免深層次結構。Go 沒有類別,而這是刻意的 — 小型、可組合的片段勝過複雜的類型樹。
- 接受介面,回傳結構。 函式應依賴行為(介面)並暴露具體類型。這讓呼叫端保持彈性,同時讓實作可被發現與除錯。
- 讓零值有用。 設計類型以使
var x T無需初始化即可使用。這消除了整整一類的空指標錯誤,並讓 API 感覺自然(bytes.Buffer、sync.Mutex、http.Client在零值時都能工作)。 - 錯誤是值。 處理它們;絕不忽略。使用
fmt.Errorf("doing X: %w", err)包裹上下文。被丟棄的錯誤會變成在數小時後於生產環境中浮現的靜默故障。 - 不要啟動你無法停止的 goroutine。 每個 goroutine 都需要一個經由上下文取消或通道關閉的關閉路徑。洩漏的 goroutine 會無限期消耗記憶體,並使優雅關閉成為不可能。
- 標準函式庫優先。 Go 的標準函式庫異常完整。僅在標準函式庫真正不足時才使用第三方函式庫 — 更少的依賴意味著更少的安全審計、更少的版本衝突和更快的建置。
- 在
gofmt格式化、通過編譯與檢查之前,程式碼不算完成。 執行gofmt/goimports、go build、go vet、go test -race和golangci-lint— 將它們設為 CI 閘門;競態檢測器能發現審查中不可見的錯誤。在沒有工具的情況下產生程式碼時,手動維持相同標準:保持 gofmt 格式,並且只包含你實際使用的匯入(未使用的匯入是編譯錯誤 — 這是在替換一種習慣用法後常犯的經典錯誤)。 - 尊重呼叫方的契約,然後再最佳化。 當任務指定了確切的識別碼、簽名、輸出格式或類型時,請完全照做 — 即使你會有不同的命名或結構。預設使用最簡單且滿足規格的正確解決方案;僅在任務的複雜度要求時,才加入抽象、間接或防禦層。匹配所需的表面本身就是正確性的一部分 — 無謂的重新命名或重構會破壞呼叫方和測試。
專案結構
從簡單開始。僅在專案需求時才加入結構。
單一二進位檔案(CLI 或小型服務)
myapp/
├── go.mod
├── main.go # package main,進入點
├── app.go # 核心應用邏輯
├── app_test.go
└── internal/ # 按需使用的私有套件
└── parser/
├── parser.go
└── parser_test.go
多二進位檔案 / 生產服務
myproject/
├── go.mod
├── cmd/ # 進入點 — 精簡的 main() 函式
│ ├── api-server/
│ │ └── main.go
│ └── worker/
│ └── main.go
├── internal/ # 私有應用程式碼(由編譯器強制執行)
│ ├── auth/
│ ├── user/ # 領域套件:handler + service + repo
│ │ ├── handler.go
│ │ ├── service.go
│ │ ├── repository.go
│ │ └── model.go
│ ├── platform/ # 橫切關注點:資料庫、快取、日誌
│ │ ├── postgres/
│ │ └── redis/
│ └── middleware/
├── pkg/ # 可選:公開可重複使用的函式庫
├── api/ # OpenAPI 規格、protobuf 定義
├── migrations/ # SQL 遷移檔案
├── scripts/ # 建置、部署、開發工具
├── configs/ # 預設設定檔
└── Makefile
關鍵規則:
internal/由 Go 編譯器強制執行 — 此處的程式碼無法被外部模組匯入。cmd/*/main.go應保持精簡:解析旗標、注入依賴、呼叫run()、處理關閉。- 按領域/功能分組,而非按技術層。避免
models/、controllers/、services/— 這是 Java 式的分層,與 Go 的套件模型相悖。 - 避免
utils/、helpers/、common/套件 — 按套件提供的功能命名,因為模糊的名稱會吸引不相關的程式碼並使匯入失去意義。 - 一個目錄就是一個套件。不要僅為了組織而建立目錄。
命名慣例
// 套件:簡短、小寫、單字。無底線或駝峰式大小寫。
package user // 好
package userService // 差 — 駝峰式大小寫
// 匯出的:MixedCaps。未匯出的:mixedCaps。底線在語法上合法,但社群將其視為風格錯誤 — 它們標示著非慣用寫法。
func ParseRequest() // 匯出的
func parseHeader() // 未匯出的
// 介面:方法 + "-er" 後綴,適用於單一方法介面
type Reader interface { Read(p []byte) (n int, err error) }
type Validator interface { Validate() error }
// Getter:Owner(),而非 GetOwner()。Setter:SetOwner()。
func (u *User) Name() string // getter — "Get" 前綴不合 Go 風格
func (u *User) SetName(n string) // setter
// 縮寫:全部大寫。URL、HTTP、ID、API — 而非 Url、Http、Id、Api。
type HTTPClient struct{}
var userID int
// 錯誤變數:ErrXxx。錯誤類型:XxxError。
var ErrNotFound = errors.New("not found")
type ValidationError struct { Field string; Message string }
// 上下文:第一個參數,始終命名為 ctx。這是 Go 的通用慣例,使上下文傳播在任何程式碼庫中都能立即被識別。
func (s *Service) GetUser(ctx context.Context, id string) (*User, error)
錯誤處理
這是 Go 最獨特的模式。請熟練掌握。
// 檢查每個錯誤 — 使用 %w 包裹上下文
f, err := os.Open(name)
if err != nil {
return fmt.Errorf("opening config %s: %w", name, err)
}
defer f.Close()
// errors.AsType — 泛型類型安全錯誤提取 [Go 1.26+]
// 優於 errors.As:類型安全,無需預先宣告變數。
if pathErr, ok := errors.AsType[*fs.PathError](err); ok {
fmt.Println("failed at path:", pathErr.Path)
}
關鍵規則:
return err而不帶上下文會使除錯變得痛苦 — 始終使用fmt.Errorf("doing X: %w", err)包裹。- 在函式庫程式碼中使用
log.Fatal(err)會呼叫os.Exit(1),跳過延遲清理 — 應將錯誤回傳給呼叫方。 panic()是給程式設計師的錯誤,而非執行時期狀況 — 應回傳錯誤。- 使用
%v(而非%w)包裹第三方錯誤,以避免將呼叫方與底層錯誤類型耦合。 - 錯誤訊息風格: 小寫,無標點符號:
fmt.Errorf("reading config file: %w", err)。
請參閱 references/errors-and-resilience.md 了解哨兵錯誤、自訂錯誤類型、errors.Join、重試模式、斷路器和優雅降級。
並發基礎
請參閱 references/concurrency.md 了解全面的模式,包括 errgroup、工作者池和扇出/扇入。核心規則:
- 使用 context 進行取消。 每個 goroutine 都必須檢查
ctx.Done()— 沒有它,就無法發出「停止工作」的信號。 - 優先使用
errgroup而非原始的sync.WaitGroup。 它捆綁了等待 + 錯誤收集 + 上下文取消。請參閱concurrency.md中帶有SetLimit的完整範例。 - 保護共享狀態 — 使用通道進行通訊,當互斥鎖更簡單時使用它。註解每個互斥鎖保護的內容。
- 每個 goroutine 都需要一個關閉路徑。 沒有取消檢查 = 洩漏的 goroutine = 記憶體洩漏。
- goroutine 中的 panic 會終止整個程序。 在衍生的 goroutine 中 recover,或使用
sourcegraph/conc。
新同步原語:
sync.OnceValue / sync.OnceValues [Go 1.21+] — 類型安全的惰性初始化,取代 sync.Once + 套件變數:
var loadConfig = sync.OnceValues(func() ([]byte, error) {
return os.ReadFile("config.json") // 僅執行一次,從任何 goroutine 呼叫都安全
})
data, err := loadConfig() // 後續呼叫回傳快取結果
sync.WaitGroup.Go [Go 1.25+] — 結合 Add(1) + go func + Done():
var wg sync.WaitGroup
wg.Go(func() { process(1) })
wg.Go(func() { process(2) })
wg.Wait()
通道大小設定: 無緩衝(0)用於同步,帶緩衝(1)用於信號,帶緩衝(N)用於已知有界的工作。
介面
// 接受介面,回傳具體類型
type UserStore interface {
GetUser(ctx context.Context, id string) (*User, error)
}
func NewService(store UserStore) *Service { return &Service{store: store} }
// 保持介面小巧 — 1-3 個方法。從小型介面組合出較大的介面。
type ReadWriter interface { Reader; Writer }
// 定義介面
…
[為翻譯而截斷]