From frostyard-dev
WHEN: General Go pattern questions, code review, asking "what's the best way to..." in Go. WHEN NOT: Non-Go languages. Topics already covered by uber-go-style.
How this skill is triggered — by the user, by Claude, or both
Slash command
/frostyard-dev:go-best-practicesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Priority:** Supplements `uber-go-style` (the baseline) with patterns Uber doesn't cover. For modern Go syntax, see `use-modern-go`. For frostyard org conventions, see `frostyard-conventions`.
Priority: Supplements
uber-go-style(the baseline) with patterns Uber doesn't cover. For modern Go syntax, seeuse-modern-go. For frostyard org conventions, seefrostyard-conventions.
Apply idiomatic Go patterns for interfaces, concurrency, testing, packages, and naming.
// Good - interface defined by consumer
type Reader interface {
Read(p []byte) (n int, err error)
}
func ProcessData(r Reader) error { ... }
// Avoid - exporting implementation details
type Service interface {
Method1() error
Method2() error
Method3() error // Too many methods
}
// Good - using errgroup
g, ctx := errgroup.WithContext(ctx)
for _, item := range items {
item := item // capture loop variable
g.Go(func() error {
return process(ctx, item)
})
}
if err := g.Wait(); err != nil {
return err
}
To collect ALL errors (not just the first), use a custom collector or errors.Join:
var mu sync.Mutex
var errs []error
for _, item := range items {
g.Go(func() error {
if err := process(ctx, item); err != nil {
mu.Lock()
errs = append(errs, err)
mu.Unlock()
}
return nil
})
}
g.Wait()
return errors.Join(errs...)
os.Interrupt and syscall.SIGTERM with signal.NotifyContextcontext.Context cancellation to propagate shutdownserver.Shutdown(ctx) for HTTP servers — not Close()ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
srv := &http.Server{Addr: ":8080", Handler: mux}
go func() { srv.ListenAndServe() }()
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(shutdownCtx)
log/slog (Go 1.21+) for structured logging*slog.Logger via dependency injection, not package globalsslog.With to add context fieldsDebug for development, Info for operations, Warn for recoverable issues, Error for failuresfunc NewServer(logger *slog.Logger) *Server {
return &Server{log: logger.With("component", "server")}
}
func (s *Server) Handle(r *http.Request) {
s.log.Info("request received", "method", r.Method, "path", r.URL.Path)
}
maintype Config struct {
Port int `env:"PORT"`
Timeout time.Duration `env:"TIMEOUT"`
}
func DefaultConfig() Config {
return Config{Port: 8080, Timeout: 30 * time.Second}
}
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"positive numbers", 2, 3, 5},
{"with zero", 5, 0, 5},
{"negative numbers", -2, -3, -5},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("Add(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want)
}
})
}
}
user not userServiceURL, HTTP, ID (all caps for exported, all lower otherwise)i for loop index, err for errorsReadConfig not RCinterface{} or any): Use specific types when possibleuser.UserService should be user.Servicego vet and staticcheck for automated guidancenpx claudepluginhub frostyard/frostyard-ai --plugin frostyard-devProvides idiomatic Go patterns and best practices for error handling, concurrency like worker pools, simplicity, zero values, and interfaces. Activates for writing, reviewing, refactoring, or designing Go code.
Go language conventions, idioms, and toolchain. Invoke when task involves any interaction with Go code — writing, reviewing, refactoring, debugging, or understanding Go projects.
Idiomatic Go patterns, best practices, and conventions for building robust, efficient, and maintainable Go applications.