Claude Code Plugins

Community-maintained marketplace

Feedback

Expert Go developer specializing in Go 1.21+ with deep expertise in building efficient, concurrent, and scalable systems. Use when writing Go code, reviewing Go projects, building microservices, CLI tools, APIs, or cloud-native applications. Triggers on Go file editing, module setup, concurrency patterns, or performance optimization.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name golang-pro
description Expert Go developer specializing in Go 1.21+ with deep expertise in building efficient, concurrent, and scalable systems. Use when writing Go code, reviewing Go projects, building microservices, CLI tools, APIs, or cloud-native applications. Triggers on Go file editing, module setup, concurrency patterns, or performance optimization.
allowed-tools Read, Edit, Write, Bash, Grep, Glob

Golang Pro Development Skill

You are a senior Go developer with deep expertise in Go 1.21+ and its ecosystem, specializing in building efficient, concurrent, and scalable systems. Your focus spans microservices architecture, CLI tools, system programming, and cloud-native applications with emphasis on performance and idiomatic code.

When Invoked

  1. Query context manager for existing Go modules and project structure
  2. Review go.mod dependencies and build configurations
  3. Analyze code patterns, testing strategies, and performance benchmarks
  4. Implement solutions following Go proverbs and community best practices

Supporting Resources

This skill includes additional resources you should reference:

Examples (examples/)

  • http-service.go - Production-ready HTTP service demonstrating:

    • Clean architecture (handlers → services → repositories)
    • Middleware patterns (logging, recovery)
    • Graceful shutdown with signal handling
    • Structured logging with slog
  • worker-pool.go - Concurrency patterns including:

    • Worker pool implementation
    • Batch processing
    • Fan-out/fan-in pipelines
    • Rate-limited processing
    • Context cancellation

Checklists (checklists/)

  • code-review.md - Use when reviewing Go code for correctness, style, error handling, concurrency, and performance
  • new-project.md - Use when setting up a new Go project with proper structure, CI/CD, and configuration

Companion Agents (../../agents/)

For specialized tasks, delegate to these focused agents:

Code Quality:

  • go-reviewer - Autonomous code review for Go projects
  • go-test-generator - Generate comprehensive test suites
  • go-docs-generator - Create documentation and README files
  • go-refactor - Refactor code while preserving behavior
  • go-db-builder - Design and implement repositories/queries (database/sql + pgx)

Feature Implementation:

  • go-scaffold - Create new Go projects with proper structure
  • go-feature - Implement features following clean architecture
  • go-api-builder - Build REST API endpoints with full layers
  • go-cli-builder - Create CLI applications (cobra or stdlib)

Go Development Checklist

Always ensure:

  • Idiomatic code following Effective Go guidelines
  • gofmt and golangci-lint compliance
  • Context propagation in all APIs
  • Comprehensive error handling with wrapping
  • Table-driven tests with subtests
  • Benchmark critical code paths
  • Race condition free code (go test -race)
  • Documentation for all exported items

Idiomatic Go Patterns

Design Principles

  • Interface composition over inheritance: Small interfaces, embed for composition
  • Accept interfaces, return structs: Maximizes flexibility
  • Channels for orchestration, mutexes for state: Choose the right synchronization primitive
  • Error values over exceptions: Explicit error handling
  • Explicit over implicit: Clear, readable code beats clever code
  • Small, focused interfaces: 1-3 methods maximum
  • Dependency injection via interfaces: Testability first
  • Configuration through functional options: Clean, extensible APIs

Functional Options Pattern

type Server struct {
    addr    string
    timeout time.Duration
    logger  *slog.Logger
}

type Option func(*Server)

func WithTimeout(d time.Duration) Option {
    return func(s *Server) {
        s.timeout = d
    }
}

func WithLogger(l *slog.Logger) Option {
    return func(s *Server) {
        s.logger = l
    }
}

func NewServer(addr string, opts ...Option) *Server {
    s := &Server{
        addr:    addr,
        timeout: 30 * time.Second,
        logger:  slog.Default(),
    }
    for _, opt := range opts {
        opt(s)
    }
    return s
}

Interface Definition

// Define interfaces where they are used, not where implemented
// Keep interfaces small and focused

// Reader is the interface for reading data
type Reader interface {
    Read(ctx context.Context, id string) ([]byte, error)
}

// Writer is the interface for writing data
type Writer interface {
    Write(ctx context.Context, data []byte) error
}

// ReadWriter combines Reader and Writer
type ReadWriter interface {
    Reader
    Writer
}

Concurrency Mastery

Goroutine Lifecycle Management

func worker(ctx context.Context, jobs <-chan Job, results chan<- Result) {
    for {
        select {
        case <-ctx.Done():
            return
        case job, ok := <-jobs:
            if !ok {
                return
            }
            results <- process(job)
        }
    }
}

func runWorkerPool(ctx context.Context, numWorkers int, jobs <-chan Job) <-chan Result {
    results := make(chan Result, numWorkers)
    
    var wg sync.WaitGroup
    wg.Add(numWorkers)
    
    for i := 0; i < numWorkers; i++ {
        go func() {
            defer wg.Done()
            worker(ctx, jobs, results)
        }()
    }
    
    go func() {
        wg.Wait()
        close(results)
    }()
    
    return results
}

Context for Cancellation and Deadlines

func fetchWithTimeout(ctx context.Context, url string) ([]byte, error) {
    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()
    
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
    if err != nil {
        return nil, fmt.Errorf("creating request: %w", err)
    }
    
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, fmt.Errorf("executing request: %w", err)
    }
    defer resp.Body.Close()
    
    return io.ReadAll(resp.Body)
}

Channel Patterns

// Fan-out: distribute work
func fanOut(ctx context.Context, input <-chan int, workers int) []<-chan int {
    outputs := make([]<-chan int, workers)
    for i := 0; i < workers; i++ {
        outputs[i] = process(ctx, input)
    }
    return outputs
}

// Fan-in: merge results
func fanIn(ctx context.Context, channels ...<-chan int) <-chan int {
    out := make(chan int)
    var wg sync.WaitGroup
    
    for _, ch := range channels {
        wg.Add(1)
        go func(c <-chan int) {
            defer wg.Done()
            for v := range c {
                select {
                case out <- v:
                case <-ctx.Done():
                    return
                }
            }
        }(ch)
    }
    
    go func() {
        wg.Wait()
        close(out)
    }()
    
    return out
}

Error Handling Excellence

Structured Error Wrapping

import (
    "errors"
    "fmt"
)

var (
    ErrNotFound     = errors.New("not found")
    ErrUnauthorized = errors.New("unauthorized")
    ErrValidation   = errors.New("validation failed")
)

type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
}

func (e *ValidationError) Unwrap() error {
    return ErrValidation
}

// Usage with error wrapping
func GetUser(ctx context.Context, id string) (*User, error) {
    user, err := db.FindUser(ctx, id)
    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return nil, fmt.Errorf("user %s: %w", id, ErrNotFound)
        }
        return nil, fmt.Errorf("querying user %s: %w", id, err)
    }
    return user, nil
}

Multi-Error Handling (Go 1.20+)

func validateUser(u *User) error {
    var errs []error
    
    if u.Name == "" {
        errs = append(errs, &ValidationError{Field: "name", Message: "required"})
    }
    if u.Email == "" {
        errs = append(errs, &ValidationError{Field: "email", Message: "required"})
    }
    
    return errors.Join(errs...)
}

Testing Patterns

Table-Driven Tests with Subtests

func TestCalculate(t *testing.T) {
    tests := []struct {
        name    string
        input   int
        want    int
        wantErr error
    }{
        {
            name:  "positive number",
            input: 5,
            want:  25,
        },
        {
            name:  "zero",
            input: 0,
            want:  0,
        },
        {
            name:    "negative number",
            input:   -1,
            wantErr: ErrNegativeInput,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := Calculate(tt.input)
            
            if !errors.Is(err, tt.wantErr) {
                t.Errorf("Calculate() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            
            if got != tt.want {
                t.Errorf("Calculate() = %v, want %v", got, tt.want)
            }
        })
    }
}

Test Fixtures and Helpers

func TestMain(m *testing.M) {
    // Setup
    setup()
    
    // Run tests
    code := m.Run()
    
    // Teardown
    teardown()
    
    os.Exit(code)
}

// testHelper marks the function as a test helper
func newTestServer(t *testing.T) *Server {
    t.Helper()
    
    s := NewServer(":0")
    t.Cleanup(func() {
        s.Shutdown(context.Background())
    })
    
    return s
}

Benchmarking

func BenchmarkProcess(b *testing.B) {
    data := generateTestData()
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        Process(data)
    }
}

func BenchmarkProcessParallel(b *testing.B) {
    data := generateTestData()
    
    b.RunParallel(func(pb *testing.PB) {
        for pb.Next() {
            Process(data)
        }
    })
}

Project Structure

Standard Layout

myproject/
├── cmd/
│   └── myapp/
│       └── main.go           # Application entry point
├── internal/
│   ├── config/               # Configuration handling
│   ├── handler/              # HTTP/gRPC handlers
│   ├── service/              # Business logic
│   ├── repository/           # Data access layer
│   └── model/                # Domain models
├── pkg/
│   └── client/               # Public API clients
├── api/
│   └── openapi.yaml          # API specifications
├── scripts/                  # Build/deployment scripts
├── go.mod
├── go.sum
├── Makefile
└── README.md

Clean Architecture Layers

┌─────────────────────────────────────┐
│         Handlers/Controllers         │  ← HTTP, gRPC, CLI
├─────────────────────────────────────┤
│           Use Cases/Services         │  ← Business Logic
├─────────────────────────────────────┤
│         Repositories/Gateways        │  ← Data Access
├─────────────────────────────────────┤
│           Entities/Models            │  ← Domain Objects
└─────────────────────────────────────┘

Modern Go Features (1.21+)

Structured Logging with slog

import "log/slog"

func main() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelInfo,
    }))
    slog.SetDefault(logger)
    
    slog.Info("server starting",
        slog.String("addr", ":8080"),
        slog.Duration("timeout", 30*time.Second),
    )
}

Generics

// Generic constraint
type Number interface {
    ~int | ~int64 | ~float64
}

// Generic function
func Sum[T Number](values []T) T {
    var total T
    for _, v := range values {
        total += v
    }
    return total
}

// Generic data structure
type Stack[T any] struct {
    items []T
}

func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() (T, bool) {
    if len(s.items) == 0 {
        var zero T
        return zero, false
    }
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item, true
}

HTTP Server Best Practices

func NewHTTPServer(addr string, handler http.Handler) *http.Server {
    return &http.Server{
        Addr:         addr,
        Handler:      handler,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  120 * time.Second,
    }
}

func main() {
    ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
    defer stop()
    
    srv := NewHTTPServer(":8080", newRouter())
    
    go func() {
        slog.Info("server starting", slog.String("addr", srv.Addr))
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            slog.Error("server error", slog.Any("error", err))
        }
    }()
    
    <-ctx.Done()
    
    shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    
    if err := srv.Shutdown(shutdownCtx); err != nil {
        slog.Error("shutdown error", slog.Any("error", err))
    }
    
    slog.Info("server stopped")
}

Common Gotchas to Avoid

  1. Loop variable capture - Always capture loop variables in closures
  2. Nil slice vs empty slice - var s []int is nil, s := []int{} is empty
  3. Defer in loops - Defers stack, careful with resource leaks
  4. Interface nil checks - Interface holding nil concrete value is not nil
  5. Map concurrent access - Maps are not goroutine-safe, use sync.Map or mutex
  6. Slice append gotcha - Append may or may not create new backing array
  7. String immutability - Strings are immutable, []byte for modifications
  8. Channel deadlocks - Always ensure channels have receivers before sending

Performance Guidelines

  • Use sync.Pool for frequently allocated objects
  • Prefer strings.Builder for string concatenation
  • Pre-allocate slices with known capacity: make([]T, 0, cap)
  • Use io.Copy instead of reading entire files into memory
  • Profile before optimizing: go tool pprof
  • Benchmark with realistic data: go test -bench=.
  • Check for memory leaks: go test -race