Source profileQuality 78/100Review permissions

affaan-m/ECC/skills/golang-patterns/SKILL.md

golang-patterns

Idiomatic Go patterns, best practices, and conventions for building robust, efficient, and maintainable Go applications.

Source repository stars
234,327
Declared platforms
0
Static risk flags
3
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

Idiomatic Go patterns and best practices for building robust, efficient, and maintainable applications.

Best for

    Not for

    • Remember: Go code should be boring in the best way - predictable, consistent, and easy to understand. When in doubt, keep it simple.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "skills/golang-patterns"
    Safe inspection promptEditorial

    Inspect the Agent Skill "golang-patterns" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/skills/golang-patterns/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      When to Activate

      Writing new Go code

      Writing new Go codeReviewing Go codeRefactoring existing Go code
    2. 02

      Core Principles

      Go favors simplicity over cleverness. Code should be obvious and easy to read.

      Go favors simplicity over cleverness. Code should be obvious and easy to read.Design types so their zero value is immediately usable without initialization.Functions should accept interface parameters and return concrete types.
    3. 03

      1. Simplicity and Clarity

      Go favors simplicity over cleverness. Code should be obvious and easy to read.

      Go favors simplicity over cleverness. Code should be obvious and easy to read.
    4. 04

      2. Make the Zero Value Useful

      Design types so their zero value is immediately usable without initialization.

      Design types so their zero value is immediately usable without initialization.
    5. 05

      3. Accept Interfaces, Return Structs

      Functions should accept interface parameters and return concrete types.

      Functions should accept interface parameters and return concrete types.

    Permission review

    Static risk signals and limitations

    Reads files

    low · line 94

    The documentation asks the agent to read local files, directories, or repositories.

    data, err := os.ReadFile(path)

    Network access

    medium · line 205

    The documentation includes network, browsing, or remote request actions.

    return nil, fmt.Errorf("fetch %s: %w", url, err)

    Network access

    medium · line 269

    The documentation includes network, browsing, or remote request actions.

    data, _ := fetch(url)

    Runs scripts

    medium · line 570

    The documentation asks the agent to run terminal commands or scripts.

    go build ./...

    Runs scripts

    medium · line 571

    The documentation asks the agent to run terminal commands or scripts.

    go run ./cmd/myapp

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score78/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    skills/golang-patterns/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    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.

    // Good: Clear and direct
    func 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 clever
    func 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.

    // Good: Zero value is useful
    type 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 value
    var buf bytes.Buffer
    buf.WriteString("hello")
    
    // Bad: Requires initialization
    type BadCounter struct {
        counts map[string]int // nil map will panic
    }
    

    3. Accept Interfaces, Return Structs

    Functions should accept interface parameters and return concrete types.

    // Good: Accepts interface, returns concrete type
    func 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

    // Good: Wrap errors with context
    func 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

    // Define domain-specific errors
    type 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 cases
    var (
        ErrNotFound     = errors.New("resource not found")
        ErrUnauthorized = errors.New("unauthorized")
        ErrInvalidInput = errors.New("invalid input")
    )
    

    Error Checking with errors.Is and errors.As

    func 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

    // Bad: Ignoring error with blank identifier
    result, _ := doSomething()
    
    // Good: Handle or explicitly document why it's safe to ignore
    result, 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

    func 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

    func 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

    func 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

    import "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

    // Bad: Goroutine leak if context is cancelled
    func 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 cancellation
    func 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

    // Good: Single-method interfaces
    type 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 needed
    type ReadWriteCloser interface {
        Reader
        Writer
        Closer
    }
    

    Define Interfaces Where They're Used

    // In the consumer package, not the provider
    package service
    
    // UserStore defines what this service needs
    type 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

    type 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

    myproject/
    ├── 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

    // Good: Short, lowercase, no underscores
    package http
    package json
    package user
    
    // Bad: Verbose, mixed case, or redundant
    package httpHandler
    package json_parser
    package userService // Redundant 'Service' suffix
    

    Avoid Package-Level State

    // Bad: Global mutable state
    var db *sql.DB
    
    func init() {
        db, _ = sql.Open("postgres", os.Getenv("DATABASE_URL"))
    }
    
    // Good: Dependency injection
    type Server struct {
        db *sql.DB
    }
    
    func NewServer(db *sql.DB) *Server {
        return &Server{db: db}
    }
    

    Struct Design

    Functional Options Pattern

    type 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
    }
    
    // Usage
    server := NewServer(":8080",
        WithTimeout(60*time.Second),
        WithLogger(customLogger),
    )
    

    Embedding for Composition

    type 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,
        }
    }
    
    // Usage
    s := NewServer(":8080")
    s.Log("Starting...") // Calls embedded Logger.Log
    

    Memory and Performance

    Preallocate Slices When Size is Known

    // Bad: Grows slice multiple times
    func processItems(items []Item) []Result {
        var results []Result
        for _, item := range items {
            results = append(results, process(item))
        }
        return results
    }
    
    // Good: Single allocation
    func 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

    var 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

    // Bad: Creates many string allocations
    func join(parts []string) string {
        var result string
        for _, p := range parts {
            result += p + ","
        }
        return result
    }
    
    // Good: Single allocation with strings.Builder
    func 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 library
    func join(parts []string) string {
        return strings.Join(parts, ",")
    }
    

    Go Tooling Integration

    Essential Commands

    # Build and run
    go build ./...
    go run ./cmd/myapp
    
    # Testing
    go test ./...
    go test -race ./...
    go test -cover ./...
    
    # Static analysis
    go vet ./...
    staticcheck ./...
    golangci-lint run
    
    # Module management
    go mod tidy
    go mod verify
    
    # Formatting
    gofmt -w .
    goimports -w .
    

    Recommended Linter Configuration (.golangci.yml)

    linters:
      enable:
        - errcheck
        - gosimple
        - govet
        - ineffassign
        - staticcheck
        - unused
        - gofmt
        - goimports
        - misspell
        - unconvert
        - unparam
    
    linters-settings:
      errcheck:
        check-type-assertions: true
      govet:
        enable:
          - shadow
    
    issues:
      exclude-use-default: false
    

    Quick Reference: Go Idioms

    IdiomDescription
    Accept interfaces, return structsFunctions accept interface params, return concrete types
    Errors are valuesTreat errors as first-class values, not exceptions
    Don't communicate by sharing memoryUse channels for coordination between goroutines
    Make the zero value usefulTypes should work without explicit initialization
    A little copying is better than a little dependencyAvoid unnecessary external dependencies
    Clear is better than cleverPrioritize readability over cleverness
    gofmt is no one's favorite but everyone's friendAlways format with gofmt/goimports
    Return earlyHandle errors first, keep happy path unindented

    Anti-Patterns to Avoid

    // Bad: Naked returns in long functions
    func process() (result int, err error) {
        // ... 50 lines ...
        return // What is being returned?
    }
    
    // Bad: Using panic for control flow
    func GetUser(id string) *User {
        user, err := db.Find(id)
        if err != nil {
            panic(err) // Don't do this
        }
        return user
    }
    
    // Bad: Passing context in struct
    type Request struct {
        ctx context.Context // Context should be first param
        ID  string
    }
    
    // Good: Context as first parameter
    func ProcessRequest(ctx context.Context, id string) error {
        // ...
    }
    
    // Bad: Mixing value and pointer receivers
    type Counter struct{ n int }
    func (c Counter) Value() int { return c.n }    // Value receiver
    func (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.

    Alternatives

    Compare before choosing