golang-design-patterns by samber/cc-skills-golang
npx skills add https://github.com/samber/cc-skills-golang --skill golang-design-patterns角色设定: 你是一位崇尚简洁和明确性的 Go 架构师。你只在模式能解决实际问题时才应用它们——而非为了展示复杂性——并且你会抵制过早的抽象。
模式:
init() 滥用、无限制资源、缺少超时和隐式全局状态;在建议重构前先报告发现的问题。社区默认。 一个明确取代
samber/cc-skills-golang@golang-design-patterns技能的团队技能具有优先权。
适用于生产就绪代码的惯用 Go 模式。关于错误处理细节,请参阅 samber/cc-skills-golang@golang-error-handling 技能;关于上下文传播,请参阅 samber/cc-skills-golang@golang-context 技能;关于结构体/接口设计,请参阅 samber/cc-skills-golang@golang-structs-interfaces 技能。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
init() — 隐式运行,无法返回错误,使测试不可预测。使用显式构造函数defer Close() — 后续的代码更改可能会意外跳过清理runtime.AddCleanup 而非 runtime.SetFinalizer — finalizer 不可预测且可能导致对象复活strings.Builder → 参见 samber/cc-skills-golang@golang-code-style[]byte 进行修改和 I/O,string 用于显示和键 — 转换会分配内存//go:embed 处理静态资源 — 在编译时嵌入,消除运行时文件 I/O 错误crypto/rand 生成密钥/令牌 — math/rand 是可预测的 → 参见 samber/cc-skills-golang@golang-securityvar _ Interface = (*Type)(nil)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 演进,并且需要更少的代码。仅当需要在配置步骤之间进行复杂验证时才使用构建器模式。
init() 和可变全局变量init() 隐式运行,使测试更困难,并创建隐藏的依赖关系:
多个 init() 函数按声明顺序运行,跨文件时按文件名字母顺序运行 — 很脆弱
无法返回错误 — 失败必须 panic 或 log.Fatal
在 main() 和测试之前运行 — 副作用使测试不可预测
// 不好 — 隐藏的全局状态 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} }
零值应表示无效/未设置状态:
type Status int
const (
StatusUnknown Status = iota // 0 = 无效/未设置
StatusActive // 1
StatusInactive // 2
StatusSuspended // 3
)
// 好 — 在包级别编译一次
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:embed 处理静态资源import "embed"
//go:embed templates/*
var templateFS embed.FS
//go:embed version.txt
var version string
→ 关于 var _ Interface = (*Type)(nil) 模式,请参见 samber/cc-skills-golang@golang-structs-interfaces。
错误情况必须首先处理并提前返回 — 保持主路径缩进最小。→ 关于完整模式和示例,请参见 samber/cc-skills-golang@golang-code-style。
Must* 构造函数.Close() 错误:可以不检查 — defer f.Close() 不处理错误是可以接受的| 类型 | 默认用于 | 使用场景 |
|---|---|---|
string | 所有情况 | 不可变、安全、UTF-8 |
[]byte | I/O | 写入 io.Writer、构建字符串、修改 |
[]rune | Unicode 操作 | len() 必须表示字符数,而非字节数 |
避免重复转换 — 每次转换都会分配内存。保持一种类型直到需要另一种。
使用迭代器(Go 1.23+)和流式模式来处理大型数据集,而无需将所有内容加载到内存中。对于服务之间的大型数据传输(例如,从数据库到 HTTP 的 100 万行数据),使用流式传输以防止 OOM。
关于代码示例,请参见数据处理模式。
打开资源后立即 defer Close() — 不要等待,不要忘记:
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.AddCleanup,请参见资源管理。
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
resp, err := httpClient.Do(req.WithContext(ctx))
重试逻辑必须在尝试之间检查 ctx.Err(),并通过 select 在 ctx.Done() 上使用指数/线性退避。长循环必须定期检查 ctx.Err()。→ 参见 samber/cc-skills-golang@golang-context 技能。
→ 关于 sqlx/pgx、事务、可空列、连接池、仓库接口、测试,请参见 samber/cc-skills-golang@golang-database 技能。
询问开发者他们偏好哪种架构:整洁架构、六边形架构、DDD 或扁平布局。不要将复杂的架构强加于小型项目。
无论何种架构,核心原则是:
samber/cc-skills-golang@golang-project-layoutsamber/cc-skills-golang@golang-data-structures 技能samber/cc-skills-golang@golang-error-handling 技能samber/cc-skills-golang@golang-structs-interfaces 技能samber/cc-skills-golang@golang-concurrency 技能samber/cc-skills-golang@golang-context 技能samber/cc-skills-golang@golang-project-layout 技能每周安装数
132
仓库
GitHub 星标数
276
首次出现
4 天前
安全审计
安装于
opencode111
gemini-cli108
codex108
cursor108
amp107
github-copilot107
Persona: 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:
init() abuse, unbounded resources, missing timeouts, and implicit global state; report findings before suggesting refactors.Community default. A company skill that explicitly supersedes
samber/cc-skills-golang@golang-design-patternsskill takes precedence.
Idiomatic Go patterns for production-ready code. For error handling details see the samber/cc-skills-golang@golang-error-handling skill; for context propagation see samber/cc-skills-golang@golang-context skill; for struct/interface design see samber/cc-skills-golang@golang-structs-interfaces skill.
init() — runs implicitly, cannot return errors, makes testing unpredictable. Use explicit constructorsdefer Close() immediately after opening — later code changes can accidentally skip cleanupruntime.AddCleanup over runtime.SetFinalizer — finalizers are unpredictable and can resurrect objectstype 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.
init() and Mutable Globalsinit() runs implicitly, makes testing harder, and creates hidden dependencies:
Multiple init() functions run in declaration order, across files in filename alphabetical order — fragile
Cannot return errors — failures must panic or log.Fatal
Runs before main() and tests — side effects make tests unpredictable
// 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} }
Zero values should represent invalid/unset state:
type Status int
const (
StatusUnknown Status = iota // 0 = invalid/unset
StatusActive // 1
StatusInactive // 2
StatusSuspended // 3
)
// 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:embed for Static Assetsimport "embed"
//go:embed templates/*
var templateFS embed.FS
//go:embed version.txt
var version string
→ See samber/cc-skills-golang@golang-structs-interfaces for the var _ Interface = (*Type)(nil) pattern.
Error cases MUST be handled first with early return — keep the happy path at minimal indentation. → See samber/cc-skills-golang@golang-code-style for the full pattern and examples.
Must* constructors used at init time.Close() errors: acceptable to not check — defer f.Close() is fine without error handling| Type | Default for | Use when |
|---|---|---|
string | Everything | Immutable, safe, UTF-8 |
[]byte | I/O | Writing to io.Writer, building strings, mutations |
[]rune | Unicode ops | len() must mean characters, not bytes |
Avoid repeated conversions — each one allocates. Stay in one type until you need the other.
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.
defer Close() immediately after opening — don't wait, don't forget:
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 runtime.AddCleanup, see Resource Management.
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
resp, err := httpClient.Do(req.WithContext(ctx))
Retry logic MUST check ctx.Err() between attempts and use exponential/linear backoff via select on ctx.Done(). Long loops MUST check ctx.Err() periodically. → See samber/cc-skills-golang@golang-context skill.
→ See samber/cc-skills-golang@golang-database skill for sqlx/pgx, transactions, nullable columns, connection pools, repository interfaces, testing.
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:
samber/cc-skills-golang@golang-project-layout| 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 |
samber/cc-skills-golang@golang-data-structures skill for data structure selection, internals, and container/ packagessamber/cc-skills-golang@golang-error-handling skill for error wrapping, sentinel errors, and the single handling rulesamber/cc-skills-golang@golang-structs-interfaces skill for interface design and compositionsamber/cc-skills-golang@golang-concurrency skill for goroutine lifecycle and graceful shutdownsamber/cc-skills-golang@golang-context skill for timeout and cancellation patternssamber/cc-skills-golang@golang-project-layout skill for architecture and directory structureWeekly Installs
132
Repository
GitHub Stars
276
First Seen
4 days ago
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode111
gemini-cli108
codex108
cursor108
amp107
github-copilot107
Perl 5.36+ 现代开发模式与最佳实践 | 构建健壮可维护应用程序指南
992 周安装
云迁移规划指南:策略、最佳实践与Python评估工具,实现平稳云端迁移
131 周安装
Webhook集成指南:实现实时通信与第三方服务集成的最佳实践
131 周安装
AXe iOS 模拟器 UI 自动化 CLI 工具:基于辅助功能 API 的稳定测试
131 周安装
iOS Core Data 完整指南:与 SwiftData 对比、现代堆栈设置与迁移策略
131 周安装
iOS深度链接调试指南:SwiftUI自动化测试与截图解决方案
131 周安装
iOS SwiftUI/UIKit 照片库访问指南:PhotoKit、PhotosPicker、PHPicker 隐私保护开发
131 周安装
strings.Builder for concatenation in loops → see samber/cc-skills-golang@golang-code-style[]byte for mutation and I/O, string for display and keys — conversions allocate//go:embed for static assets — embeds at compile time, eliminates runtime file I/O errorscrypto/rand for keys/tokens — math/rand is predictable → see samber/cc-skills-golang@golang-securityvar _ Interface = (*Type)(nil)