---
agent: go-engineer
allowed-tools:
- Read
- Bash
- Grep
- Glob
context: fork
description: Idiomatic Go 1.25+ development. Use when writing Go code, designing APIs,
  discussing Go patterns, or reviewing Go implementations. Emphasizes stdlib, concrete
  types, simple error handling, and minimal dependencies. NOT for Python, TypeScript,
  or shell scripting tasks.
name: writing-go
user-invocable: false
---

# Go Development (1.25+)

## Critical Output Rules

- State stdlib-first choices explicitly: use `net/http`, `encoding/json`, `context`, and `testing` where practical before adding dependencies.
- Avoid unnecessary dependencies. Add a library only when concrete requirements beat stdlib simplicity.
- Prefer concrete types and small consumer-side interfaces only at real seams; do not abstract everything by default.
- Error handling guidance must say normal failures return `error`, not `panic`; wrap with context using `%w`; avoid custom error hierarchies unless callers need distinct behavior.
- Always mention behavior tests for success and error paths, even when only giving design or error-handling advice. Prefer table-driven tests with `t.Run`; stdlib `testing` is enough unless the project already uses another test library.
- For handlers/services, pass `context.Context` through API boundaries and map service errors to HTTP status at the edge.
- Do not run destructive shell commands. For broad or risky changes, state the risk and ask before acting.

## Core Philosophy

### Stdlib and Mature Libraries First

- Prefer Go stdlib solutions; add a library only when concrete requirements beat stdlib simplicity
- Choose mature, well-maintained libs when needed

### Concrete Types Over `any`

- Never use `interface{}` or `any` when a concrete type works
- Generics for reusable utilities, concrete types for business logic
- Accept interfaces, return structs

### Private Interfaces at Consumer

- Define interfaces private (lowercase) where used
- Decouples code, enables testing
- Implementation returns concrete types

### Flat Control Flow

- Early returns, guard clauses
- No nested IFs—max 2 levels
- Switch for multi-case logic

### Explicit Error Handling

- Always wrap with context
- Use `errors.Is()`/`errors.As()`
- No bare `return err`

## Quick Patterns

### Private Interface at Consumer

```go
// service/user.go - private interface where it's USED
type userStore interface {
    Get(ctx context.Context, id string) (*User, error)
}

type Service struct {
    store userStore  // accepts interface
}

// repo/postgres.go - returns concrete type
func NewPostgresStore(db *sql.DB) *PostgresStore {
    return &PostgresStore{db: db}
}
```

### Flat Control Flow (No Nesting)

```go
// GOOD: guard clauses, early returns
func process(user *User) error {
    if user == nil {
        return ErrNilUser
    }
    if user.Email == "" {
        return ErrMissingEmail
    }
    if !isValidEmail(user.Email) {
        return ErrInvalidEmail
    }
    return doWork(user)
}

// BAD: nested conditions
func process(user *User) error {
    if user != nil {
        if user.Email != "" {
            if isValidEmail(user.Email) {
                return doWork(user)
            }
        }
    }
    return nil
}
```

### Error Handling

```go
if err := doThing(); err != nil {
    return fmt.Errorf("do thing: %w", err)  // always wrap
}

// Sentinel errors
if errors.Is(err, ErrNotFound) {
    return http.StatusNotFound
}
```

### Concrete Types (Avoid `any`)

```go
// GOOD: concrete types
func ProcessUsers(users []User) error { ... }
func GetUserByID(id string) (*User, error) { ... }

// BAD: unnecessary any
func ProcessItems(items []any) error { ... }
func GetByID(id any) (any, error) { ... }
```

### Table-Driven Tests

```go
tests := []struct {
    name    string
    input   string
    want    string
    wantErr bool
}{
    {"valid", "hello", "HELLO", false},
    {"empty", "", "", true},
}
for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        got, err := Process(tt.input)
        if tt.wantErr {
            require.Error(t, err)
            return
        }
        require.NoError(t, err)
        assert.Equal(t, tt.want, got)
    })
}
```

## Go 1.25 Features

- **testing/synctest**: Deterministic concurrent testing
- **encoding/json/v2**: 3-10x faster (GOEXPERIMENT=jsonv2)
- **runtime/trace.FlightRecorder**: Production trace capture
- **Container-aware GOMAXPROCS**: Auto-detects cgroup limits

## References

- [PATTERNS.md](references/PATTERNS.md) - Detailed code patterns
- [TESTING.md](references/TESTING.md) - Testing with testify/mockery
- [CLI.md](references/CLI.md) - CLI application patterns

## Tooling

```bash
go build ./...           # Build
go test -race ./...      # Test with race detector
golangci-lint run        # Lint
mockery --all            # Generate mocks
```

## Verify Generated Code

After generating code, always verify it compiles, tests pass, and lint runs when configured:

```bash
go test ./...
go test -race ./...
go vet ./...
golangci-lint run ./...
```

Use the project's configured commands if different.

## Failure Cases

- **No Go files in repo / ambiguous project root**: run `find . -name 'go.mod'` to locate modules before generating code; do not assume a single root.
- **Compilation or test failure after generation**: quote the failing line, state the cause, show the exact fix. Do not retry blindly—diagnose first.
