# Golang Patterns

> When to activate: general Go code, idiomatic Go, interfaces, error handling, defer, init, blank identifier, iota

- Skill: `mattakushi432/golang-patterns` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mattakushi432/golang-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mattakushi432/golang-patterns/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Mattakushi432 (https://skillmd.com/u/mattakushi432)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mattakushi432/golang-patterns

---


# Go Patterns

## Error Handling

Errors are values — wrap, check, and propagate explicitly.

```go
var (
    ErrNotFound   = errors.New("not found")
    ErrPermission = errors.New("permission denied")
)

type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation error: %s — %s", e.Field, e.Message)
}

// Wrap with %w to preserve the chain for errors.Is / errors.As
func loadUser(id string) (User, error) {
    u, err := db.FindUser(id)
    if err != nil {
        return User{}, fmt.Errorf("loadUser %s: %w", id, err)
    }
    return u, nil
}

if errors.Is(err, ErrNotFound) { /* handle */ }

var ve *ValidationError
if errors.As(err, &ve) { fmt.Println("bad field:", ve.Field) }
```

## Interfaces

Define interfaces at the point of use (consumer), not at implementation.

```go
// Small, focused interface
type Storer interface {
    Save(ctx context.Context, item Item) error
    Load(ctx context.Context, id string) (Item, error)
}

// Compose small interfaces
type ReadWriteCloser interface {
    io.Reader
    io.Writer
    io.Closer
}

// Accept interfaces, return concrete structs
func Process(r io.Reader) error { ... }
func NewProcessor(cfg Config) (*Processor, error) { ... }
```

## Defer

```go
func writeFile(path string, data []byte) error {
    f, err := os.Create(path)
    if err != nil { return err }
    defer f.Close()

    _, err = f.Write(data)
    return err
}

// Named returns + defer for logging
func fetchUser(id string) (user User, err error) {
    defer func() {
        if err != nil { log.Printf("fetchUser %s: %v", id, err) }
    }()
    user, err = db.Find(id)
    return
}
```

## Struct Embedding

```go
type Logger struct{ prefix string }
func (l *Logger) Log(msg string) { fmt.Printf("[%s] %s\n", l.prefix, msg) }

type Server struct {
    Logger        // promotes Log method
    addr   string
}

s := Server{Logger: Logger{prefix: "server"}, addr: ":8080"}
s.Log("started")
```

## iota for Typed Enums

```go
type Direction int

const (
    North Direction = iota
    East
    South
    West
)

func (d Direction) String() string {
    return [...]string{"North", "East", "South", "West"}[d]
}

type ByteSize float64
const (
    _           = iota
    KB ByteSize = 1 << (10 * iota)
    MB
    GB
    TB
)
```

## Functional Options

```go
type ServerOption func(*Server)

func WithTimeout(d time.Duration) ServerOption {
    return func(s *Server) { s.timeout = d }
}

func WithMaxConns(n int) ServerOption {
    return func(s *Server) { s.maxConns = n }
}

func NewServer(addr string, opts ...ServerOption) *Server {
    s := &Server{addr: addr, timeout: 30 * time.Second, maxConns: 100}
    for _, o := range opts { o(s) }
    return s
}

// Usage
srv := NewServer(":8080", WithTimeout(60*time.Second), WithMaxConns(200))
```

## Common Anti-Patterns

- **Panic for recoverable errors** — return errors; panic only for programming bugs
- **Large interfaces** — 1-3 methods max; compose if you need more
- **Ignoring errors with `_`** — always check errors unless justified in comments
- **`init()` with side effects** — use explicit initialization functions instead
- **Naked returns in long functions** — hurt readability; only OK in short functions

