Raw view: Go

No invisible, zero-width, or bidi-control characters detected.

---
description: Definitive guidelines for writing clear, simple, performant, and maintainable Go code, adhering to Google's style and modern best practices.
globs: **/*
---
# Go Best Practices

This guide outlines our team's definitive Go coding standards, emphasizing clarity, simplicity, performance, and maintainability. Adhere to these principles to ensure a consistent, high-quality codebase that aligns with Google's Go Style Guide.

## 1. Code Organization & Structure

**Prioritize clarity and discoverability.** Structure your projects logically, making it easy for new team members to understand the codebase.

### Package Naming & Purpose
Packages must have short, lowercase names that clearly describe their single purpose. Avoid generic names like `util` or `common`.

❌ BAD:
```go
// myproject/utils/string_helpers.go
package utils // Too generic, doesn't describe specific utility

func SanitizeString(s string) string { /* ... */ }
```

✅ GOOD:
```go
// myproject/text/sanitizer.go
package text // Clear domain, specific functionality

func Sanitize(s string) string { /* ... */ }
```

### `internal` Package for Encapsulation
Always use the `internal` directory for code that should not be imported by other projects or external modules. This enforces strong encapsulation and prevents unintended dependencies.

❌ BAD:
```go
// myproject/service/auth.go (Exposes internal logic to external consumers)
package service

type AuthService struct { /* ... */ }
```

✅ GOOD:
```go
// myproject/internal/auth/service.go (Only importable by modules within 'myproject')
package auth

type Service struct { /* ... */ }
```

## 2. Error Handling

**Handle errors explicitly and provide actionable context.** Never ignore errors. Use `errors.Is` and `errors.As` for robust error inspection.

### Wrap Errors with Context
Always wrap errors to add context, making debugging easier and preserving the error chain. Use `%w` with `fmt.Errorf` to enable `errors.Is` and `errors.As`.

❌ BAD:
```go
if err := db.Save(user); err != nil {
    return fmt.Errorf("failed to save user: %v", err) // Loses original error type
}
```

✅ GOOD:
```go
if err := db.Save(user); err != nil {
    return fmt.Errorf("failed to save user %s: %w", user.ID, err) // Original error preserved
}
```

### Use `errors.Is` and `errors.As` for Inspection
For checking specific error types or values, use `errors.Is` for direct comparison and `errors.As` for unwrapping to a specific error type. This works correctly with wrapped errors.

❌ BAD:
```go
if err == sql.ErrNoRows { // Fails if error is wrapped by fmt.Errorf
    return ErrNotFound
}
if _, ok := err.(*MyCustomError); ok { // Fails if error is wrapped
    // ...
}
```

✅ GOOD:
```go
if errors.Is(err, sql.ErrNoRows) { // Correctly identifies sql.ErrNoRows in chain
    return ErrNotFound
}
var customErr *MyCustomError
if errors.As(err, &customErr) { // Correctly unwraps to MyCustomError
    // ...
}
```

## 3. Performance Considerations

**Minimize allocations and GC pressure.** Go's GC is efficient, but excessive allocations will always be a bottleneck in hot paths.

### Pre-allocate Slices and Maps
Always pre-allocate slices and maps with known or estimated capacities to avoid repeated reallocations and garbage generation.

❌ BAD:
```go
var results []Result // Grows dynamically, causing multiple reallocations
for _, item := range items {
    results = append(results, process(item))
}
```

✅ GOOD:
```go
results := make([]Result, 0, len(items)) // Pre-allocate capacity
for _, item := range items {
    results = append(results, process(item))
}
```

### Use `strings.Builder` for String Concatenation
Avoid `+` or `fmt.Sprintf` in loops for building strings. `strings.Builder` minimizes allocations by writing to a single underlying buffer.

❌ BAD:
```go
var s string
for i := 0; i < 1000; i++ {
    s += strconv.Itoa(i) + "," // Creates many intermediate string allocations
}
```

✅ GOOD:
```go
var sb strings.Builder
sb.Grow(1000 * 5) // Estimate required size to avoid initial reallocations
for i := 0; i < 1000; i++ {
    sb.WriteString(strconv.Itoa(i))
    sb.WriteString(",")
}
s := sb.String()
```

### Leverage `sync.Pool` for Reusable Objects
For frequently created and discarded objects (e.g., HTTP request buffers, database connections), use `sync.Pool` to reduce GC overhead by reusing objects.

❌ BAD:
```go
func handleRequest(w http.ResponseWriter, r *http.Request) {
    buf := make([]byte, 4096) // New allocation per request
    // ... use buf ...
}
```

✅ GOOD:
```go
var bufferPool = sync.Pool{
    New: func() interface{} {
        return make([]byte, 4096) // Allocate new buffer if pool is empty
    },
}

func handleRequest(w http.ResponseWriter, r *http.Request) {
    buf := bufferPool.Get().([]byte) // Get a buffer from the pool
    defer bufferPool.Put(buf)        // Return buffer to pool when done
    // ... use buf ...
}
```

### Pass Small Structs by Value
For small, immutable structs (e.g., coordinates, simple IDs), passing by value can avoid heap allocations and improve cache locality, as determined by escape analysis.

❌ BAD:
```go
type Point struct { X, Y int }
func movePoint(p *Point, dx, dy int) { // Pointer might force heap allocation
    p.X += dx
    p.Y += dy
}
```

✅ GOOD:
```go
type Point struct { X, Y int }
func movePoint(p Point, dx, dy int) Point { // Value type, likely stack allocated
    p.X += dx
    p.Y += dy
    return p
}
```

## 4. Concurrency & Context

**Manage goroutines and resource lifetimes effectively.** Always use `context.Context` for cancellation and timeouts.

### Use `context.Context` for Cancellation and Timeouts
Pass `context.Context` as the first argument to functions that perform I/O or long-running operations. This enables graceful shutdown and resource management.

❌ BAD:
```go
func fetchData(url string) ([]byte, error) {
    // No way to cancel or set a timeout for the HTTP request
}
```

✅ GOOD:
```go
func fetchData(ctx context.Context, url string) ([]byte, error) {
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
    if err != nil {
        return nil, fmt.Errorf("failed to create request: %w", err)
    }
    resp, err := http.DefaultClient.Do(req) // Respects context cancellation/timeout
    if err != nil {
        return nil, fmt.Errorf("http request failed: %w", err)
    }
    defer resp.Body.Close()
    return io.ReadAll(resp.Body)
}
```

### Coordinate Goroutines with `errgroup.Group`
For managing multiple goroutines that need to complete or be cancelled together, use `golang.org/x/sync/errgroup`. This simplifies error propagation and context cancellation across concurrent tasks.

❌ BAD:
```go
var wg sync.WaitGroup
var mu sync.Mutex
var errors []error // Manual error collection

for _, task := range tasks {
    wg.Add(1)
    go func(t Task) {
        defer wg.Done()
        if err := t.Run(); err != nil {
            mu.Lock()
            errors = append(errors, err)
            mu.Unlock()
        }
    }(task)
}
wg.Wait()
// Manual error checking and potential resource leaks on first error
```

✅ GOOD:
```go
group, ctx := errgroup.WithContext(context.Background()) // Context for cancellation
// var results []Result // Use a channel or mutex-protected slice if results are needed

for _, task := range tasks {
    task := task // Capture loop variable for closure
    group.Go(func() error {
        select {
        case <-ctx.Done(): // Respect cancellation from other goroutines
            return ctx.Err()
        default:
            if err := task.Run(ctx); err != nil { // Pass context to task
                return fmt.Errorf("task %s failed: %w", task.ID, err)
            }
            return nil
        }
    })
}

if err := group.Wait(); err != nil { // Waits for all, returns first non-nil error
    log.Printf("One or more tasks failed: %v", err)
}
```

## 5. API Design

**Design clear, intuitive, and idiomatic APIs.** Follow Go's conventions for function signatures and return values.

### Idiomatic Function Signatures
Prefer `(value, error)` return patterns. Avoid naked returns and ensure clarity in parameter order (e.g., `context.Context` first, then inputs, then options).

❌ BAD:
```go
func GetUser(id string) (user User, err error) { // Naked return, less clear
    // ...
    return
}
```

✅ GOOD:
```go
func GetUser(ctx context.Context, id string) (User, error) { // Clear return, context first
    // ...
    return user, nil
}
```

### Accept Interfaces, Return Structs
This principle promotes flexibility for callers (they can pass any type satisfying the interface) while maintaining concrete implementation details internally.

❌ BAD:
```go
func Process(reader *bytes.Reader) error { // Too specific, limits caller flexibility
    // ...
}
```

✅ GOOD:
```go
func Process(reader io.Reader) error { // Accepts any io.Reader, highly flexible
    // ...
}
```

## 6. Testing Approaches

**Write comprehensive, fast, and maintainable tests.** Prioritize table-driven tests for clarity and coverage.

### Table-Driven Tests
Use table-driven tests for functions with multiple inputs and expected outputs. This reduces boilerplate, improves readability, and makes it easy to add new test cases.

❌ BAD:
```go
func TestAdd(t *testing.T) {
    if Add(1, 2) != 3 {
        t.Errorf("Add(1, 2) = %d; want 3", Add(1, 2))
    }
    if Add(-1, 1) != 0 {
        t.Errorf("Add(-1, 1) = %d; want 0", Add(-1, 1))
    }
}
```

✅ GOOD:
```go
func TestAdd(t *testing.T) {
    tests := []struct {
        name string
        a, b int
        want int
    }{
        {"positive numbers", 1, 2, 3},
        {"negative and positive", -1, 1, 0},
        {"zero", 0, 0, 0},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) { // Use t.Run for isolated subtests
            if got := Add(tt.a, tt.b); got != tt.want {
                t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want)
            }
        })
    }
}
```

## 7. Security Best Practices

**Integrate security from the start.** Always validate inputs and handle sensitive data carefully.

### Input Validation
Never trust user input. Validate all inputs at the API boundary to prevent injection attacks, buffer overflows, and other vulnerabilities.

❌ BAD:
```go
func createUser(username string) error {
    // Directly uses username from request without validation
    db.CreateUser(username)
}
```

✅ GOOD:
```go
func createUser(username string) error {
    if !isValidUsername(username) { // Validate length, allowed characters, format
        return errors.New("invalid username format")
    }
    db.CreateUser(username)
}
```

### Secure Dependency Management
Regularly audit and update dependencies to mitigate known vulnerabilities. Use `go mod tidy` to clean up unused modules and `govulncheck` to identify issues.

❌ BAD:
```go
// Relying on old, potentially vulnerable dependencies without checks
```

✅ GOOD:
```go
// Keep go.mod clean and dependencies updated.
// Use `go get -u ./...` and `go mod tidy`.
// Regularly run `govulncheck ./...` to identify known vulnerabilities.
```