# Golang Agent Skill

> Go Best Practices

- Skill: `comeonoliver/golang-agent-skill` (Agent Skill)
- Install (CLI): `npx skillmds@latest add comeonoliver/golang-agent-skill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/comeonoliver/golang-agent-skill/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: ComeOnOliver (https://skillmd.com/u/comeonoliver)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/comeonoliver/golang-agent-skill

---

# Go Best Practices

Battle-tested patterns from Google, Uber, and the Go team. These are practices proven in large-scale production systems, updated for modern Go (1.25).

## Core Principles

Readable code prioritizes these attributes in order:

1. **Clarity**: purpose and rationale are obvious to the reader
2. **Simplicity**: accomplishes the goal in the simplest way
3. **Concision**: high signal to noise ratio
4. **Maintainability**: easy to modify correctly
5. **Consistency**: matches surrounding codebase

---

## Error Handling

### Return Errors, Do Not Panic

Production code must avoid panics. Return errors and let callers decide how to handle them.

```go
// Wrong
func run(args []string) {
    if len(args) == 0 {
        panic("an argument is required")
    }
}

// Correct
func run(args []string) error {
    if len(args) == 0 {
        return errors.New("an argument is required")
    }
    return nil
}

func main() {
    if err := run(os.Args[1:]); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}
```

### Error Wrapping

Use `%w` when callers need to inspect the underlying error with `errors.Is` or `errors.As`. Use `%v` when you want to hide implementation details or at system boundaries.

```go
// Preserve error chain for programmatic inspection
if err != nil {
    return fmt.Errorf("load config: %w", err)
}

// Hide internal details at API boundaries
if err != nil {
    return fmt.Errorf("database unavailable: %v", err)
}
```

Keep context succinct. Avoid phrases like "failed to" that pile up as errors propagate.

```go
// Wrong: produces "failed to x: failed to y: failed to create store: the error"
return fmt.Errorf("failed to create new store: %w", err)

// Correct: produces "x: y: new store: the error"
return fmt.Errorf("new store: %w", err)
```

### Joining Multiple Errors (Go 1.20+)

Use `errors.Join` when multiple operations can fail independently.

```go
func validateUser(u User) error {
    var errs []error
    if u.Name == "" {
        errs = append(errs, errors.New("name required"))
    }
    if u.Email == "" {
        errs = append(errs, errors.New("email required"))
    }
    return errors.Join(errs...)
}

// Checking joined errors
if err := validateUser(u); err != nil {
    if errors.Is(err, ErrNameRequired) {
        // handles even when joined with other errors
    }
}
```

### Error Types

Choose based on caller needs:

| Caller needs to match? | Message type | Approach |
|------------------------|--------------|----------|
| No | Static | `errors.New("something bad")` |
| No | Dynamic | `fmt.Errorf("file %q not found", file)` |
| Yes | Static | Exported `var ErrNotFound = errors.New("not found")` |
| Yes | Dynamic | Custom error type with `Error()` method |

### Sentinel Errors and errors.Is

Define sentinel errors for conditions callers need to check.

```go
var (
    ErrNotFound    = errors.New("not found")
    ErrInvalidUser = errors.New("invalid user")
)

// Checking wrapped errors
if errors.Is(err, ErrNotFound) {
    // handles ErrNotFound even when wrapped
}

// Custom error types use errors.As
var pathErr *os.PathError
if errors.As(err, &pathErr) {
    fmt.Println("failed path:", pathErr.Path)
}
```

### Error Naming

Exported error variables use `Err` prefix. Custom error types use `Error` suffix.

```go
var (
    ErrNotFound    = errors.New("not found")
    ErrInvalidUser = errors.New("invalid user")
)

type NotFoundError struct {
    Resource string
}

func (e *NotFoundError) Error() string {
    return fmt.Sprintf("%s not found", e.Resource)
}
```

### Handle Errors Once

Do not log an error and also return it. The caller will likely log it again.

```go
// Wrong: logs and returns, causing duplicate logs
if err != nil {
    log.Printf("could not get user %q: %v", id, err)
    return err
}

// Correct: wrap and return, let caller decide
if err != nil {
    return fmt.Errorf("get user %q: %w", id, err)
}

// Also correct: log and degrade gracefully without returning error
if err := emitMetrics(); err != nil {
    log.Printf("could not emit metrics: %v", err)
}
```

### Error Strings

Do not capitalize error strings or end with punctuation. They often appear mid-sentence in logs.

```go
// Wrong
fmt.Errorf("Something bad happened.")

// Correct
fmt.Errorf("something bad happened")
```

### Indent Error Flow

Keep the happy path at minimal indentation. Handle errors first.

```go
// Wrong
if err != nil {
    // error handling
} else {
    // normal code
}

// Correct
if err != nil {
    return err
}
// normal code continues
```

---

## Concurrency

### Channel Size

Channels should have size zero (unbuffered) or one. Any other size requires justification about what prevents filling under load.

```go
// Wrong: arbitrary buffer
c := make(chan int, 64)

// Correct
c := make(chan int)    // unbuffered: synchronous handoff
c := make(chan int, 1) // buffered: allows one pending send
```

### Goroutine Lifetimes

Document when and how goroutines exit. Goroutines blocked on channels will not be garbage collected even if the channel is unreachable.

```go
// Document exit conditions
func (w *Worker) Run(ctx context.Context) error {
    for {
        select {
        case <-ctx.Done():
            return ctx.Err()
        case job := <-w.jobs:
            w.process(job)
        }
    }
}
```

### Use errgroup for Concurrent Operations

Prefer `errgroup.Group` over manual `sync.WaitGroup` for error-returning goroutines.

```go
import "golang.org/x/sync/errgroup"

func processItems(ctx context.Context, items []Item) error {
    g, ctx := errgroup.WithContext(ctx)
    
    for _, item := range items {
        g.Go(func() error {
            return process(ctx, item)
        })
    }
    
    return g.Wait() // returns first error, cancels others via ctx
}

// With concurrency limit
func processItemsLimited(ctx context.Context, items []Item) error {
    g, ctx := errgroup.WithContext(ctx)
    g.SetLimit(10) // max 10 concurrent goroutines
    
    for _, item := range items {
        g.Go(func() error {
            return process(ctx, item)
        })
    }
    
    return g.Wait()
}
```

### Prefer Synchronous Functions

Synchronous functions are easier to reason about and test. Let callers add concurrency when needed.

```go
// Wrong: forces concurrency on caller
func Fetch(url string) <-chan Result

// Correct: caller can wrap in goroutine if needed
func Fetch(url string) (Result, error)
```

### Zero Value Mutexes

The zero value of `sync.Mutex` is valid. Do not use pointers to mutexes or embed them in exported structs.

```go
// Wrong
mu := new(sync.Mutex)

// Wrong: exposes Lock/Unlock in API
type SMap struct {
    sync.Mutex
    data map[string]string
}

// Correct
type SMap struct {
    mu   sync.Mutex
    data map[string]string
}
```

### Atomic Operations (Go 1.19+)

Use the standard library's typed atomics. External packages are no longer necessary.

```go
import "sync/atomic"

type Counter struct {
    value atomic.Int64
}

func (c *Counter) Inc() {
    c.value.Add(1)
}

func (c *Counter) Value() int64 {
    return c.value.Load()
}

// Also available: atomic.Bool, atomic.Pointer[T], atomic.Uint32, etc.
```

### sync.Map Performance (Go 1.24+)

The `sync.Map` implementation was significantly improved in Go 1.24. Modifications of disjoint sets of keys are much less likely to contend on larger maps, and there is no longer any ramp-up time required to achieve low-contention loads.

---

## Naming

### MixedCaps Always

Go uses MixedCaps, never underscores. This applies even when it breaks other language conventions.

```go
// Wrong
MAX_LENGTH, max_length, HTTP_Server

// Correct
MaxLength, maxLength, HTTPServer
```

### Initialisms

Initialisms maintain consistent case: URL not Url, ID not Id, HTTP not Http.

```go
// Wrong
xmlHttpRequest, serverId, apiUrl

// Correct
xmlHTTPRequest, serverID, apiURL
```

### Short Variable Names

Variables should be short, especially with limited scope. The further from declaration a name is used, the more descriptive it needs to be.

```go
// Good for local scope
for i, v := range items { }
r := bufio.NewReader(f)

// Global or struct fields need more context
var DefaultTimeout = 30 * time.Second
```

### Receiver Names

Use one or two letter abbreviations of the type. Be consistent across methods. Do not use generic names like `this`, `self`, or `me`.

```go
// Wrong
func (this *Client) Get() {}
func (c *Client) Get() {}
func (cl *Client) Post() {} // inconsistent

// Correct
func (c *Client) Get() {}
func (c *Client) Post() {}
```

### Pointer vs Value Receivers

| Use pointer receiver when | Use value receiver when |
|---------------------------|-------------------------|
| Method modifies the receiver | Struct is small and immutable |
| Struct is large (avoid copying) | Method doesn't modify state |
| Consistency with other methods | Receiver is a map, func, or chan |
| Struct contains sync.Mutex | Basic types (int, string, etc.) |

```go
// Pointer: modifies state
func (s *Server) Shutdown() error {
    s.running = false
    return s.listener.Close()
}

// Value: small, read-only
func (p Point) Distance(q Point) float64 {
    return math.Hypot(p.X-q.X, p.Y-q.Y)
}
```

### Package Names

Package names are lowercase, single words. Avoid util, common, misc, api, types. The package name becomes part of the identifier at call sites.

```go
// Wrong
package chubby
type ChubbyFile struct{}  // chubby.ChubbyFile is redundant

// Correct
package chubby
type File struct{}  // chubby.File reads well
```

### Avoid Repetition in Names

Do not repeat package or receiver names in function names.

```go
// Wrong
package http
func HTTPServe() {}  // http.HTTPServe is redundant

func (c *Config) WriteConfigTo(w io.Writer) {}  // Config repeated

// Correct
package http
func Serve() {}  // http.Serve

func (c *Config) WriteTo(w io.Writer) {}
```

---

## Imports

### Grouping

Organize imports in three groups separated by blank lines: standard library, external packages, internal packages.

```go
import (
    "context"
    "fmt"
    "os"

    "github.com/google/uuid"
    "golang.org/x/sync/errgroup"

    "yourcompany/internal/config"
    "yourcompany/internal/metrics"
)
```

### Avoid Renaming

Rename imports only to avoid collisions. Prefer renaming the most local import.

### Avoid Import Dot

The dot import (`import . "pkg"`) makes code harder to read. Use only in test files with circular dependencies.

### Blank Imports

Import for side effects (`import _ "pkg"`) only in main packages or tests.

---

## Module Management

### Tool Directives (Go 1.24+)

Go modules can now track executable dependencies using tool directives in go.mod. This removes the need for the previous workaround of adding tools as blank imports to a file conventionally named "tools.go".

```go
// go.mod
module example.com/myproject

go 1.24

tool (
    golang.org/x/tools/cmd/stringer
    github.com/golangci/golangci-lint/cmd/golangci-lint
)
```

```bash
# Add a tool dependency
go get -tool golang.org/x/tools/cmd/stringer

# Run a tool
go tool stringer -type=Status

# Update all tools
go get tool

# Install all tools to GOBIN
go install tool
```

---

## Structs

### Use Field Names in Initialization

Always use field names. Positional arguments break when fields are added.

```go
// Wrong: breaks if fields change
k := User{"John", "john@example.com", true}

// Correct
k := User{
    Name:   "John",
    Email:  "john@example.com",
    Active: true,
}
```

### Omit Zero Value Fields

Do not initialize fields to their zero values.

```go
// Wrong
user := User{
    Name:   "John",
    Active: false,  // false is zero value
    Count:  0,      // 0 is zero value
}

// Correct
user := User{
    Name: "John",
}
```

### Embedding

Do not embed types in public structs. Embedding exposes methods and fields to the public API unintentionally.

```go
// Wrong: Lock and Unlock become part of SMap's API
type SMap struct {
    sync.Mutex
    data map[string]string
}

// Correct
type SMap struct {
    mu   sync.Mutex
    data map[string]string
}
```

### Use var for Zero Value Structs

```go
// Correct
var user User

// Also acceptable
user := User{}
```

---

## Slices and Maps

### Nil Slice Declaration

Prefer nil slices over empty slices. They are functionally equivalent but nil is the preferred style.

```go
// Preferred
var t []string

// Use only when JSON must encode as [] instead of null
t := []string{}
```

### Copy at Boundaries

Slices and maps hold references. Copy them when storing or returning to prevent mutation.

```go
// Wrong: caller can modify internal state
func (d *Driver) SetTrips(trips []Trip) {
    d.trips = trips
}

// Correct
func (d *Driver) SetTrips(trips []Trip) {
    d.trips = make([]Trip, len(trips))
    copy(d.trips, trips)
}

// For maps
func (d *Driver) SetMetadata(m map[string]string) {
    d.metadata = maps.Clone(m)
}
```

### Specify Capacity

Preallocate when size is known. This reduces allocations.

```go
// Wrong
var result []Item
for _, v := range input {
    result = append(result, transform(v))
}

// Correct
result := make([]Item, 0, len(input))
for _, v := range input {
    result = append(result, transform(v))
}
```

### Use slices and maps Packages

Prefer standard library functions for common operations.

```go
import (
    "cmp"
    "maps"
    "slices"
)

// Sorting
slices.Sort(numbers)
slices.SortFunc(users, func(a, b User) int {
    return cmp.Compare(a.Name, b.Name)
})

// Searching
idx, found := slices.BinarySearch(sorted, target)

// Cloning
copy := slices.Clone(original)
mapCopy := maps.Clone(original)

// Comparison
if slices.Equal(a, b) { }
if maps.Equal(m1, m2) { }
```

---

## Generics (Go 1.18+)

### When to Use Generics

Use generics when you find yourself writing the same code for different types.

```go
// Generic helper functions
func Filter[T any](slice []T, predicate func(T) bool) []T {
    result := make([]T, 0, len(slice))
    for _, v := range slice {
        if predicate(v) {
            result = append(result, v)
        }
    }
    return result
}

func Map[T, U any](slice []T, transform func(T) U) []U {
    result := make([]U, len(slice))
    for i, v := range slice {
        result[i] = transform(v)
    }
    return result
}

// Usage
adults := Filter(users, func(u User) bool { return u.Age >= 18 })
names := Map(users, func(u User) string { return u.Name })
```

### Type Constraints

Use constraints for type safety.

```go
import "cmp"

// cmp.Ordered covers all comparable types
func Max[T cmp.Ordered](a, b T) T {
    if a > b {
        return a
    }
    return b
}

// Custom constraints
type Number interface {
    ~int | ~int64 | ~float64
}

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

### Generic Type Aliases (Go 1.24+)

Type aliases can now be parameterized like defined types.

```go
// Generic type alias
type Set[T comparable] = map[T]struct{}

// Usage
var s Set[string]
s = make(Set[string])
s["hello"] = struct{}{}

// With constraints
type OrderedSlice[T cmp.Ordered] = []T
```

### Avoid Over-Generalization

Do not use generics when a concrete type or interface suffices.

```go
// Wrong: unnecessary generic
func PrintAll[T fmt.Stringer](items []T) {
    for _, item := range items {
        fmt.Println(item.String())
    }
}

// Correct: interface is sufficient
func PrintAll(items []fmt.Stringer) {
    for _, item := range items {
        fmt.Println(item.String())
    }
}
```

---

## Iterators (Go 1.23+)

### Range Over Functions

Go 1.23 introduced range-over-func, allowing custom iterators.

```go
// Iterator function signature
type Seq[V any] func(yield func(V) bool)
type Seq2[K, V any] func(yield func(K, V) bool)

// Custom iterator
func Backward[T any](s []T) func(yield func(int, T) bool) {
    return func(yield func(int, T) bool) {
        for i := len(s) - 1; i >= 0; i-- {
            if !yield(i, s[i]) {
                return
            }
        }
    }
}

// Usage
for i, v := range Backward(items) {
    fmt.Println(i, v)
}
```

### String and Bytes Iterators (Go 1.24+)

New iterator functions for efficient string and byte processing.

```go
import "strings"

// Iterate over lines (includes newline characters)
text := "line1\nline2\nline3"
for line := range strings.Lines(text) {
    fmt.Print(line)
}

// Split by delimiter (iterator, no slice allocation)
csvData := "apple,banana,cherry"
for value := range strings.SplitSeq(csvData, ",") {
    fmt.Println(value)
}

// Split after delimiter
for part := range strings.SplitAfterSeq(csvData, ",") {
    fmt.Println(part) // "apple," "banana," "cherry"
}

// Equivalent functions exist in bytes package
```

---

## Structured Logging (Go 1.21+)

### Use slog for New Code

The standard library now includes structured logging.

```go
import "log/slog"

// Basic usage
slog.Info("user created", "id", userID, "email", email)
slog.Error("request failed", "err", err, "method", r.Method)

// With context
logger := slog.With("service", "auth", "version", "1.0")
logger.Info("starting")

// JSON output for production
handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
    Level: slog.LevelInfo,
})
slog.SetDefault(slog.New(handler))
```

### slog.DiscardHandler (Go 1.24+)

Use the built-in discard handler for suppressing logs in tests.

```go
// Before Go 1.24
log := slog.New(slog.NewJSONHandler(io.Discard, nil))

// Go 1.24+
log := slog.New(slog.DiscardHandler)
```

### Structured Logging Best Practices

```go
// Use consistent key names
slog.Info("request completed",
    "method", r.Method,
    "path", r.URL.Path,
    "status", statusCode,
    "duration_ms", duration.Milliseconds(),
)

// Group related fields
slog.Info("user action",
    slog.Group("user",
        "id", user.ID,
        "role", user.Role,
    ),
    slog.Group("request",
        "method", r.Method,
        "path", r.URL.Path,
    ),
)
```

---

## Performance

### Prefer strconv Over fmt

strconv is faster for primitive conversions.

```go
// Slower
s := fmt.Sprintf("%d", n)

// Faster
s := strconv.Itoa(n)
```

### Avoid Repeated String to Byte Conversions

```go
// Wrong: converts on every iteration
for i := 0; i < n; i++ {
    w.Write([]byte("hello"))
}

// Correct
data := []byte("hello")
for i := 0; i < n; i++ {
    w.Write(data)
}
```

### Specify Map Capacity

```go
// Wrong
m := make(map[string]int)

// Correct when size is known
m := make(map[string]int, len(items))
```

### Use strings.Builder for Concatenation

```go
// Wrong: creates many allocations
var s string
for _, part := range parts {
    s += part
}

// Correct
var b strings.Builder
b.Grow(totalLen) // optional: preallocate
for _, part := range parts {
    b.WriteString(part)
}
s := b.String()
```

---

## Testing

### Table Driven Tests with Parallel Execution

Use table driven tests to avoid code duplication. Run subtests in parallel when safe.

```go
func TestSplit(t *testing.T) {
    tests := []struct {
        name  string
        input string
        sep   string
        want  []string
    }{
        {
            name:  "simple",
            input: "a/b/c",
            sep:   "/",
            want:  []string{"a", "b", "c"},
        },
        {
            name:  "empty",
            input: "",
            sep:   "/",
            want:  []string{""},
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            t.Parallel() // run subtests concurrently
            
            got := strings.Split(tt.input, tt.sep)
            if diff := cmp.Diff(tt.want, got); diff != "" {
                t.Errorf("Split() mismatch (-want +got):\n%s", diff)
            }
        })
    }
}
```

### T.Context and T.Chdir (Go 1.24+)

New helper methods for test context and working directory.

```go
func TestWithContext(t *testing.T) {
    // T.Context returns a context canceled after test completes
    // but before cleanup functions run
    ctx := t.Context()
    
    result, err := doWork(ctx)
    if err != nil {
        t.Fatal(err)
    }
    // ...
}

func TestWithChdir(t *testing.T) {
    // T.Chdir changes working directory for duration of test
    // and automatically restores it after
    t.Chdir("testdata")
    
    // Now in testdata directory
    data, err := os.ReadFile("input.txt")
    // ...
}
```

### Benchmark with b.Loop (Go 1.24+)

Use `b.Loop()` for cleaner, more accurate benchmarks.

```go
// Old way - error prone
func BenchmarkOld(b *testing.B) {
    input := setupInput() // counted in benchmark time!
    b.ResetTimer()        // easy to forget
    for i := 0; i < b.N; i++ {
        process(input)    // compiler might optimize away
    }
}

// Go 1.24+ - preferred
func BenchmarkNew(b *testing.B) {
    input := setupInput() // setup runs once, excluded from timing
    for b.Loop() {
        process(input)    // compiler cannot optimize away
    }
}
```

Benefits of `b.Loop()`:
- Setup code runs exactly once per `-count`, automatically excluded from timing
- No need to call `b.ResetTimer()`
- Function call parameters and results are kept alive, preventing compiler optimization

### Testing Concurrent Code with synctest (Go 1.25+)

The `testing/synctest` package provides deterministic testing for concurrent code using synthetic time.

```go
import "testing/synctest"

func TestTimeout(t *testing.T) {
    synctest.Test(t, func(t *testing.T) {
        ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
        defer cancel()
        
        // Inside the "bubble", time is synthetic
        // This sleep completes instantly in real time
        time.Sleep(4 * time.Second)
        
        // Context should not be expired yet
        if err := ctx.Err(); err != nil {
            t.Fatalf("unexpected timeout: %v", err)
        }
        
        // Advance past the timeout
        time.Sleep(2 * time.Second)
        
        // Now it should be expired
        if ctx.Err() != context.DeadlineExceeded {
            t.Fatal("expected deadline exceeded")
        }
    })
}
```

Key concepts:
- `synctest.Test` creates an isolated "bubble" with synthetic time
- Time only advances when all goroutines in the bubble are blocked
- Initial time is midnight UTC 2000-01-01
- `synctest.Wait()` waits for all goroutines to be durably blocked

```go
func TestConcurrentCounter(t *testing.T) {
    synctest.Test(t, func(t *testing.T) {
        var counter atomic.Int64
        var wg sync.WaitGroup
        
        // Start concurrent workers
        for i := 0; i < 10; i++ {
            wg.Add(1)
            go func() {
                defer wg.Done()
                counter.Add(1)
            }()
        }
        
        // Wait for all goroutines to complete
        wg.Wait()
        
        // Counter is now deterministically 10
        if got := counter.Load(); got != 10 {
            t.Errorf("got %d, want 10", got)
        }
    })
}

// Example with time-based operations
func TestPeriodicTask(t *testing.T) {
    synctest.Test(t, func(t *testing.T) {
        var count atomic.Int64
        ctx, cancel := context.WithCancel(t.Context())
        
        // Start a periodic task
        go func() {
            ticker := time.NewTicker(100 * time.Millisecond)
            defer ticker.Stop()
            for {
                select {
                case <-ctx.Done():
                    return
                case <-ticker.C:
                    count.Add(1)
                }
            }
        }()
        
        // Advance synthetic time by 350ms
        time.Sleep(350 * time.Millisecond)
        synctest.Wait() // wait for goroutine to process
        
        cancel()
        synctest.Wait() // wait for goroutine to exit
        
        // Should have ticked 3 times (at 100ms, 200ms, 300ms)
        if got := count.Load(); got != 3 {
            t.Errorf("got %d ticks, want 3", got)
        }
    })
}
```

**Important restrictions in synctest bubbles:**
- Do not call `t.Run()`, `t.Parallel()`, or `t.Deadline()`
- Channels created outside the bubble behave differently
- External I/O operations are not durably blocking

### Use go-cmp for Comparisons

Prefer `github.com/google/go-cmp/cmp` over `reflect.DeepEqual`.

```go
import "github.com/google/go-cmp/cmp"

// Clear diff output on failure
if diff := cmp.Diff(want, got); diff != "" {
    t.Errorf("mismatch (-want +got):\n%s", diff)
}

// With options for custom comparison
if diff := cmp.Diff(want, got, cmpopts.IgnoreUnexported(User{})); diff != "" {
    t.Errorf("mismatch (-want +got):\n%s", diff)
}
```

### Useful Test Failures

Include: what was wrong, inputs, actual result, expected result.

```go
// Wrong
if got != want {
    t.Error("wrong result")
}

// Correct
if got != want {
    t.Errorf("Foo(%q) = %d; want %d", input, got, want)
}
```

### Use t.Fatal for Setup Failures

```go
f, err := os.CreateTemp("", "test")
if err != nil {
    t.Fatal("failed to set up test")
}
```

### Interfaces Belong to Consumers

Define interfaces in the package that uses them, not the package that implements them.

```go
// Wrong: defining interface in producer
package producer
type Thinger interface { Thing() bool }
func NewThinger() Thinger { return &thinger{} }

// Correct: producer returns concrete type
package producer
type Thinger struct{}
func (t *Thinger) Thing() bool { return true }
func NewThinger() *Thinger { return &Thinger{} }

// Consumer defines interface it needs
package consumer
type Thinger interface { Thing() bool }
func Process(t Thinger) { }
```

---

## Resource Management

### runtime.AddCleanup (Go 1.24+)

Prefer `runtime.AddCleanup` over `runtime.SetFinalizer` for cleanup operations.

```go
import "runtime"

type Resource struct {
    handle uintptr
}

func NewResource() *Resource {
    r := &Resource{handle: allocHandle()}
    
    // AddCleanup is more flexible than SetFinalizer:
    // - Multiple cleanups can be attached to one object
    // - Works with interior pointers
    // - Doesn't cause leaks with cycles
    // - Doesn't delay freeing the object
    runtime.AddCleanup(r, func(handle uintptr) {
        freeHandle(handle)
    }, r.handle)
    
    return r
}
```

Key advantages over `SetFinalizer`:
- Multiple cleanups per object
- Works with interior pointers
- No cycle-related leaks
- Object freed promptly (single GC cycle)

### Weak Pointers (Go 1.24+)

The `weak` package provides weak references that don't prevent garbage collection.

```go
import "weak"

// Create a weak pointer from a strong pointer
type ExpensiveResource struct {
    data []byte
}

func NewCache() *Cache {
    return &Cache{
        items: make(map[string]weak.Pointer[ExpensiveResource]),
    }
}

type Cache struct {
    mu    sync.Mutex
    items map[string]weak.Pointer[ExpensiveResource]
}

func (c *Cache) Get(key string) *ExpensiveResource {
    c.mu.Lock()
    defer c.mu.Unlock()
    
    if wp, ok := c.items[key]; ok {
        // Value returns the original pointer, or nil if collected
        if r := wp.Value(); r != nil {
            return r
        }
        // Resource was garbage collected, remove stale entry
        delete(c.items, key)
    }
    return nil
}

func (c *Cache) Set(key string, r *ExpensiveResource) {
    c.mu.Lock()
    defer c.mu.Unlock()
    // Make creates a weak pointer from a strong pointer
    c.items[key] = weak.Make(r)
}
```

Use cases for weak pointers:
- Caches that shouldn't prevent garbage collection
- Canonicalization maps (interning)
- Observer patterns where observers may be collected

### Secure Directory Access with os.Root (Go 1.24+)

The `os.Root` type provides safe, scoped file system access that prevents path traversal attacks.

```go
import "os"

func ServeUserFiles(userDir string) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // OpenRoot opens a directory as a root for safe access
        root, err := os.OpenRoot(userDir)
        if err != nil {
            http.Error(w, "directory not found", http.StatusNotFound)
            return
        }
        defer root.Close()
        
        // Open is safe: paths are resolved relative to root
        // Attempts to escape (like "../etc/passwd") are rejected
        filename := r.URL.Query().Get("file")
        f, err := root.Open(filename)
        if err != nil {
            http.Error(w, "file not found", http.StatusNotFound)
            return
        }
        defer f.Close()
        
        io.Copy(w, f)
    }
}

// Available methods on os.Root:
// - Open(name) - open file for reading
// - Create(name) - create or truncate file
// - OpenFile(name, flag, perm) - open with flags
// - Mkdir(name, perm) - create directory
// - Remove(name) - remove file or empty directory
// - Stat(name), Lstat(name) - file info
// - ReadDir(name) - list directory contents
```

Key benefits:
- Prevents path traversal vulnerabilities ("../" attacks)
- Symlinks cannot escape the root directory
- Race-condition safe (uses openat2 on Linux)
- Drop-in replacement for typical file operations

---

## Patterns

### Functional Options

Use functional options for configurable constructors with many optional parameters.

```go
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
}

// Usage
srv := NewServer("localhost:8080",
    WithTimeout(60*time.Second),
    WithLogger(logger),
)
```

### Verify Interface Compliance

Use compile time checks to verify interface implementations.

```go
type Handler struct{}

var _ http.Handler = (*Handler)(nil)

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {}
```

### Defer for Cleanup

Use defer to clean up resources. The small overhead is worth the readability and safety.

```go
p.Lock()
defer p.Unlock()

if p.count < 10 {
    return p.count
}
p.count++
return p.count
```

### Graceful Shutdown Pattern

Production servers need graceful shutdown to drain connections.

```go
func main() {
    srv := &http.Server{Addr: ":8080", Handler: handler}

    // Start server in goroutine
    go func() {
        if err := srv.ListenAndServe(); err != http.ErrServerClosed {
            slog.Error("server error", "err", err)
        }
    }()

    // Wait for interrupt signal
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    // Graceful shutdown with timeout
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    
    if err := srv.Shutdown(ctx); err != nil {
        slog.Error("shutdown error", "err", err)
    }
    slog.Info("server stopped")
}
```

### Start Enums at One

Zero values should represent invalid or unset state.

```go
type Operation int

const (
    OperationUnknown Operation = iota // 0 = invalid
    OperationAdd                      // 1
    OperationSubtract                 // 2
)
```

### Use time Package for Time

Do not use integers for time. Use `time.Time` for instants and `time.Duration` for periods.

```go
// Wrong
func poll(delay int) {
    time.Sleep(time.Duration(delay) * time.Millisecond)
}
poll(10) // is this seconds or milliseconds?

// Correct
func poll(delay time.Duration) {
    time.Sleep(delay)
}
poll(10 * time.Second)
```

### Handle Type Assertions

Always use the two value form to avoid panics.

```go
// Wrong: panics on wrong type
t := i.(string)

// Correct
t, ok := i.(string)
if !ok {
    // handle error
}
```

### Context as First Parameter

Context should be the first parameter, named ctx. Do not store context in structs.

```go
func (s *Service) Process(ctx context.Context, req *Request) (*Response, error) {
    // ...
}
```

### Avoid Mutable Globals

Use dependency injection instead of modifying global state.

```go
// Wrong
var db *sql.DB

func init() {
    db, _ = sql.Open("postgres", os.Getenv("DSN"))
}

// Correct
type Server struct {
    db *sql.DB
}

func NewServer(db *sql.DB) *Server {
    return &Server{db: db}
}
```

### Avoid init()

Prefer explicit initialization in main. init() makes code harder to reason about and test.

### Embed Static Files (Go 1.16+)

Use `//go:embed` for static assets.

```go
import "embed"

//go:embed templates/*
var templates embed.FS

//go:embed config.json
var configData []byte
```

### Use Field Tags in Marshaled Structs

Explicit field names protect against accidental contract changes from refactoring.

```go
type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}
```

---

## Container and Runtime Considerations

### Container-Aware GOMAXPROCS (Go 1.25+)

Go 1.25 automatically adjusts GOMAXPROCS based on container CPU limits.

```go
// On Linux with cgroups, GOMAXPROCS now considers:
// - CPU bandwidth limits (CPU limit in Kubernetes)
// - Changes dynamically if limits change

// The runtime periodically updates GOMAXPROCS if:
// - Number of logical CPUs changes
// - cgroup CPU bandwidth limit changes

// Automatic behavior is disabled if you set GOMAXPROCS explicitly:
// - Via GOMAXPROCS environment variable
// - Via runtime.GOMAXPROCS() call
```

This means Go programs in containers should now perform better out-of-the-box without manual GOMAXPROCS tuning.

---

## Common Gotchas

### Loop Variable Capture (Fixed in Go 1.22+)

Prior to Go 1.22, loop variables were reused. This is no longer an issue.

```go
// Pre-Go 1.22: All goroutines see last value
for _, v := range values {
    go func() {
        process(v) // Wrong: captures loop variable
    }()
}

// Fix for pre-Go 1.22
for _, v := range values {
    v := v // shadow the loop variable
    go func() {
        process(v)
    }()
}

// Go 1.22+: Loop variables are per-iteration (no fix needed)
for _, v := range values {
    go func() {
        process(v) // Safe: v is unique per iteration
    }()
}
```

### Defer Argument Evaluation

Defer evaluates arguments immediately, not when deferred function runs.

```go
// Wrong: always prints 0
for i := 0; i < 5; i++ {
    defer fmt.Println(i) // i evaluated when defer is called
}
// Prints: 4 3 2 1 0

// Gotcha with file handles
for _, f := range files {
    defer f.Close() // All defer the same f!
}

// Fix: capture in closure
for _, f := range files {
    f := f
    defer f.Close()
}
```

### Nil Interface vs Nil Pointer

An interface containing a nil pointer is not nil.

```go
type MyError struct{}
func (e *MyError) Error() string { return "error" }

func returnsError() error {
    var e *MyError = nil
    return e // Returns non-nil interface containing nil pointer!
}

if err := returnsError(); err != nil {
    fmt.Println("error is not nil!") // This prints
}

// Fix: return nil explicitly
func returnsError() error {
    var e *MyError = nil
    if e == nil {
        return nil
    }
    return e
}
```

### Use Result Before Checking Error (Go 1.25 Fix)

Go 1.25 fixed a compiler bug where using a result before checking for error sometimes didn't panic. Your code should always check errors first.

```go
// Wrong: uses f before checking err
f, err := os.Open("file.txt")
fmt.Println(f.Name()) // May panic if f is nil
if err != nil {
    return err
}

// Correct: always check error first
f, err := os.Open("file.txt")
if err != nil {
    return err
}
fmt.Println(f.Name()) // Safe: err was nil, so f is valid
```

In Go 1.21-1.24, a compiler bug sometimes suppressed the panic. Go 1.25 correctly panics, so ensure your code follows the proper pattern.

### Map Iteration Order

Map iteration order is randomized. Do not depend on it.

```go
// Wrong: results vary between runs
for k, v := range m {
    results = append(results, v)
}

// Correct: sort keys first if order matters
keys := slices.Sorted(maps.Keys(m))
for _, k := range keys {
    results = append(results, m[k])
}
```

### Slice Append Gotcha

Append may or may not allocate new backing array.

```go
a := []int{1, 2, 3}
b := a[:2]
b = append(b, 4)
// a is now [1, 2, 4]! They share backing array

// Fix: use full slice expression to limit capacity
b := a[:2:2] // len=2, cap=2
b = append(b, 4) // forces new allocation
// a is still [1, 2, 3]
```

---

## Experimental Features

### encoding/json/v2 (Go 1.25, Experimental)

A new JSON engine is available with improved performance and streaming support.

```go
// Enable with: GOEXPERIMENT=jsonv2

import (
    "encoding/json/jsontext"
    "encoding/json/v2"
)

// The v2 API offers:
// - Better performance
// - Streaming-friendly jsontext package  
// - Custom marshalers/unmarshalers per call
// - Existing encoding/json can use v2 engine internally
```

This is experimental and subject to change.

---

## Documentation

### Comment Sentences

Comments documenting declarations should be full sentences starting with the name being described.

```go
// Request represents a request to run a command.
type Request struct{}

// Encode writes the JSON encoding of req to w.
func Encode(w io.Writer, req *Request) error {}
```

### Package Comments

Package comments appear before the package declaration with no blank line.

```go
// Package math provides basic constants and mathematical functions.
package math
```

---

## References

1. [Google Go Style Guide](https://google.github.io/styleguide/go/)
2. [Uber Go Style Guide](https://github.com/uber-go/guide)
3. [Effective Go](https://go.dev/doc/effective_go)
4. [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments)
5. [Go 1.23 Release Notes](https://go.dev/doc/go1.23)
6. [Go 1.24 Release Notes](https://go.dev/doc/go1.24)
7. [Go 1.25 Release Notes](https://go.dev/doc/go1.25)

