Install
Terminal · npx$
npx skills add https://github.com/affaan-m/everything-claude-code --skill golang-patternsWorks with Paperclip
How Golang Patterns fits into a Paperclip company.
Golang Patterns drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.
S
SaaS FactoryPaired
Pre-configured AI company — 18 agents, 18 skills, one-time purchase.
$27$59
Explore packSource file
SKILL.md674 linesExpandCollapse
---name: golang-patternsdescription: Idiomatic Go patterns, best practices, and conventions for building robust, efficient, and maintainable Go applications.origin: ECC--- # Go Development Patterns Idiomatic Go patterns and best practices for building robust, efficient, and maintainable applications. ## When to Activate - Writing new Go code- Reviewing Go code- Refactoring existing Go code- Designing Go packages/modules ## Core Principles ### 1. Simplicity and Clarity Go favors simplicity over cleverness. Code should be obvious and easy to read. ```go// Good: Clear and directfunc GetUser(id string) (*User, error) { user, err := db.FindUser(id) if err != nil { return nil, fmt.Errorf("get user %s: %w", id, err) } return user, nil} // Bad: Overly cleverfunc GetUser(id string) (*User, error) { return func() (*User, error) { if u, e := db.FindUser(id); e == nil { return u, nil } else { return nil, e } }()}``` ### 2. Make the Zero Value Useful Design types so their zero value is immediately usable without initialization. ```go// Good: Zero value is usefultype Counter struct { mu sync.Mutex count int // zero value is 0, ready to use} func (c *Counter) Inc() { c.mu.Lock() c.count++ c.mu.Unlock()} // Good: bytes.Buffer works with zero valuevar buf bytes.Bufferbuf.WriteString("hello") // Bad: Requires initializationtype BadCounter struct { counts map[string]int // nil map will panic}``` ### 3. Accept Interfaces, Return Structs Functions should accept interface parameters and return concrete types. ```go// Good: Accepts interface, returns concrete typefunc ProcessData(r io.Reader) (*Result, error) { data, err := io.ReadAll(r) if err != nil { return nil, err } return &Result{Data: data}, nil} // Bad: Returns interface (hides implementation details unnecessarily)func ProcessData(r io.Reader) (io.Reader, error) { // ...}``` ## Error Handling Patterns ### Error Wrapping with Context ```go// Good: Wrap errors with contextfunc LoadConfig(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("load config %s: %w", path, err) } var cfg Config if err := json.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("parse config %s: %w", path, err) } return &cfg, nil}``` ### Custom Error Types ```go// Define domain-specific errorstype ValidationError struct { Field string Message string} func (e *ValidationError) Error() string { return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)} // Sentinel errors for common casesvar ( ErrNotFound = errors.New("resource not found") ErrUnauthorized = errors.New("unauthorized") ErrInvalidInput = errors.New("invalid input"))``` ### Error Checking with errors.Is and errors.As ```gofunc HandleError(err error) { // Check for specific error if errors.Is(err, sql.ErrNoRows) { log.Println("No records found") return } // Check for error type var validationErr *ValidationError if errors.As(err, &validationErr) { log.Printf("Validation error on field %s: %s", validationErr.Field, validationErr.Message) return } // Unknown error log.Printf("Unexpected error: %v", err)}``` ### Never Ignore Errors ```go// Bad: Ignoring error with blank identifierresult, _ := doSomething() // Good: Handle or explicitly document why it's safe to ignoreresult, err := doSomething()if err != nil { return err} // Acceptable: When error truly doesn't matter (rare)_ = writer.Close() // Best-effort cleanup, error logged elsewhere``` ## Concurrency Patterns ### Worker Pool ```gofunc WorkerPool(jobs <-chan Job, results chan<- Result, numWorkers int) { var wg sync.WaitGroup for i := 0; i < numWorkers; i++ { wg.Add(1) go func() { defer wg.Done() for job := range jobs { results <- process(job) } }() } wg.Wait() close(results)}``` ### Context for Cancellation and Timeouts ```gofunc FetchWithTimeout(ctx context.Context, url string) ([]byte, error) { ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, fmt.Errorf("create request: %w", err) } resp, err := http.DefaultClient.Do(req) if err != nil { return nil, fmt.Errorf("fetch %s: %w", url, err) } defer resp.Body.Close() return io.ReadAll(resp.Body)}``` ### Graceful Shutdown ```gofunc GracefulShutdown(server *http.Server) { quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("Shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() if err := server.Shutdown(ctx); err != nil { log.Fatalf("Server forced to shutdown: %v", err) } log.Println("Server exited")}``` ### errgroup for Coordinated Goroutines ```goimport "golang.org/x/sync/errgroup" func FetchAll(ctx context.Context, urls []string) ([][]byte, error) { g, ctx := errgroup.WithContext(ctx) results := make([][]byte, len(urls)) for i, url := range urls { i, url := i, url // Capture loop variables g.Go(func() error { data, err := FetchWithTimeout(ctx, url) if err != nil { return err } results[i] = data return nil }) } if err := g.Wait(); err != nil { return nil, err } return results, nil}``` ### Avoiding Goroutine Leaks ```go// Bad: Goroutine leak if context is cancelledfunc leakyFetch(ctx context.Context, url string) <-chan []byte { ch := make(chan []byte) go func() { data, _ := fetch(url) ch <- data // Blocks forever if no receiver }() return ch} // Good: Properly handles cancellationfunc safeFetch(ctx context.Context, url string) <-chan []byte { ch := make(chan []byte, 1) // Buffered channel go func() { data, err := fetch(url) if err != nil { return } select { case ch <- data: case <-ctx.Done(): } }() return ch}``` ## Interface Design ### Small, Focused Interfaces ```go// Good: Single-method interfacestype Reader interface { Read(p []byte) (n int, err error)} type Writer interface { Write(p []byte) (n int, err error)} type Closer interface { Close() error} // Compose interfaces as neededtype ReadWriteCloser interface { Reader Writer Closer}``` ### Define Interfaces Where They're Used ```go// In the consumer package, not the providerpackage service // UserStore defines what this service needstype UserStore interface { GetUser(id string) (*User, error) SaveUser(user *User) error} type Service struct { store UserStore} // Concrete implementation can be in another package// It doesn't need to know about this interface``` ### Optional Behavior with Type Assertions ```gotype Flusher interface { Flush() error} func WriteAndFlush(w io.Writer, data []byte) error { if _, err := w.Write(data); err != nil { return err } // Flush if supported if f, ok := w.(Flusher); ok { return f.Flush() } return nil}``` ## Package Organization ### Standard Project Layout ```textmyproject/├── cmd/│ └── myapp/│ └── main.go # Entry point├── internal/│ ├── handler/ # HTTP handlers│ ├── service/ # Business logic│ ├── repository/ # Data access│ └── config/ # Configuration├── pkg/│ └── client/ # Public API client├── api/│ └── v1/ # API definitions (proto, OpenAPI)├── testdata/ # Test fixtures├── go.mod├── go.sum└── Makefile``` ### Package Naming ```go// Good: Short, lowercase, no underscorespackage httppackage jsonpackage user // Bad: Verbose, mixed case, or redundantpackage httpHandlerpackage json_parserpackage userService // Redundant 'Service' suffix``` ### Avoid Package-Level State ```go// Bad: Global mutable statevar db *sql.DB func init() { db, _ = sql.Open("postgres", os.Getenv("DATABASE_URL"))} // Good: Dependency injectiontype Server struct { db *sql.DB} func NewServer(db *sql.DB) *Server { return &Server{db: db}}``` ## Struct Design ### Functional Options Pattern ```gotype Server struct { addr string timeout time.Duration logger *log.Logger} type Option func(*Server) func WithTimeout(d time.Duration) Option { return func(s *Server) { s.timeout = d }} func WithLogger(l *log.Logger) Option { return func(s *Server) { s.logger = l }} func NewServer(addr string, opts ...Option) *Server { s := &Server{ addr: addr, timeout: 30 * time.Second, // default logger: log.Default(), // default } for _, opt := range opts { opt(s) } return s} // Usageserver := NewServer(":8080", WithTimeout(60*time.Second), WithLogger(customLogger),)``` ### Embedding for Composition ```gotype Logger struct { prefix string} func (l *Logger) Log(msg string) { fmt.Printf("[%s] %s\n", l.prefix, msg)} type Server struct { *Logger // Embedding - Server gets Log method addr string} func NewServer(addr string) *Server { return &Server{ Logger: &Logger{prefix: "SERVER"}, addr: addr, }} // Usages := NewServer(":8080")s.Log("Starting...") // Calls embedded Logger.Log``` ## Memory and Performance ### Preallocate Slices When Size is Known ```go// Bad: Grows slice multiple timesfunc processItems(items []Item) []Result { var results []Result for _, item := range items { results = append(results, process(item)) } return results} // Good: Single allocationfunc processItems(items []Item) []Result { results := make([]Result, 0, len(items)) for _, item := range items { results = append(results, process(item)) } return results}``` ### Use sync.Pool for Frequent Allocations ```govar bufferPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) },} func ProcessRequest(data []byte) []byte { buf := bufferPool.Get().(*bytes.Buffer) defer func() { buf.Reset() bufferPool.Put(buf) }() buf.Write(data) // Process... return buf.Bytes()}``` ### Avoid String Concatenation in Loops ```go// Bad: Creates many string allocationsfunc join(parts []string) string { var result string for _, p := range parts { result += p + "," } return result} // Good: Single allocation with strings.Builderfunc join(parts []string) string { var sb strings.Builder for i, p := range parts { if i > 0 { sb.WriteString(",") } sb.WriteString(p) } return sb.String()} // Best: Use standard libraryfunc join(parts []string) string { return strings.Join(parts, ",")}``` ## Go Tooling Integration ### Essential Commands ```bash# Build and rungo build ./...go run ./cmd/myapp # Testinggo test ./...go test -race ./...go test -cover ./... # Static analysisgo vet ./...staticcheck ./...golangci-lint run # Module managementgo mod tidygo mod verify # Formattinggofmt -w .goimports -w .``` ### Recommended Linter Configuration (.golangci.yml) ```yamllinters: enable: - errcheck - gosimple - govet - ineffassign - staticcheck - unused - gofmt - goimports - misspell - unconvert - unparam linters-settings: errcheck: check-type-assertions: true govet: check-shadowing: true issues: exclude-use-default: false``` ## Quick Reference: Go Idioms | Idiom | Description ||-------|-------------|| Accept interfaces, return structs | Functions accept interface params, return concrete types || Errors are values | Treat errors as first-class values, not exceptions || Don't communicate by sharing memory | Use channels for coordination between goroutines || Make the zero value useful | Types should work without explicit initialization || A little copying is better than a little dependency | Avoid unnecessary external dependencies || Clear is better than clever | Prioritize readability over cleverness || gofmt is no one's favorite but everyone's friend | Always format with gofmt/goimports || Return early | Handle errors first, keep happy path unindented | ## Anti-Patterns to Avoid ```go// Bad: Naked returns in long functionsfunc process() (result int, err error) { // ... 50 lines ... return // What is being returned?} // Bad: Using panic for control flowfunc GetUser(id string) *User { user, err := db.Find(id) if err != nil { panic(err) // Don't do this } return user} // Bad: Passing context in structtype Request struct { ctx context.Context // Context should be first param ID string} // Good: Context as first parameterfunc ProcessRequest(ctx context.Context, id string) error { // ...} // Bad: Mixing value and pointer receiverstype Counter struct{ n int }func (c Counter) Value() int { return c.n } // Value receiverfunc (c *Counter) Increment() { c.n++ } // Pointer receiver// Pick one style and be consistent``` **Remember**: Go code should be boring in the best way - predictable, consistent, and easy to understand. When in doubt, keep it simple.Related skills
Agent Eval
Install Agent Eval skill for Claude Code from affaan-m/everything-claude-code.
Agent Harness Construction
Install Agent Harness Construction skill for Claude Code from affaan-m/everything-claude-code.
Agent Payment X402
Install Agent Payment X402 skill for Claude Code from affaan-m/everything-claude-code.