Idiomatic Golang design patterns — functional options, constructors, error flow and cascading, resource management and lifecycle, graceful shutdown, resilience, architecture, dependency injection, data handling, streaming, and more. Apply when explicitly choosing between architectural patterns, implementing functional options, designing constructor APIs, setting up graceful shutdown, applying resilience patterns, or asking which idiomatic Go pattern fits a specific problem.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Um comando direto ignora o prompt de revisão. Verifique a origem antes de executá-lo.
Idiomatic Golang design patterns — functional options, constructors, error flow and cascading, resource management and lifecycle, graceful shutdown, resilience, architecture, dependency injection, data handling, streaming, and more. Apply when explicitly choosing between architectural patterns, implementing functional options, designing constructor APIs, setting up graceful shutdown, applying resilience patterns, or asking which idiomatic Go pattern fits a specific problem.
user-invocable
true
license
MIT
compatibility
Designed for Claude Code or similar AI coding agents, and for projects using Golang.
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:
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 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-patterns skill takes precedence.
Go Design Patterns & Idioms
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.
Best 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 init() — runs implicitly, cannot return errors, makes testing unpredictable. Use explicit constructors
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
defer Close() immediately after opening — later code changes can accidentally skip cleanup
runtime.AddCleanup over runtime.SetFinalizer — finalizers are unpredictable and can resurrect objects
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 strings.Builder for concatenation in loops → see samber/cc-skills-golang@golang-code-style
string vs []byte: use []byte for mutation and I/O, string for display and keys — conversions allocate
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
//go:embed for static assets — embeds at compile time, eliminates runtime file I/O errors
Use crypto/rand for keys/tokens — math/rand is predictable → see samber/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
Constructor Patterns: Functional Options vs Builder
Functional Options (Preferred)
type Server struct {
addr string
readTimeout time.Duration
writeTimeout time.Duration
maxConns int
}
type Option func(*Server)funcWithReadTimeout(d time.Duration) Option {
returnfunc(s *Server) { s.readTimeout = d }
}
funcWithWriteTimeout(d time.Duration) Option {
returnfunc(s *Server) { s.writeTimeout = d }
}
funcWithMaxConns(n int) Option {
returnfunc(s *Server) { s.maxConns = n }
}
funcNewServer(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.
Constructors & Initialization
Avoid init() and Mutable Globals
init() 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 statevar db *sql.DB
funcinit() {
var err error
db, err = sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
log.Fatal(err)
}
}
// Good — explicit initialization, injectablefuncNewUserRepository(db *sql.DB) *UserRepository {
return &UserRepository{db: db}
}
Enums: Start at 1
Zero values should represent invalid/unset state:
type Status intconst (
StatusUnknown Status = iota// 0 = invalid/unset
StatusActive // 1
StatusInactive // 2
StatusSuspended // 3
)
Compile Regexp Once
// Good — compiled once at package levelvar emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
funcValidateEmail(email string)bool {
return emailRegex.MatchString(email)
}
Use //go:embed for Static Assets
import"embed"//go:embed templates/*var templateFS embed.FS
//go:embed version.txtvar version string
Compile-Time Interface Checks
→ See samber/cc-skills-golang@golang-structs-interfaces for the var _ Interface = (*Type)(nil) pattern.
Error Flow Patterns
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.
When to Panic vs Return Error
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, Must* constructors used at init time
.Close() / Flush() errors: read-only cleanup can often use defer f.Close(), but write/flush resources must report close or flush errors when durability matters
Data Handling
string vs []byte vs []rune
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.
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.
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.
Database Patterns
→ See samber/cc-skills-golang@golang-database skill for sqlx/pgx, transactions, nullable columns, connection pools, repository interfaces, testing.
Architecture
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
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 samber/cc-skills-golang@golang-data-structures skill for data structure selection, internals, and container/ packages
→ See samber/cc-skills-golang@golang-error-handling skill for error wrapping, sentinel errors, and the single handling rule
→ See samber/cc-skills-golang@golang-structs-interfaces skill for interface design and composition
→ See samber/cc-skills-golang@golang-concurrency skill for goroutine lifecycle and graceful shutdown
→ See samber/cc-skills-golang@golang-context skill for timeout and cancellation patterns
→ See samber/cc-skills-golang@golang-project-layout skill for architecture and directory structure
→ See samber/cc-skills-golang@golang-refactoring skill for safely staging a migration toward one of these patterns (options struct, DI, consumer-side interfaces) across an existing codebase