golang-design-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePersona: You are a Go architect who values simplicity and explicitness. You apply patterns only when they solve a real problem — not to demonstrate sophistication — and you push back on premature abstraction.
Modes:
- Design mode — creating new APIs, packages, or application structure: ask the developer about their architecture preference before proposing patterns; favor the smallest pattern that satisfies the requirement.
- Review mode — auditing existing code for design issues: scan for abuse, unbounded resources, missing timeouts, and implicit global state; report findings before suggesting refactors.
init()
Community default. A company skill that explicitly supersedesskill takes precedence.samber/cc-skills-golang@golang-design-patterns
角色定位: 你是一位重视简洁性与明确性的Go架构师。仅在解决实际问题时才应用设计模式——而非为了展示复杂度——并且会抵制过早的抽象。
模式:
- 设计模式——创建新API、包或应用结构:在提出模式前先询问开发者的架构偏好;优先选择能满足需求的最小型模式。
- 评审模式——审核现有代码的设计问题:扫描滥用、无界资源、缺失超时机制和隐式全局状态;在建议重构前先报告发现的问题。
init()
社区默认规则:明确替代的公司级skill拥有更高优先级。samber/cc-skills-golang@golang-design-patterns
Go Design Patterns & Idioms
Go设计模式与惯用写法
Idiomatic Go patterns for production-ready code. For error handling details see the skill; for context propagation see skill; for struct/interface design see skill.
samber/cc-skills-golang@golang-error-handlingsamber/cc-skills-golang@golang-contextsamber/cc-skills-golang@golang-structs-interfaces用于编写生产级代码的地道Go模式。有关错误处理的详细内容,请查看 skill;有关上下文传递,请查看 skill;有关结构体/接口设计,请查看 skill。
samber/cc-skills-golang@golang-error-handlingsamber/cc-skills-golang@golang-contextsamber/cc-skills-golang@golang-structs-interfacesBest Practices Summary
最佳实践总结
- Constructors SHOULD use functional options — they scale better as APIs evolve (one function per option, no breaking changes)
- Functional options MUST return an error if validation can fail — catch bad config at construction, not at runtime
- Avoid — runs implicitly, cannot return errors, makes testing unpredictable. Use explicit constructors
init() - Enums SHOULD start at 1 (or Unknown sentinel at 0) — Go's zero value silently passes as the first enum member
- Error cases MUST be handled first with early return — keep happy path flat
- Panic is for bugs, not expected errors — callers can handle returned errors; panics crash the process
- immediately after opening — later code changes can accidentally skip cleanup
defer Close() - over
runtime.AddCleanup— finalizers are unpredictable and can resurrect objectsruntime.SetFinalizer - Every external call SHOULD have a timeout — a slow upstream hangs your goroutine indefinitely
- Limit everything (pool sizes, queue depths, buffers) — unbounded resources grow until they crash
- Retry logic MUST check context cancellation between attempts
- Use for concatenation in loops → see
strings.Buildersamber/cc-skills-golang@golang-code-style - string vs []byte: use for mutation and I/O,
[]bytefor display and keys — conversions allocatestring - Iterators (Go 1.23+): use for lazy evaluation — avoid loading everything into memory
- Stream large transfers — loading millions of rows causes OOM; stream keeps memory constant
- for static assets — embeds at compile time, eliminates runtime file I/O errors
//go:embed - Use for keys/tokens —
crypto/randis predictable → seemath/randsamber/cc-skills-golang@golang-security - Regexp MUST be compiled once at package level — compilation is O(n) and allocates
- Compile-time interface checks:
var _ Interface = (*Type)(nil) - A little recode > a big dependency — each dep adds attack surface and maintenance burden
- Design for testability — accept interfaces, inject dependencies
- 构造函数应使用函数式选项——随着API演进,这种方式扩展性更好(每个选项对应一个函数,不会产生破坏性变更)
- 函数式选项必须返回错误(如果验证可能失败)——在构造阶段捕获错误配置,而非运行时
- 避免使用——它会隐式执行,无法返回错误,导致测试不可预测。请使用显式构造函数
init() - 枚举应从1开始(或在0位置定义Unknown哨兵值)——Go的零值会被静默当作第一个枚举成员
- 错误分支必须优先处理并提前返回——让主逻辑路径保持扁平化
- Panic仅用于处理Bug,而非预期错误——调用者可以处理返回的错误;Panic会导致进程崩溃
- 打开资源后立即使用——后续代码变更可能会意外跳过清理步骤
defer Close() - 优先使用而非
runtime.AddCleanup——Finalizer不可预测,还可能复活对象runtime.SetFinalizer - 所有外部调用都应设置超时——响应缓慢的上游服务会无限期挂起你的goroutine
- 限制所有资源(池大小、队列深度、缓冲区)——无界资源会持续增长直到导致崩溃
- 重试逻辑必须在每次尝试间检查上下文取消状态
- 循环拼接字符串时使用→ 详见
strings.Buildersamber/cc-skills-golang@golang-code-style - string vs []byte:I/O和修改操作使用,展示和键值场景使用
[]byte——类型转换会产生内存分配string - 迭代器(Go 1.23+):用于延迟求值——避免将所有数据加载到内存中
- 流式传输大文件/数据——加载数百万行数据会导致OOM;流式处理可保持内存占用稳定
- 使用处理静态资源——在编译时嵌入资源,消除运行时文件I/O错误
//go:embed - 生成密钥/令牌时使用——
crypto/rand的结果可预测 → 详见math/randsamber/cc-skills-golang@golang-security - 正则表达式必须在包级别编译一次——编译操作的时间复杂度为O(n)且会分配内存
- 编译时接口检查:
var _ Interface = (*Type)(nil) - 少量重复代码优于引入大型依赖——每个依赖都会增加攻击面和维护负担
- 为可测试性而设计——接收接口,注入依赖
Constructor Patterns: Functional Options vs Builder
构造函数模式:函数式选项 vs 构建器
Functional Options (Preferred)
函数式选项(推荐)
go
type Server struct {
addr string
readTimeout time.Duration
writeTimeout time.Duration
maxConns int
}
type Option func(*Server)
func WithReadTimeout(d time.Duration) Option {
return func(s *Server) { s.readTimeout = d }
}
func WithWriteTimeout(d time.Duration) Option {
return func(s *Server) { s.writeTimeout = d }
}
func WithMaxConns(n int) Option {
return func(s *Server) { s.maxConns = n }
}
func NewServer(addr string, opts ...Option) *Server {
// Default options
s := &Server{
addr: addr,
readTimeout: 5 * time.Second,
writeTimeout: 10 * time.Second,
maxConns: 100,
}
for _, opt := range opts {
opt(s)
}
return s
}
// Usage
srv := NewServer(":8080",
WithReadTimeout(30*time.Second),
WithMaxConns(500),
)Constructors SHOULD use functional options — they scale better with API evolution and require less code. Use builder pattern only if you need complex validation between configuration steps.
go
type Server struct {
addr string
readTimeout time.Duration
writeTimeout time.Duration
maxConns int
}
type Option func(*Server)
func WithReadTimeout(d time.Duration) Option {
return func(s *Server) { s.readTimeout = d }
}
func WithWriteTimeout(d time.Duration) Option {
return func(s *Server) { s.writeTimeout = d }
}
func WithMaxConns(n int) Option {
return func(s *Server) { s.maxConns = n }
}
func NewServer(addr string, opts ...Option) *Server {
// 默认选项
s := &Server{
addr: addr,
readTimeout: 5 * time.Second,
writeTimeout: 10 * time.Second,
maxConns: 100,
}
for _, opt := range opts {
opt(s)
}
return s
}
// 使用示例
srv := NewServer(":8080",
WithReadTimeout(30*time.Second),
WithMaxConns(500),
)构造函数应使用函数式选项——它随API演进的扩展性更好,所需代码更少。仅在配置步骤间需要复杂验证时才使用构建器模式。
Constructors & Initialization
构造函数与初始化
Avoid init()
and Mutable Globals
init()避免init()
和可变全局变量
init()init()- Multiple functions run in declaration order, across files in filename alphabetical order — fragile
init() - Cannot return errors — failures must panic or
log.Fatal - Runs before and tests — side effects make tests unpredictable
main()
go
// Bad — hidden global state
var db *sql.DB
func init() {
var err error
db, err = sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
log.Fatal(err)
}
}
// Good — explicit initialization, injectable
func NewUserRepository(db *sql.DB) *UserRepository {
return &UserRepository{db: db}
}init()- 多个函数会按声明顺序执行,跨文件时按文件名字母顺序——逻辑脆弱
init() - 无法返回错误——失败时必须Panic或调用
log.Fatal - 在和测试执行前运行——副作用会导致测试不可预测
main()
go
// 不良实践——隐藏的全局状态
var db *sql.DB
func init() {
var err error
db, err = sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
log.Fatal(err)
}
}
// 良好实践——显式初始化,支持依赖注入
func NewUserRepository(db *sql.DB) *UserRepository {
return &UserRepository{db: db}
}Enums: Start at 1
枚举:从1开始
Zero values should represent invalid/unset state:
go
type Status int
const (
StatusUnknown Status = iota // 0 = invalid/unset
StatusActive // 1
StatusInactive // 2
StatusSuspended // 3
)零值应代表无效/未设置状态:
go
type Status int
const (
StatusUnknown Status = iota // 0 = 无效/未设置
StatusActive // 1
StatusInactive // 2
StatusSuspended // 3
)Compile Regexp Once
正则表达式仅编译一次
go
// Good — compiled once at package level
var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
func ValidateEmail(email string) bool {
return emailRegex.MatchString(email)
}go
// 良好实践——在包级别编译一次
var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
func ValidateEmail(email string) bool {
return emailRegex.MatchString(email)
}Use //go:embed
for Static Assets
//go:embed使用//go:embed
处理静态资源
//go:embedgo
import "embed"
//go:embed templates/*
var templateFS embed.FS
//go:embed version.txt
var version stringgo
import "embed"
//go:embed templates/*
var templateFS embed.FS
//go:embed version.txt
var version stringCompile-Time Interface Checks
编译时接口检查
→ See for the pattern.
samber/cc-skills-golang@golang-structs-interfacesvar _ Interface = (*Type)(nil)→ 有关模式的详情,请查看。
var _ Interface = (*Type)(nil)samber/cc-skills-golang@golang-structs-interfacesError Flow Patterns
错误流转模式
Error cases MUST be handled first with early return — keep the happy path at minimal indentation. → See for the full pattern and examples.
samber/cc-skills-golang@golang-code-style错误分支必须优先处理并提前返回——让主逻辑路径保持最小缩进。→ 完整模式与示例请查看。
samber/cc-skills-golang@golang-code-styleWhen to Panic vs Return Error
Panic与返回错误的适用场景
- Return error: network failures, file not found, invalid input — anything a caller can handle
- Panic: nil pointer in a place that should be impossible, violated invariant, constructors used at init time
Must* - errors: acceptable to not check —
.Close()is fine without error handlingdefer f.Close()
- 返回错误:网络故障、文件未找到、无效输入——任何调用者可以处理的情况
- Panic:在本应不可能出现的场景中遇到空指针、违反不变式、初始化时使用的构造函数失败
Must* - 错误:可以不检查——
.Close()无需错误处理是可接受的defer f.Close()
Data Handling
数据处理
string vs []byte vs []rune
string vs []byte vs []rune
| Type | Default for | Use when |
|---|---|---|
| Everything | Immutable, safe, UTF-8 |
| I/O | Writing to |
| Unicode ops | |
Avoid repeated conversions — each one allocates. Stay in one type until you need the other.
| 类型 | 默认适用场景 | 使用时机 |
|---|---|---|
| 大多数场景 | 不可变、安全、UTF-8编码 |
| I/O操作 | 写入 |
| Unicode操作 | 需要 |
避免重复的类型转换——每次转换都会产生内存分配。在需要切换类型前,尽量保持使用同一类型。
Iterators & Streaming for Large Data
大数据处理:迭代器与流式传输
Use iterators (Go 1.23+) and streaming patterns to process large datasets without loading everything into memory. For large transfers between services (e.g., 1M rows DB to HTTP), stream to prevent OOM.
For code examples, see Data Handling Patterns.
使用迭代器(Go 1.23+)和流式模式处理大型数据集,避免将所有数据加载到内存中。在服务间传输大量数据时(例如从数据库向HTTP传输100万行数据),使用流式传输可防止OOM。
代码示例请查看数据处理模式。
Resource Management
资源管理
defer Close()go
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // right here, not 50 lines later
rows, err := db.QueryContext(ctx, query)
if err != nil {
return err
}
defer rows.Close()For graceful shutdown, resource pools, and , see Resource Management.
runtime.AddCleanup打开资源后立即使用——不要等待,不要遗忘:
defer Close()go
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // 就在这里,不要等到50行之后
rows, err := db.QueryContext(ctx, query)
if err != nil {
return err
}
defer rows.Close()有关优雅停机、资源池和的详情,请查看资源管理。
runtime.AddCleanupResilience & Limits
弹性设计与资源限制
Timeout Every External Call
所有外部调用都设置超时
go
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
resp, err := httpClient.Do(req.WithContext(ctx))go
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
resp, err := httpClient.Do(req.WithContext(ctx))Retry & Context Checks
重试与上下文检查
Retry logic MUST check between attempts and use exponential/linear backoff via on . Long loops MUST check periodically. → See skill.
ctx.Err()selectctx.Done()ctx.Err()samber/cc-skills-golang@golang-context重试逻辑必须在每次尝试间检查,并通过监听实现指数/线性退避。长循环必须定期检查。→ 详情请查看 skill。
ctx.Err()selectctx.Done()ctx.Err()samber/cc-skills-golang@golang-contextDatabase Patterns
数据库模式
→ See skill for sqlx/pgx, transactions, nullable columns, connection pools, repository interfaces, testing.
samber/cc-skills-golang@golang-database→ 有关sqlx/pgx、事务、可空列、连接池、仓库接口和测试的详情,请查看 skill。
samber/cc-skills-golang@golang-databaseArchitecture
架构设计
Ask the developer which architecture they prefer: clean architecture, hexagonal, DDD, or flat layout. Don't impose complex architecture on a small project.
Core principles regardless of architecture:
- Keep domain pure — no framework dependencies in the domain layer
- Fail fast — validate at boundaries, trust internal code
- Make illegal states unrepresentable — use types to enforce invariants
- Respect 12-factor app principles — → see
samber/cc-skills-golang@golang-project-layout
先询问开发者偏好的架构类型:整洁架构、六边形架构、DDD或扁平化布局。不要在小型项目中强加复杂架构。
无论采用哪种架构,核心原则如下:
- 保持领域层纯净——领域层不依赖任何框架
- 快速失败——在边界层验证数据,信任内部代码
- 让非法状态无法被表示——使用类型强制保证不变式
- 遵循12要素应用原则——→ 详见
samber/cc-skills-golang@golang-project-layout
Detailed Guides
详细指南
| Guide | Scope |
|---|---|
| Architecture Patterns | High-level principles, when each architecture fits |
| Clean Architecture | Use cases, dependency rule, layered adapters |
| Hexagonal Architecture | Ports and adapters, domain core isolation |
| Domain-Driven Design | Aggregates, value objects, bounded contexts |
| 指南 | 范围 |
|---|---|
| 架构模式 | 高层原则、各架构的适用场景 |
| 整洁架构 | 用例、依赖规则、分层适配器 |
| 六边形架构 | 端口与适配器、领域核心隔离 |
| 领域驱动设计 | 聚合根、值对象、限界上下文 |
Code Philosophy
代码哲学
- Avoid repetitive code — but don't abstract prematurely
- Minimize dependencies — a little recode > a big dependency
- Design for testability — accept interfaces, inject dependencies, keep functions pure
- 避免重复代码——但不要过早抽象
- 最小化依赖——少量重复代码优于引入大型依赖
- 为可测试性而设计——接收接口,注入依赖,保持函数纯态
Cross-References
交叉引用
- → See skill for data structure selection, internals, and container/ packages
samber/cc-skills-golang@golang-data-structures - → See skill for error wrapping, sentinel errors, and the single handling rule
samber/cc-skills-golang@golang-error-handling - → See skill for interface design and composition
samber/cc-skills-golang@golang-structs-interfaces - → See skill for goroutine lifecycle and graceful shutdown
samber/cc-skills-golang@golang-concurrency - → See skill for timeout and cancellation patterns
samber/cc-skills-golang@golang-context - → See skill for architecture and directory structure
samber/cc-skills-golang@golang-project-layout
- → 有关数据结构选型、内部实现和container包的详情,请查看skill
samber/cc-skills-golang@golang-data-structures - → 有关错误包装、哨兵错误和单一处理规则的详情,请查看skill
samber/cc-skills-golang@golang-error-handling - → 有关接口设计与组合的详情,请查看skill
samber/cc-skills-golang@golang-structs-interfaces - → 有关goroutine生命周期和优雅停机的详情,请查看skill
samber/cc-skills-golang@golang-concurrency - → 有关超时和取消模式的详情,请查看skill
samber/cc-skills-golang@golang-context - → 有关架构和目录结构的详情,请查看skill
samber/cc-skills-golang@golang-project-layout