# Go

Idiomatic Go patterns, concurrency, and modern tooling for maintainable Go systems.

---

## Go Philosophy

> "A little copying is better than a little dependency." — Rob Pike
> "Clear is better than clever."

### Core Principles
- **Simplicity** — Readable code > clever abstractions
- **Explicit errors** — Return errors as values; handle at call site
- **Composition** — Embed interfaces; prefer small interfaces
- **Concurrency** — Goroutines + channels; don't communicate by sharing memory
- **Standard library first** — Avoid frameworks until needed

---

## Modern Go (1.22+)

### Error Handling (1.13+)
```go
// Wrap with context
if err != nil {
    return fmt.Errorf("find user %s: %w", id, err)
}

// Check with errors.Is / errors.As
if errors.Is(err, sql.ErrNoRows) {
    return ErrNotFound
}
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) && pgErr.Code == "23505" {
    return ErrDuplicateKey
}

// Custom error types
type ErrNotFound struct{ ID string }
func (e ErrNotFound) Error() string { return "not found: " + e.ID }
```

### Generics (1.18+)
```go
// Generic Result type
type Result[T any] struct {
    Value T
    Err   error
}

func Ok[T any](v T) Result[T] { return Result[T]{Value: v} }
func Err[T any](err error) Result[T] { return Result[T]{Err: err} }

func (r Result[T]) Unwrap() (T, error) { return r.Value, r.Err }

// Generic repository interface
type Repository[T any, ID comparable] interface {
    FindByID(ctx context.Context, id ID) (T, error)
    Save(ctx context.Context, entity T) error
}
```

### Structured Logging (slog, 1.21+)
```go
import "log/slog"

logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
    Level: slog.LevelInfo,
    AddSource: true,
}))
slog.SetDefault(logger)

slog.Info("user created", "user_id", id, "email", email)
slog.Error("failed to save", "err", err, "user_id", id)
```

---

## Project Structure (Clean Architecture)

```
.
├── cmd/
│   └── api/
│       └── main.go           # Composition root
├── internal/
│   ├── domain/
│   │   ├── entity/
│   │   ├── valueobject/
│   │   ├── service/
│   │   ├── event/
│   │   └── repository/       # Interfaces
│   ├── application/
│   │   ├── command/
│   │   ├── query/
│   │   └── dto/
│   ├── infrastructure/
│   │   ├── persistence/      # SQL, Redis implementations
│   │   ├── http/             # Chi, Gin, Echo routers
│   │   ├── messaging/        # Kafka, NATS, RabbitMQ
│   │   └── config/           # Viper, envconfig
│   └── platform/             # Cross-cutting: logging, metrics, tracing
├── pkg/                      # Public reusable packages
├── go.mod
├── go.sum
└── Makefile
```

---

## Tooling Stack (2024+)

| Category | Recommended | Alternatives |
|---|---|---|
| **Dependency Mgmt** | Go modules (`go mod`) | — |
| **Build** | `go build` / `go generate` | Mage, Task |
| **Linting** | **golangci-lint** (aggregator) | staticcheck, govet |
| **Formatting** | `gofmt` / `gofumpt` | — |
| **Testing** | `go test` + **testify** / **ginkgo** | stretchr/testify |
| **Mock Generation** | **mockery** / **gomock** | — |
| **API Generation** | **oapi-codegen** (OpenAPI) | **buf** (Protobuf/gRPC) |
| **Database** | **sqlc** (type-safe SQL) | GORM, ent, bun |
| **Migrations** | **golang-migrate** / **atlas** | sqlc + goose |
| **Observability** | **OpenTelemetry Go** | Prometheus client |
| **Vulnerability Scan** | **govulncheck** | Trivy, Snyk |

### golangci-lint Config (`.golangci.yml`)
```yaml
linters:
  enable:
    - errcheck
    - gosec
    - govet
    - ineffassign
    - staticcheck
    - unused
    - misspell
    - nakedret
    - prealloc
    - bodyclose
    - noctx
    - rowserrcheck
    - sqlclosecheck
    - errorlint
linters-settings:
  errcheck:
    check-type-assertions: true
    check-blank: true
  gosec:
    excludes:
      - G104  # Errors unhandled (we handle explicitly)
issues:
  exclude-rules:
    - path: _test\.go
      linters: [errcheck, gosec]
```

---

## Database Access

### sqlc (Type-Safe SQL)
```sql
-- sqlc: UserRepository
-- name: GetUser :one
SELECT id, email, created_at FROM users WHERE id = $1;

-- name: CreateUser :one
INSERT INTO users (id, email, created_at) VALUES ($1, $2, $3)
RETURNING id, email, created_at;
```

```go
// Generated code — type-safe!
func (q *Queries) GetUser(ctx context.Context, id string) (User, error) {
    row := q.db.QueryRowContext(ctx, getUser, id)
    var u User
    err := row.Scan(&u.ID, &u.Email, &u.CreatedAt)
    return u, err
}
```

### Repository Pattern
```go
// domain/repository/user.go
type UserRepository interface {
    FindByID(ctx context.Context, id UserID) (User, error)
    Save(ctx context.Context, u User) error
}

// infrastructure/persistence/postgres_user_repo.go
type PostgresUserRepo struct { q *Queries }

func (r *PostgresUserRepo) FindByID(ctx context.Context, id UserID) (User, error) {
    u, err := r.q.GetUser(ctx, id.String())
    return toDomain(u), err
}
```

---

## Concurrency Patterns

### Worker Pool
```go
func ProcessItems(ctx context.Context, items []Item, workers int) error {
    type job struct { item Item }
    type result struct { err error }

    jobs := make(chan job, len(items))
    results := make(chan result, len(items))

    // Start workers
    var wg sync.WaitGroup
    for i := 0; i < workers; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for j := range jobs {
                results <- result{err: processItem(ctx, j.item)}
            }
        }()
    }

    // Send jobs
    go func() {
        for _, item := range items { jobs <- job{item: item} }
        close(jobs)
    }()

    // Wait and collect
    go func() { wg.Wait(); close(results) }()

    for r := range results {
        if r.err != nil { return r.err } // or collect all
    }
    return nil
}
```

### ErrGroup (Structured Concurrency)
```go
import "golang.org/x/sync/errgroup"

func ProcessAll(ctx context.Context, items []Item) error {
    g, ctx := errgroup.WithContext(ctx)
    g.SetLimit(10) // concurrency limit

    for _, item := range items {
        item := item // capture loop var
        g.Go(func() error { return processItem(ctx, item) })
    }
    return g.Wait()
}
```

### Context Propagation
```go
// Always pass context as first parameter
func (s *UserService) GetUser(ctx context.Context, id UserID) (User, error) {
    // Timeout at call site
    ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
    defer cancel()
    return s.repo.FindByID(ctx, id)
}
```

### Channels for Communication
```go
// Pipeline pattern
func generate(ctx context.Context) <-chan Item {
    out := make(chan Item)
    go func() {
        defer close(out)
        for i := 0; ; i++ {
            select {
            case <-ctx.Done(): return
            case out <- Item{ID: i}:
            }
        }
    }()
    return out
}

func process(ctx context.Context, in <-chan Item) <-chan Result {
    out := make(chan Result)
    go func() {
        defer close(out)
        for item := range in {
            select {
            case <-ctx.Done(): return
            case out <- doWork(item):
            }
        }
    }()
    return out
}
```

---

## HTTP/API Layer

### Chi Router (Lightweight, Composable)
```go
r := chi.NewRouter()
r.Use(middleware.RequestID, middleware.RealIP, middleware.Recoverer)
r.Use(middleware.Timeout(30 * time.Second))

r.Route("/api/v1", func(r chi.Router) {
    r.Get("/users/{id}", getUserHandler)
    r.Post("/users", createUserHandler)
})

// Handler with dependency injection
func getUserHandler(w http.ResponseWriter, r *http.Request) {
    id := chi.URLParam(r, "id")
    user, err := app.UserService.GetUser(r.Context(), UserID(id))
    if err != nil {
        renderError(w, err)
        return
    }
    render.JSON(w, r, user)
}
```

### gRPC + Protobuf (buf)
```protobuf
// proto/user/v1/user.proto
service UserService {
  rpc GetUser(GetUserRequest) returns (User);
  rpc CreateUser(CreateUserRequest) returns (User);
}
```
```bash
buf generate  # generates Go + gRPC + HTTP gateway code
```

---

## Testing

### Unit Tests (table-driven)
```go
func TestUserService_GetUser(t *testing.T) {
    tests := []struct{
        name    string
        setup   func() (UserRepository, func())
        id      UserID
        want    User
        wantErr error
    }{
        {"found", func() (UserRepository, func()) {
            repo := &mockRepo{user: User{ID: "1", Email: "a@b.com"}}
            return repo, func() {}
        }, UserID("1"), User{ID: "1", Email: "a@b.com"}, nil},
        {"not found", func() (UserRepository, func()) {
            return &mockRepo{err: ErrNotFound}, func() {}
        }, UserID("999"), User{}, ErrNotFound},
    }

    for _, tc := range tests {
        t.Run(tc.name, func(t *testing.T) {
            repo, cleanup := tc.setup()
            defer cleanup()
            svc := NewUserService(repo)
            got, err := svc.GetUser(context.Background(), tc.id)
            if diff := cmp.Diff(tc.wantErr, err); diff != "" {
                t.Errorf("error mismatch (-want +got):\n%s", diff)
            }
            if diff := cmp.Diff(tc.want, got); diff != "" {
                t.Errorf("user mismatch (-want +got):\n%s", diff)
            }
        })
    }
}
```

### Integration Tests (testcontainers)
```go
func TestPostgresUserRepo_Integration(t *testing.T) {
    ctx := context.Background()
    pgContainer, _ := postgres.RunContainer(ctx, postgres.WithDatabase("test"))
    defer pgContainer.Terminate(ctx)

    connStr, _ := pgContainer.ConnectionString(ctx)
    db, _ := sql.Open("pgx", connStr)
    defer db.Close()

    repo := NewPostgresUserRepo(db)
    // ... test with real DB
}
```

### Contract Tests (Pact Go)
```go
// Consumer test
pact.AddInteraction().
    Given("user exists").
    UponReceiving("request for user").
    WithRequest("GET", "/users/123", nil).
    WillRespondWith(200, map[string]interface{}{
        "id": "123", "email": "test@example.com",
    })
```

---

## Observability

### OpenTelemetry (Standard)
```go
import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/sdk/trace"
)

func initTracer(ctx context.Context) (*trace.TracerProvider, error) {
    exporter, _ := otlptracegrpc.New(ctx)
    tp := trace.NewTracerProvider(trace.WithBatcher(exporter))
    otel.SetTracerProvider(tp)
    return tp, nil
}

// In handler
func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {
    ctx, span := otel.Tracer("api").Start(r.Context(), "GetUser")
    defer span.End()
    // ...
}
```

### Metrics (Prometheus)
```go
var (
    httpRequests = promauto.NewCounterVec(prometheus.CounterOpts{
        Name: "http_requests_total",
    }, []string{"method", "path", "status"})
)

func metricsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        rw := &responseWriter{ResponseWriter: w, statusCode: 200}
        next.ServeHTTP(rw, r)
        httpRequests.WithLabelValues(r.Method, r.URL.Path, strconv.Itoa(rw.statusCode)).Inc()
    })
}
```

---

## Performance

- **Escape analysis** — Avoid heap allocation; use value receivers, avoid interface{} in hot paths
- **pprof** — `go tool pprof` for CPU, memory, block, mutex profiles
- **Benchmark** — `go test -bench=. -benchmem`
- **Inlining** — `//go:inline` (1.22+); small functions auto-inlined
- **GC tuning** — `GOGC`, `GOMEMLIMIT` (1.19+); ballast for latency-sensitive services

---

## Security

- **govulncheck** in CI — `go install golang.org/x/vuln/cmd/govulncheck@latest`
- **Gosec** — Static analysis for security issues
- **Dependencies** — `go mod verify`; `go mod audit` (third-party)
- **Secrets** — Never in code; env vars + secret manager
- **Input validation** — Validate at boundaries; use `validator` lib

---

## Related Notes

- [[Languages/Language-Agnostic-Principles]] — Universal patterns
- [[Languages/TypeScript-JavaScript]] — JS/TS patterns
- [[Languages/Python]] — Python patterns
- [[Languages/Rust]] — Rust patterns
- [[Patterns/Concurrency-Patterns]] — Concurrency deep dive
- [[Patterns/Resilience-Patterns]] — Circuit breaker, retry in Go
- [[DevOps/CI-CD-Best-Practices]] — Pipeline for Go
- [[Architecture/Architectural-Styles]] — Hexagonal in Go

---

*Sources: Effective Go, Go Blog, Dave Cheney (Practical Go), Go Time Podcast, Uber Go Style Guide, Google Go Style, sqlc, golangci-lint, OpenTelemetry Go, testify, ginkgo, mockery, oapi-codegen, buf, atlas, govulncheck*