go-idioms

Boring, explicit, race-safe Go. Accept interfaces, return structs.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "go-idioms" with this command: npx skills add phrazzld/claude-config/phrazzld-claude-config-go-idioms

Go Idioms

Boring, explicit, race-safe Go. Accept interfaces, return structs.

Error Handling

Always wrap with context at package boundaries:

// Use %w to preserve error chain return fmt.Errorf("fetching user %s: %w", userID, err)

// Check wrapped errors if errors.Is(err, sql.ErrNoRows) { ... }

var paymentErr *PaymentError if errors.As(err, &paymentErr) { ... }

Never: %v (loses type), raw errors from exported functions, generic context.

Interface Design

Define interfaces in consuming package, not provider:

// notification/sender.go (consumer defines interface) type EmailSender interface { Send(ctx context.Context, to, subject, body string) error }

// email/client.go (provider implements) type Client struct { ... } func (c *Client) Send(ctx context.Context, to, subject, body string) error { ... }

Small interfaces (1-3 methods). Compose larger from smaller:

type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } type ReadWriter interface { Reader; Writer }

Compile-time verification:

var _ EmailSender = (*Client)(nil)

Concurrency

Always propagate context:

func FetchData(ctx context.Context) ([]byte, error) { select { case <-ctx.Done(): return nil, ctx.Err() case result := <-dataChan: return result, nil } }

Bounded concurrency (semaphore):

sem := make(chan struct{}, 10) for _, item := range items { sem <- struct{}{} go func(item Item) { defer func() { <-sem }() process(item) }(item) }

Race safety: Always run go test -race ./...

Package Design

internal/ user/ # Domain: single purpose user.go service.go repository.go order/ # Another domain app/ # Dependency wiring cmd/ api/ # Entry points

Rules:

  • Single purpose per package

  • No generic names (utils, helpers, common)

  • No circular dependencies

  • Export only what's necessary

Dependency Injection

// Constructor accepts interfaces func NewUserService(repo UserRepository, mailer EmailSender) *UserService { return &UserService{repo: repo, mailer: mailer} }

// Wire in app/ package func NewApp() *App { repo := postgres.NewUserRepo(db) mailer := sendgrid.NewClient(apiKey) userSvc := user.NewUserService(repo, mailer) return &App{UserService: userSvc} }

Deps Struct Pattern (5+ Parameters)

When constructor takes 5+ parameters, use a deps struct:

// Group dependencies into named struct type ServiceDeps struct { Repo Repository Mailer EmailSender Logger Logger Config *Config Cache Cache }

// Panic on nil - catches programming errors at construction func NewService(deps ServiceDeps) *Service { if deps.Repo == nil { panic("NewService: Repo cannot be nil") } if deps.Mailer == nil { panic("NewService: Mailer cannot be nil") } // ... check all required deps return &Service{...} }

Benefits:

  • Named fields = self-documenting call sites

  • Adding deps doesn't change signature

  • Nil panics catch bugs at construction, not at runtime

When changing old constructor to deps struct:

  • Update ALL call sites to struct literal pattern

  • Check for tests expecting old nil-tolerance behavior

Test Cleanup

Use t.Cleanup() for teardown, not defer with error suppression:

// BAD - Error suppression hides failures defer func() { _ = os.Chdir(orig) }() // SA4017 warning

// GOOD - Proper cleanup with error handling t.Cleanup(func() { if err := os.Chdir(orig); err != nil { t.Errorf("cleanup failed: %v", err) } })

t.Cleanup runs after test completes (even on panic), integrates with test reporting.

Anti-Patterns

  • Goroutines without cancellation path (leaks)

  • Monolithic interfaces (10+ methods)

  • Framework-like inheritance patterns

  • Reflection when explicit types work

  • Global singletons for dependencies

  • Generic everything (overuse of generics)

  • interface{} / any without justification

  • defer with error suppression in tests

Embrace Boring

  • Explicit error handling at each step

  • Standard library first (map , []T , sort.Slice )

  • Table-driven tests

  • Struct composition, not inheritance

  • Clear, verbose code over clever code

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

pencil-renderer

No summary provided by upstream source.

Repository SourceNeeds Review
General

ui-skills

No summary provided by upstream source.

Repository SourceNeeds Review
General

llm-gateway-routing

No summary provided by upstream source.

Repository SourceNeeds Review