eduardo-sl/go-agent-skills

go-design-patterns

Idiomatic Go design patterns: functional options, builder, factory, strategy, middleware chain, pub/sub, and other patterns adapted for Go's type system.

View source

How to use

  1. Copy the installation command and review the source package before installing it in your own agent environment.
  2. Check the source document for required tools, credentials and supported hosts.
  3. Describe your input and desired deliverable; review the output against the source material.
npx skills add https://github.com/eduardo-sl/go-agent-skills --skill go-design-patterns

Before you start

  • Installation does not include a model subscription, API allowance or permission to access your files.
  • Repository stars and installation counts describe activity, not an independent quality or security test.
Original skill document

Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.

Go Design Patterns

Go favors composition over inheritance and simplicity over abstraction. These patterns are idiomatic Go — not Java patterns ported to Go.

Detailed reference material, loaded on demand:

  • references/creation-patterns.md — functional options (full example),

options vs config struct, constructors, factory.

  • references/behavioral-patterns.md — strategy, middleware/decorator,

result type, defer cleanup, sentinel vs zero values.

Read a reference file only when the summary below is not enough.

Pattern Selection

NeedPatternReference
Constructor with many optional settingsFunctional optionscreation-patterns.md
Config loaded from file/env, mostly required fieldsConfig structcreation-patterns.md
Enforce invariants at creationConstructor returning errorcreation-patterns.md
Pick implementation from runtime configFactory returning interfacecreation-patterns.md
Swap simple behavior at runtimeStrategy via function typebehavioral-patterns.md
Swap complex behavior at runtimeStrategy via interfacebehavioral-patterns.md
Wrap cross-cutting concerns (log, cache, metrics)Middleware / decoratorbehavioral-patterns.md
Value-or-error in concurrent pipelinesResult[T] structbehavioral-patterns.md

1. Functional Options (essentials)

go
type Option func(*Server)

func WithAddr(addr string) Option {
    return func(s *Server) { s.addr = addr }
}

func NewServer(opts ...Option) *Server {
    s := &Server{
        addr:        ":8080", // sensible defaults first
        readTimeout: 5 * time.Second,
        logger:      slog.Default(),
    }
    for _, opt := range opts {
        opt(s)
    }
    return s
}

srv := NewServer(WithAddr(":9090"))

Use when: many optional parameters with sensible defaults, API evolves over time (new options don't break callers), options need validation. Use a plain config struct instead when most fields are required or the configuration is deserialized from file/env.

2. Constructor Rules

  • Every exported type with invariants needs a constructor.
  • Validate required dependencies; return an error, don't panic:
go
// ✅ Good — constructor enforces invariants
func NewUserService(repo UserRepository, logger *slog.Logger) (*UserService, error) {
    if repo == nil {
        return nil, errors.New("user service: nil repository")
    }
    return &UserService{repo: repo, logger: logger}, nil
}

// ❌ Bad — struct literal with no validation
svc := &UserService{} // nil dependencies → panic at runtime

3. Factory

Return the interface, not a concrete type. The factory is the only place that knows about concrete implementations:

go
func NewStore(cfg Config) (Store, error) {
    switch cfg.StoreType {
    case "redis":
        return newRedisStore(cfg.RedisAddr)
    case "memory":
        return newMemoryStore(), nil
    default:
        return nil, fmt.Errorf("unknown store type: %s", cfg.StoreType)
    }
}

4. Middleware Chain

The standard HTTP composition pattern:

go
type Middleware func(http.Handler) http.Handler

func Chain(handler http.Handler, middlewares ...Middleware) http.Handler {
    for i := len(middlewares) - 1; i >= 0; i-- {
        handler = middlewares[i](handler)
    }
    return handler
}

handler := Chain(appHandler, Recoverer, RequestID, Logger, Auth)

The same shape works for any interface: stack decorators as cache → logging → metrics → actual repo (see references/behavioral-patterns.md).

5. Zero Values First

Prefer types whose zero value is useful (sync.Mutex, bytes.Buffer, nil slices). Reach for sentinel wrappers or pointers only when the zero value is ambiguous as an input (nil *float64 = "not configured").

Anti-Patterns to Avoid

go
// ❌ God interface — too many methods
type Service interface {
    GetUser(ctx context.Context, id string) (*User, error)
    CreateUser(ctx context.Context, u *User) error
    DeleteUser(ctx context.Context, id string) error
    ListOrders(ctx context.Context, userID string) ([]Order, error)
    // 20 more methods...
}
// → Split into focused interfaces: UserReader, UserWriter, OrderLister

// ❌ Premature abstraction — interface for one implementation
type UserCache interface {
    Get(key string) (*User, bool)
    Set(key string, user *User)
}
// If there's only ever one implementation, use the concrete type.
// Extract an interface when a second consumer or implementation appears.

// ❌ Java-style inheritance simulation
type BaseService struct{ /* ... */ }
type UserService struct{ BaseService } // embedding is NOT inheritance
// → Use composition: UserService has a dependency, not a parent.

Verification Checklist

  1. Functional options used for types with optional configuration
  2. Constructors validate required dependencies and return errors
  3. Factory functions return interfaces, not concrete types
  4. No god interfaces — each interface has 1-3 methods
  5. Middleware follows func(http.Handler) http.Handler signature
  6. Decorators wrap interfaces, not concrete types
  7. defer used for all resource cleanup (files, connections, locks)
  8. Zero values are meaningful — no unnecessary initialization
  9. No premature abstractions — interfaces extracted only when needed
  10. Composition used instead of embedding for code reuse

Related by task

from this repository

More skills

All skills
eduardo-sl
Community

go-ci

Continuous integration for Go projects: GitHub Actions pipelines, caching, golangci-lint setup, test/coverage gates, vulnerability scanning, build matrices, and Makefile targets. Use when: "set up CI", "GitHub Actions for Go", "add lint to the pipeline", "CI is slow", "coverage gate", "build matrix", "write a Makefile", "run govulncheck in CI". Not for: commit conventions (git-commit), choosing or auditing dependencies (go-dependency-audit), writing the tests themselves (go-test-quality).

installs
1
GitHub stars
71
Updated
Aug 18
eduardo-sl
Community

go-cli

Build command-line tools in Go: flag handling, subcommands, stdin/stdout discipline, exit codes, signal handling, and when Cobra/Viper earn their weight over the standard library. Use when: "build a CLI", "add a subcommand", "parse flags", "exit codes", "handle Ctrl+C", "cobra command", "read from stdin", "CLI UX". Not for: HTTP APIs (go-api-design), scaffolding (go-project-layout), service configuration (go-architecture-review).

installs
1
GitHub stars
71
Updated
Aug 18
eduardo-sl
Community

go-coding-standards

Go coding standards and style conventions grounded in Effective Go, Go Code Review Comments, and production-proven idioms. Use when writing or reviewing Go code, enforcing naming conventions, import ordering, variable declarations, struct initialization, or formatting rules. Trigger examples: "check Go style", "fix formatting", "review naming", "Go conventions". Not for: architecture (go-architecture-review), concurrency (go-concurrency-review), performance tuning (go-performance-review).

installs
1
GitHub stars
71
Updated
Aug 18
eduardo-sl
Community

go-context

Correct usage of context.Context in Go: propagation, cancellation, timeouts, deadlines, values, and common anti-patterns. Use when: "context usage", "context.Context", "context cancellation", "timeout", "context.WithTimeout", "context.WithCancel", "context values", "context propagation". Not for: concurrency beyond context (go-concurrency-review), HTTP middleware (go-api-design), error handling (go-error-handling).

installs
1
GitHub stars
71
Updated
Aug 18