Idiomatic Go patterns for error handling, interfaces, concurrency, testing, and module management
Generates idiomatic Go code for error handling, concurrency, testing, and module management.
/plugin marketplace add https://www.claudepluginhub.com/api/plugins/rohitg00-claude-code-toolkit/marketplace.json/plugin install rohitg00-claude-code-toolkit@cpd-rohitg00-claude-code-toolkitThis skill inherits all available tools. When active, it can use any tool Claude has access to.
// Return errors, never panic in library code
func LoadConfig(path string) (Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return Config{}, fmt.Errorf("reading config %s: %w", path, err)
}
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return Config{}, fmt.Errorf("parsing config: %w", err)
}
return cfg, nil
}
Rules:
fmt.Errorf("context: %w", err)%w to allow callers to use errors.Is and errors.Asvar (
ErrNotFound = errors.New("not found")
ErrUnauthorized = errors.New("unauthorized")
)
func GetUser(id string) (User, error) {
user, ok := store[id]
if !ok {
return User{}, fmt.Errorf("user %s: %w", id, ErrNotFound)
}
return user, nil
}
// Caller
user, err := GetUser(id)
if errors.Is(err, ErrNotFound) {
http.Error(w, "user not found", http.StatusNotFound)
return
}
// Keep interfaces small (1-3 methods)
type Reader interface {
Read(p []byte) (n int, err error)
}
type UserStore interface {
GetUser(ctx context.Context, id string) (User, error)
CreateUser(ctx context.Context, u User) error
}
// Accept interfaces, return structs
func NewService(store UserStore, logger *slog.Logger) *Service {
return &Service{store: store, logger: logger}
}
Rules:
io.Reader, io.Writer, fmt.Stringer from the standard libraryer suffixfunc process(ctx context.Context, jobs <-chan Job, workers int) <-chan Result {
results := make(chan Result, workers)
var wg sync.WaitGroup
for range workers {
wg.Add(1)
go func() {
defer wg.Done()
for job := range jobs {
select {
case <-ctx.Done():
return
case results <- job.Execute():
}
}
}()
}
go func() {
wg.Wait()
close(results)
}()
return results
}
func fanOut[T, R any](ctx context.Context, items []T, fn func(T) R, concurrency int) []R {
sem := make(chan struct{}, concurrency)
results := make([]R, len(items))
var wg sync.WaitGroup
for i, item := range items {
wg.Add(1)
sem <- struct{}{}
go func() {
defer func() { <-sem; wg.Done() }()
results[i] = fn(item)
}()
}
wg.Wait()
return results
}
Rules:
context.Context as the first parametersync.WaitGroup to wait for goroutine completionfunc (s *Service) HandleRequest(ctx context.Context, req Request) (Response, error) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
user, err := s.store.GetUser(ctx, req.UserID)
if err != nil {
return Response{}, fmt.Errorf("getting user: %w", err)
}
ctx = context.WithValue(ctx, userKey, user)
return s.processRequest(ctx, req)
}
Rules:
context.WithTimeout or context.WithDeadline for all external callsdefer cancel() after creating a cancellable contextcontext.WithValue sparingly (request-scoped values only: trace IDs, auth info)func TestValidateEmail(t *testing.T) {
tests := []struct {
name string
email string
want bool
}{
{"valid email", "user@example.com", true},
{"missing @", "userexample.com", false},
{"empty string", "", false},
{"multiple @", "user@@example.com", false},
{"valid with subdomain", "user@mail.example.com", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateEmail(tt.email)
if got != tt.want {
t.Errorf("ValidateEmail(%q) = %v, want %v", tt.email, got, tt.want)
}
})
}
}
func newTestServer(t *testing.T) *httptest.Server {
t.Helper()
handler := setupRoutes()
srv := httptest.NewServer(handler)
t.Cleanup(srv.Close)
return srv
}
func assertEqual[T comparable](t *testing.T, got, want T) {
t.Helper()
if got != want {
t.Errorf("got %v, want %v", got, want)
}
}
Use t.Helper() in all test utility functions. Use t.Cleanup() instead of defer for test resource cleanup. Use testdata/ directory for test fixtures.
go.mod structure:
module github.com/org/project
go 1.23
require (
github.com/lib/pq v1.10.9
golang.org/x/sync v0.7.0
)
Commands:
go mod tidy # remove unused, add missing
go mod verify # verify checksums
go list -m -u all # check for updates
go get -u ./... # update all dependencies
go mod vendor # vendor dependencies (optional)
Use go mod tidy before every commit. Pin major versions. Review changelogs before updating.
Design types so their zero value is useful:
// sync.Mutex zero value is an unlocked mutex (ready to use)
var mu sync.Mutex
// bytes.Buffer zero value is an empty buffer (ready to use)
var buf bytes.Buffer
buf.WriteString("hello")
// Custom types: make zero value meaningful
type Server struct {
Addr string // defaults to ""
Handler http.Handler // defaults to nil
Timeout time.Duration // defaults to 0 (no timeout)
}
func (s *Server) ListenAndServe() error {
addr := s.Addr
if addr == "" {
addr = ":8080" // useful default
}
handler := s.Handler
if handler == nil {
handler = http.DefaultServeMux
}
// ...
}
Rules:
import "log/slog"
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
logger.Info("request handled",
slog.String("method", r.Method),
slog.String("path", r.URL.Path),
slog.Int("status", status),
slog.Duration("latency", time.Since(start)),
)
Use log/slog (standard library, Go 1.21+). Use structured fields, never string interpolation. Include request ID, user ID, and operation name in every log entry.
interface{} / any instead of concrete typesinit() for complex setup (makes testing hard)_ without commentActivates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.