openclaw 网盘下载
OpenClaw

技能详情(站内镜像,无评论)

首页 > 技能库 > Go Production Engineering

Expertise in Go project architecture, error handling, concurrency safety, testing, observability, configuration, CI/CD, and documentation for production depl...

通信与消息

许可证:MIT-0

MIT-0 ·免费使用、修改和重新分发。无需归因。

版本:v1.0.0

统计:⭐ 0 · 354 · 1 current installs · 1 all-time installs

0

安装量(当前) 1

🛡 VirusTotal :良性 · OpenClaw :良性

Package:1kalin/afrexai-go-production

安全扫描(ClawHub)

  • VirusTotal :良性
  • OpenClaw :良性

OpenClaw 评估

The skill is an instruction-only Go production-engineering guide whose requirements, instructions, and assets are coherent with its stated purpose and do not request additional privileges, installs, or credentials.

目的

Name/description (Go production engineering) matches the content: SKILL.md and README provide phased guidance, code examples, checklists, and scaffolding instructions. There are no unrelated requested binaries, env vars, or config paths that would be inconsistent with the stated purpose.

说明范围

SKILL.md is a long, prescriptive text with architecture patterns, code snippets, and checklists. It does not instruct the agent to read arbitrary system files, access secrets, or call external endpoints beyond recommending libraries and CI/CD patterns. No vague 'gather whatever context you need' directives were found; the instructions stay within code review/scaffold/advice scope.

安装机制

There is no install spec and no code files that would be written or executed on install. This is the lower-risk instruction-only pattern — nothing is downloaded or extracted.

证书

The skill declares no required environment variables, credentials, or config paths. The guidance references common third-party libraries and CI practices but does not request unrelated secrets or multiple external credentials.

持久

always is false and the skill does not request persistent system presence or modifications to other skills or global agent settings. Autonomous invocation is allowed by platform default but is not combined with other red flags.

综合结论

This skill is a content-only production-engineering guide for Go and appears coherent with its description. Because it is instruction-only, installing it does not place new binaries or code on disk. Still consider: review any scaffolding or code the agent generates before committing it; do not supply credentials (GitHub, cloud keys) to the agent unless you explicitly intend to grant repository or deployment access; vet third-party library reco…

安装(复制给龙虾 AI)

将下方整段复制到龙虾中文库对话中,由龙虾按 SKILL.md 完成安装。

请把本段交给龙虾中文库(龙虾 AI)执行:为本机安装 OpenClaw 技能「Go Production Engineering」。简介:Expertise in Go project architecture, error handling, concurrency safety, testi…。
请 fetch 以下地址读取 SKILL.md 并按文档完成安装:https://raw.githubusercontent.com/openclaw/skills/refs/heads/main/skills/1kalin/afrexai-go-production/SKILL.md
(来源:yingzhi8.cn 技能库)

SKILL.md

打开原始 SKILL.md(GitHub raw)

# Go Production Engineering

You are a Go production engineering expert. Follow this system for every Go project — from architecture decisions through production deployment. Apply phases sequentially for new projects; use individual phases as needed for existing codebases.

---

## Quick Health Check (/16)

Score 0 (missing), 1 (partial), or 2 (solid) for each signal:

| Signal | What to Check |
|--------|--------------|
| Project structure | Standard layout, clean package boundaries |
| Error handling | Wrapped errors, sentinel errors, no swallowed errors |
| Concurrency safety | No goroutine leaks, proper context propagation |
| Testing | >80% coverage, table-driven tests, race detector clean |
| Observability | Structured logging, metrics, tracing |
| Configuration | 12-factor, validated at startup |
| CI/CD | Linting, testing, building in pipeline |
| Documentation | GoDoc comments, README, ADRs |

**Score interpretation:** 0-6 = 🔴 Critical gaps | 7-10 = 🟡 Needs work | 11-14 = 🟢 Solid | 15-16 = 💎 Exemplary

---

## Phase 1: Project Architecture

### Project Structure (Standard Layout)

```
project-root/
├── cmd/
│   ├── api/              # HTTP API binary
│   │   └── main.go
│   └── worker/           # Background worker binary
│       └── main.go
├── internal/             # Private packages (enforced by Go)
│   ├── domain/           # Business types & interfaces
│   │   ├── user.go
│   │   └── order.go
│   ├── service/          # Business logic
│   │   ├── user.go
│   │   └── user_test.go
│   ├── repository/       # Data access
│   │   ├── postgres/
│   │   └── redis/
│   ├── handler/          # HTTP/gRPC handlers
│   │   ├── http/
│   │   └── grpc/
│   ├── middleware/        # HTTP middleware
│   └── config/           # Configuration
├── pkg/                  # Public packages (use sparingly)
├── api/                  # OpenAPI specs, proto files
├── migrations/           # Database migrations
├── scripts/              # Build/deploy scripts
├── Makefile
├── Dockerfile
├── go.mod
├── go.sum
└── .golangci.yml
```

**7 Architecture Rules:**
1. `internal/` is your best friend — use it aggressively to prevent leaky abstractions
2. `cmd/` contains only `main.go` files — wire dependencies here, zero business logic
3. Domain types live in `internal/domain/` — no external dependencies allowed in this package
4. Interfaces are defined by the consumer, not the implementer (Go convention)
5. One package = one responsibility. If you can't name it in one word, split it
6. Avoid `pkg/` unless you genuinely intend the package to be imported by other projects
7. Circular imports are compile errors in Go — design your dependency graph as a DAG

### Dependency Injection Pattern

```go
// cmd/api/main.go — wire everything here
func main() {
    cfg := config.MustLoad()
    
    // Infrastructure
    db := postgres.MustConnect(cfg.Database)
    cache := redis.MustConnect(cfg.Redis)
    logger := logging.New(cfg.Log)
    
    // Repositories
    userRepo := postgres.NewUserRepository(db)
    orderRepo := postgres.NewOrderRepository(db)
    
    // Services
    userSvc := service.NewUserService(userRepo, cache, logger)
    orderSvc := service.NewOrderService(orderRepo, userSvc, logger)
    
    // Handlers
    router := handler.NewRouter(userSvc, orderSvc, logger)
    
    // Server
    srv := &http.Server{
        Addr:         cfg.Server.Addr,
        Handler:      router,
        ReadTimeout:  cfg.Server.ReadTimeout,
        WriteTimeout: cfg.Server.WriteTimeout,
        IdleTimeout:  cfg.Server.IdleTimeout,
    }
    
    // Graceful shutdown
    go func() {
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            logger.Fatal("server failed", "error", err)
        }
    }()
    
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    
    if err := srv.Shutdown(ctx); err != nil {
        logger.Fatal("forced shutdown", "error", err)
    }
}
```

### Framework & Library Selection

| Category | Recommended | Alternative | Avoid |
|----------|------------|-------------|-------|
| HTTP Router | chi, echo | gin, fiber | net/http alone for APIs |
| Database | pgx (Postgres), sqlc | GORM, ent | database/sql directly |
| Migrations | goose, golang-migrate | atlas | manual SQL files |
| Config | viper, envconfig | koanf | os.Getenv scattered |
| Logging | slog (stdlib), zerolog | zap | log (stdlib) |
| Testing | testify, is | gomock, mockery | custom assert helpers |
| Validation | validator/v10 | ozzo-validation | manual if-checks |
| CLI | cobra | urfave/cli | flag (stdlib) alone |
| gRPC | google.golang.org/grpc | connect-go | — |
| Observability | OTel SDK | prometheus client | custom metrics |

**Selection Rules:**
1. Prefer stdlib when it's good enough (`slog`, `net/http` for simple services, `encoding/json`)
2. `pgx` > `database/sql` for Postgres (performance, features, pgx pool)
3. `sqlc` generates type-safe code from SQL — prefer over ORMs for query-heavy apps
4. Use `chi` for REST APIs (stdlib-compatible, middleware ecosystem)
5. For gRPC, use `connect-go` if you want both gRPC and HTTP/JSON from one definition

---

## Phase 2: Error Handling

### Error Architecture

```go
// internal/domain/errors.go — sentinel errors
package domain

import "errors"

var (
    ErrNotFound      = errors.New("not found")
    ErrConflict      = errors.New("conflict")
    ErrUnauthorized  = errors.New("unauthorized")
    ErrForbidden     = errors.New("forbidden")
    ErrValidation    = errors.New("validation error")
    ErrInternal      = errors.New("internal error")
)

// Typed error with context
type ValidationError struct {
    Field   string
    Message string
}

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

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

### Error Wrapping Rules

```go
// ✅ GOOD: Wrap with context using fmt.Errorf %w
func (r *UserRepo) GetByID(ctx context.Context, id string) (*User, error) {
    user, err := r.db.QueryRow(ctx, query, id)
    if err != nil {
        if errors.Is(err, pgx.ErrNoRows) {
            return nil, fmt.Errorf("user %s: %w", id, domain.ErrNotFound)
        }
        return nil, fmt.Errorf("get user %s: %w", id, err)
    }
    return user, nil
}

// ❌ BAD: Swallowed error
if err != nil {
    log.Println(err) // logged but not returned — caller doesn't know it failed
    return nil
}

// ❌ BAD: Bare return
if err != nil {
    return err // no context — impossible to debug in production
}

// ❌ BAD: String wrapping (breaks errors.Is/As)
return fmt.Errorf("failed: %s", err) // use %w, not %s or %v
```

**8 Error Handling Rules:**
1. Always wrap errors with context: `fmt.Errorf("doing X: %w", err)`
2. Use `%w` verb — it preserves the error chain for `errors.Is()` and `errors.As()`
3. Define sentinel errors in the domain package for business-level errors
4. Handle errors at the boundary (HTTP handler) — map to status codes there
5. Never ignore errors: `_ = f.Close()` is a code smell. At minimum: `defer func() { _ = f.Close() }()`
6. Use `errors.Is()` for sentinel comparisons, `errors.As()` for typed errors
7. Don't log AND return an error — pick one (usually return; log at the top)
8. Panics are for programmer errors only (impossible states) — never for runtime errors

### HTTP Error Response Mapping

```go
func mapError(err error) (int, string) {
    switch {
    case errors.Is(err, domain.ErrNotFound):
        return http.StatusNotFound, "resource not found"
    case errors.Is(err, domain.ErrConflict):
        return http.StatusConflict, "resource already exists"
    case errors.Is(err, domain.ErrUnauthorized):
        return http.StatusUnauthorized, "authentication required"
    case errors.Is(err, domain.ErrForbidden):
        return http.StatusForbidden, "insufficient permissions"
    case errors.Is(err, domain.ErrValidation):
        var ve *domain.ValidationError
        if errors.As(err, &ve) {
            return http.StatusBadRequest, ve.Error()
        }
        return http.StatusBadRequest, "invalid request"
    default:
        return http.StatusInternalServerError, "internal server error"
    }
}
```

---

## Phase 3: Concurrency Patterns

### Context Propagation (Non-Negotiable)

```go
// Every function that does I/O takes context as first parameter
func (s *OrderService) Create(ctx context.Context, req CreateOrderRequest) (*Order, error) {
    // Check cancellation before expensive operations
    select {
    case <-ctx.Done():
        return nil, ctx.Err()
    default:
    }
    
    user, err := s.userRepo.GetByID(ctx, req.UserID)
    if err != nil {
        return nil, fmt.Errorf("get user: %w", err)
    }
    
    order, err := s.orderRepo.Create(ctx, user, req)
    if err != nil {
        return nil, fmt.Errorf("create order: %w", err)
    }
    
    // Fire-and-forget with NEW context (don't use request context)
    go func() {
        bgCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        defer cancel()
        _ = s.notifier.SendOrderConfirmation(bgCtx, order)
    }()
    
    return order, nil
}
```

### Goroutine Lifecycle Management

```go
// ✅ Worker pool with errgroup
func (w *Worker) ProcessBatch(ctx context.Context, items []Item) error {
    g, ctx := errgroup.WithContext(ctx)
    g.SetLimit(10) // Max 10 concurrent goroutines
    
    for _, item := range items {
        item := item // Go < 1.22 loop variable capture
        g.Go(func() error {
            return w.processItem(ctx, item)
        })
    }
    
    return g.Wait()
}

// ✅ Long-running goroutine with shutdown
type Processor struct {
    done chan struct{}
    wg   sync.WaitGroup
}

func (p *Processor) Start(ctx context.Context) {
    p.wg.Add(1)
    go func() {
        defer p.wg.Done()
        ticker := time.NewTicker(5 * time.Second)
        defer ticker.Stop()
        
        for {
            select {
            case <-ctx.Done():
                return
            case <-ticker.C:
                p.process(ctx)
            }
        }
    }()
}

func (p *Processor) Stop() {
    p.wg.Wait()
}
```

### Common Concurrency Pitfalls

| Pitfall | Symptom | Fix |
|---------|---------|-----|
| Goroutine leak | Memory grows forever | Always have a termination path (context, done channel) |
| Race condition | `-race` flag failures | Use `sync.Mutex`, channels, or `sync/atomic` |
| Channel deadlock | Goroutine hangs | Buffered channels or `select` with `default`/timeout |
| Shared closure variable | Wrong values in goroutine | `item := item` (Go < 1.22) or use function params |
| Missing `sync.WaitGroup` | Goroutines outlive caller | `wg.Add` before `go`, `wg.Wait` at boundary |
| Mutex copy | Silent data races | Never copy a struct containing `sync.Mutex` |
| Context leak | Resources not freed | Always `defer cancel()` after `context.WithCancel/Timeout` |

**6 Concurrency Rules:**
1. Always run tests with `-race` flag
2. `errgroup` > manual goroutine + WaitGroup for bounded work
3. Channels for communication, mutexes for state protection — pick one per use case
4. Never start a goroutine without a plan for how it stops
5. Use `context.Background()` for fire-and-forget, NEVER the request context
6. `sync.Once` for one-time initialization (DB connections, configs)

---

## Phase 4: Interface Design

### Consumer-Defined Interfaces (Go Convention)

```go
// ❌ BAD: Defining interface where implemented
// repository/user.go
type UserRepository interface { // Don't define here
    GetByID(ctx context.Context, id string) (*User, error)
    Create(ctx context.Context, user *User) error
}

// ✅ GOOD: Define interface where consumed
// service/user.go
type userRepository interface { // Private — only this package uses it
    GetByID(ctx context.Context, id string) (*domain.User, error)
    Create(ctx context.Context, user *domain.User) error
}

type UserService struct {
    repo   userRepository
    logger *slog.Logger
}

func NewUserService(repo userRepository, logger *slog.Logger) *UserService {
    return &UserService{repo: repo, logger: logger}
}
```

**Interface Rules:**
1. Accept interfaces, return structs
2. Keep interfaces small — 1-3 methods ideal
3. Name interfaces by what they do: `Reader`, `Storer`, `Notifier` — not `IUser` or `UserInterface`
4. The empty interface (`any`) means you've given up on type safety — use sparingly
5. Interfaces are satisfied implicitly — no `implements` keyword needed (duck typing)

---

## Phase 5: Testing

### Table-Driven Tests (The Go Way)

```go
func TestUserService_Create(t *testing.T) {
    tests := []struct {
        name    string
        input   CreateUserRequest
        setup   func(*mockUserRepo)
        want    *domain.User
        wantErr error
    }{
        {
            name:  "success",
            input: CreateUserRequest{Name: "Alice", Email: "alice@example.com"},
            setup: func(m *mockUserRepo) {
                m.On("Create", mock.Anything, mock.AnythingOfType("*domain.User")).Return(nil)
            },
            want: &domain.User{Name: "Alice", Email: "alice@example.com"},
        },
        {
            name:  "duplicate email",
            input: CreateUserRequest{Name: "Alice", Email: "existing@example.com"},
            setup: func(m *mockUserRepo) {
                m.On("Create", mock.Anything, mock.Anything).Return(domain.ErrConflict)
            },
            wantErr: domain.ErrConflict,
        },
        {
            name:    "empty name",
            input:   CreateUserRequest{Name: "", Email: "alice@example.com"},
            wantErr: domain.ErrValidation,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            repo := new(mockUserRepo)
            if tt.setup != nil {
                tt.setup(repo)
            }
            
            svc := NewUserService(repo, slog.Default())
            got, err := svc.Create(context.Background(), tt.input)
            
            if tt.wantErr != nil {
                assert.ErrorIs(t, err, tt.wantErr)
                return
            }
            require.NoError(t, err)
            assert.Equal(t, tt.want.Name, got.Name)
            assert.Equal(t, tt.want.Email, got.Email)
        })
    }
}
```

### Test Categories & Targets

| Category | Target | Tools | Location |
|----------|--------|-------|----------|
| Unit | >80% of service/domain | testify, mockery | `*_test.go` alongside code |
| Integration | DB queries, external APIs | testcontainers-go | `*_integration_test.go` |
| E2E/API | Full request lifecycle | httptest, testcontainers | `test/e2e/` |
| Fuzz | Input parsing, serialization | `testing.F` (stdlib) | `*_test.go` |
| Benchmark | Hot paths, serialization | `testing.B` (stdlib) | `*_test.go` |

### Integration Testing with testcontainers

```go
func TestUserRepository_Integration(t *testing.T) {
    if testing.Short() {
        t.Skip("skipping integration test")
    }
    
    ctx := context.Background()
    
    pg, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
        ContainerRequest: testcontainers.ContainerRequest{
            Image:        "postgres:16-alpine",
            ExposedPorts: []string{"5432/tcp"},
            Env: map[string]string{
                "POSTGRES_PASSWORD": "test",
                "POSTGRES_DB":       "testdb",
            },
            WaitingFor: wait.ForListeningPort("5432/tcp"),
        },
        Started: true,
    })
    require.NoError(t, err)
    defer pg.Terminate(ctx)
    
    connStr, _ := pg.ConnectionString(ctx, "sslmode=disable")
    db := pgx.MustConnect(ctx, connStr)
    runMigrations(db)
    
    repo := NewUserRepository(db)
    
    t.Run("create and get", func(t *testing.T) {
        user := &domain.User{Name: "Test", Email: "test@example.com"}
        err := repo.Create(ctx, user)
        require.NoError(t, err)
        
        got, err := repo.GetByID(ctx, user.ID)
        require.NoError(t, err)
        assert.Equal(t, user.Name, got.Name)
    })
}
```

**7 Testing Rules:**
1. `-race` flag in ALL test runs: `go test -race ./...`
2. Table-driven tests for anything with >2 cases
3. `testcontainers-go` for integration tests (real DB, real Redis)
4. Use `t.Parallel()` where safe — Go tests run sequentially by default
5. `testing.Short()` to skip slow tests: `go test -short ./...`
6. Fuzz critical parsing code: `func FuzzParseInput(f *testing.F)` 
7. Benchmark hot paths: `func BenchmarkSerialize(b *testing.B)`

---

## Phase 6: Configuration & Startup

### 12-Factor Configuration

```go
// internal/config/config.go
package config

import (
    "fmt"
    "time"
    "github.com/kelseyhightower/envconfig"
)

type Config struct {
    Server   ServerConfig
    Database DatabaseConfig
    Redis    RedisConfig
    Log      LogConfig
}

type ServerConfig struct {
    Addr         string        `envconfig:"SERVER_ADDR" default:":8080"`
    ReadTimeout  time.Duration `envconfig:"SERVER_READ_TIMEOUT" default:"5s"`
    WriteTimeout time.Duration `envconfig:"SERVER_WRITE_TIMEOUT" default:"10s"`
    IdleTimeout  time.Duration `envconfig:"SERVER_IDLE_TIMEOUT" default:"120s"`
}

type DatabaseConfig struct {
    URL             string        `envconfig:"DATABASE_URL" required:"true"`
    MaxConns        int           `envconfig:"DATABASE_MAX_CONNS" default:"25"`
    MinConns        int           `envconfig:"DATABASE_MIN_CONNS" default:"5"`
    MaxConnLifetime time.Duration `envconfig:"DATABASE_MAX_CONN_LIFETIME" default:"1h"`
}

type RedisConfig struct {
    URL          string        `envconfig:"REDIS_URL" default:"localhost:6379"`
    MaxRetries   int           `envconfig:"REDIS_MAX_RETRIES" default:"3"`
    DialTimeout  time.Duration `envconfig:"REDIS_DIAL_TIMEOUT" default:"5s"`
    ReadTimeout  time.Duration `envconfig:"REDIS_READ_TIMEOUT" default:"3s"`
    WriteTimeout time.Duration `envconfig:"REDIS_WRITE_TIMEOUT" default:"3s"`
}

type LogConfig struct {
    Level  string `envconfig:"LOG_LEVEL" default:"info"`
    Format string `envconfig:"LOG_FORMAT" default:"json"` // json | text
}

func MustLoad() *Config {
    var cfg Config
    if err := envconfig.Process("", &cfg); err != nil {
        panic(fmt.Sprintf("config: %v", err))
    }
    return &cfg
}
```

**Configuration Rules:**
1. Validate ALL config at startup — fail fast, not at 3 AM
2. Use `envconfig` or `viper` — no scattered `os.Getenv()` calls
3. Provide sensible defaults for non-secret values
4. `required:"true"` for secrets and connection strings
5. Never log secrets — redact in String() methods

---

## Phase 7: Structured Logging

### slog (Go 1.21+ stdlib)

```go
// internal/logging/logger.go
package logging

import (
    "log/slog"
    "os"
)

func New(cfg LogConfig) *slog.Logger {
    var handler slog.Handler
    
    opts := &slog.HandlerOptions{
        Level: parseLevel(cfg.Level),
    }
    
    switch cfg.Format {
    case "text":
        handler = slog.NewTextHandler(os.Stdout, opts)
    default:
        handler = slog.NewJSONHandler(os.Stdout, opts)
    }
    
    return slog.New(handler)
}

// Usage in services
func (s *OrderService) Create(ctx context.Context, req CreateOrderRequest) (*Order, error) {
    s.logger.InfoContext(ctx, "creating order",
        "user_id", req.UserID,
        "items", len(req.Items),
    )
    
    order, err := s.repo.Create(ctx, req)
    if err != nil {
        s.logger.ErrorContext(ctx, "order creation failed",
            "user_id", req.UserID,
            "error", err,
        )
        return nil, fmt.Errorf("create order: %w", err)
    }
    
    s.logger.InfoContext(ctx, "order created",
        "order_id", order.ID,
        "total", order.Total,
    )
    return order, nil
}
```

### Request ID Middleware

```go
func RequestIDMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        requestID := r.Header.Get("X-Request-ID")
        if requestID == "" {
            requestID = uuid.NewString()
        }
        
        ctx := context.WithValue(r.Context(), requestIDKey, requestID)
        w.Header().Set("X-Request-ID", requestID)
        
        // Add to logger context
        logger := slog.Default().With("request_id", requestID)
        ctx = context.WithValue(ctx, loggerKey, logger)
        
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}
```

**Log Level Guide:**
| Level | When | Example |
|-------|------|---------|
| DEBUG | Development tracing | SQL queries, cache hits/misses |
| INFO | Business events | Order created, user registered |
| WARN | Recoverable issues | Retry succeeded, deprecated API used |
| ERROR | Failed operations | DB connection lost, external API 500 |

---

## Phase 8: Database Patterns

### pgx Connection Pool

```go
func MustConnect(cfg DatabaseConfig) *pgxpool.Pool {
    poolCfg, err := pgxpool.ParseConfig(cfg.URL)
    if err != nil {
        panic(fmt.Sprintf("parse db config: %v", err))
    }
    
    poolCfg.MaxConns = int32(cfg.MaxConns)
    poolCfg.MinConns = int32(cfg.MinConns)
    poolCfg.MaxConnLifetime = cfg.MaxConnLifetime
    poolCfg.HealthCheckPeriod = 30 * time.Second
    
    pool, err := pgxpool.NewWithConfig(context.Background(), poolCfg)
    if err != nil {
        panic(fmt.Sprintf("connect db: %v", err))
    }
    
    if err := pool.Ping(context.Background()); err != nil {
        panic(fmt.Sprintf("ping db: %v", err))
    }
    
    return pool
}
```

### sqlc Pattern (Type-Safe SQL)

```sql
-- queries/user.sql
-- name: GetUser :one
SELECT id, name, email, created_at FROM users WHERE id = $1;

-- name: ListUsers :many
SELECT id, name, email, created_at FROM users
WHERE ($1::text IS NULL OR name ILIKE '%' || $1 || '%')
ORDER BY created_at DESC
LIMIT $2 OFFSET $3;

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

```yaml
# sqlc.yaml
version: "2"
sql:
  - engine: "postgresql"
    queries: "queries/"
    schema: "migrations/"
    gen:
      go:
        package: "db"
        out: "internal/repository/db"
        sql_package: "pgx/v5"
        emit_json_tags: true
        emit_empty_slices: true
```

### Transaction Pattern

```go
func (r *OrderRepo) CreateWithItems(ctx context.Context, order *Order, items []Item) error {
    tx, err := r.pool.Begin(ctx)
    if err != nil {
        return fmt.Errorf("begin tx: %w", err)
    }
    defer tx.Rollback(ctx) // No-op if committed
    
    if err := r.queries.WithTx(tx).CreateOrder(ctx, order); err != nil {
        return fmt.Errorf("create order: %w", err)
    }
    
    for _, item := range items {
        if err := r.queries.WithTx(tx).CreateOrderItem(ctx, item); err != nil {
            return fmt.Errorf("create item: %w", err)
        }
    }
    
    if err := tx.Commit(ctx); err != nil {
        return fmt.Errorf("commit: %w", err)
    }
    return nil
}
```

---

## Phase 9: HTTP API Design

### Router Setup with chi

```go
func NewRouter(userSvc *service.UserService, logger *slog.Logger) http.Handler {
    r := chi.NewRouter()
    
    // Middleware stack (order matters)
    r.Use(middleware.RequestID)
    r.Use(middleware.RealIP)
    r.Use(RequestLoggerMiddleware(logger))
    r.Use(middleware.Recoverer)
    r.Use(middleware.Timeout(30 * time.Second))
    r.Use(CORSMiddleware)
    
    // Health checks (no auth)
    r.Get("/healthz", healthCheck)
    r.Get("/readyz", readinessCheck)
    
    // API v1
    r.Route("/api/v1", func(r chi.Router) {
        r.Use(AuthMiddleware)
        
        r.Route("/users", func(r chi.Router) {
            r.Get("/", listUsers(userSvc))
            r.Post("/", createUser(userSvc))
            r.Route("/{id}", func(r chi.Router) {
                r.Get("/", getUser(userSvc))
                r.Put("/", updateUser(userSvc))
                r.Delete("/", deleteUser(userSvc))
            })
        })
    })
    
    return r
}
```

### Request/Response Pattern

```go
func createUser(svc *service.UserService) http.HandlerFunc {
    type request struct {
        Name  string `json:"name" validate:"required,min=2,max=100"`
        Email string `json:"email" validate:"required,email"`
    }
    
    type response struct {
        ID        string    `json:"id"`
        Name      string    `json:"name"`
        Email     string    `json:"email"`
        CreatedAt time.Time `json:"created_at"`
    }
    
    return func(w http.ResponseWriter, r *http.Request) {
        var req request
        if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
            respondError(w, http.StatusBadRequest, "invalid JSON")
            return
        }
        
        if err := validate.Struct(req); err != nil {
            respondError(w, http.StatusBadRequest, formatValidation(err))
            return
        }
        
        user, err := svc.Create(r.Context(), service.CreateUserRequest{
            Name:  req.Name,
            Email: req.Email,
        })
        if err != nil {
            code, msg := mapError(err)
            respondError(w, code, msg)
            return
        }
        
        respondJSON(w, http.StatusCreated, response{
            ID:        user.ID,
            Name:      user.Name,
            Email:     user.Email,
            CreatedAt: user.CreatedAt,
        })
    }
}

func respondJSON(w http.ResponseWriter, code int, data any) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(code)
    json.NewEncoder(w).Encode(data)
}

func respondError(w http.ResponseWriter, code int, message string) {
    respondJSON(w, code, map[string]string{"error": message})
}
```

### Health Check Pattern

```go
func healthCheck(w http.ResponseWriter, r *http.Request) {
    respondJSON(w, http.StatusOK, map[string]string{"status": "ok"})
}

func readinessCheck(db *pgxpool.Pool, redis *redis.Client) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
        defer cancel()
        
        checks := map[string]string{}
        healthy := true
        
        if err := db.Ping(ctx); err != nil {
            checks["database"] = "unhealthy"
            healthy = false
        } else {
            checks["database"] = "healthy"
        }
        
        if err := redis.Ping(ctx).Err(); err != nil {
            checks["redis"] = "unhealthy"
            healthy = false
        } else {
            checks["redis"] = "healthy"
        }
        
        code := http.StatusOK
        if !healthy {
            code = http.StatusServiceUnavailable
        }
        respondJSON(w, code, checks)
    }
}
```

---

## Phase 10: Observability (OpenTelemetry)

### OTel Setup

```go
func initTracer(ctx context.Context, serviceName string) (*sdktrace.TracerProvider, error) {
    exporter, err := otlptracehttp.New(ctx)
    if err != nil {
        return nil, fmt.Errorf("create exporter: %w", err)
    }
    
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithResource(resource.NewWithAttributes(
            semconv.SchemaURL,
            semconv.ServiceName(serviceName),
            semconv.ServiceVersion("1.0.0"),
        )),
        sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(0.1))),
    )
    
    otel.SetTracerProvider(tp)
    otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
        propagation.TraceContext{},
        propagation.Baggage{},
    ))
    
    return tp, nil
}
```

### Metrics with Prometheus

```go
var (
    httpRequestsTotal = promauto.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total HTTP requests",
        },
        []string{"method", "path", "status"},
    )
    
    httpRequestDuration = promauto.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "http_request_duration_seconds",
            Help:    "HTTP request duration",
            Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5},
        },
        []string{"method", "path"},
    )
)

func MetricsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
        
        next.ServeHTTP(ww, r)
        
        duration := time.Since(start).Seconds()
        path := chi.RouteContext(r.Context()).RoutePattern()
        
        httpRequestsTotal.WithLabelValues(r.Method, path, strconv.Itoa(ww.Status())).Inc()
        httpRequestDuration.WithLabelValues(r.Method, path).Observe(duration)
    })
}
```

---

## Phase 11: Production Deployment

### Multi-Stage Dockerfile

```dockerfile
# Build stage
FROM golang:1.23-alpine AS builder

RUN apk add --no-cache git ca-certificates

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 
    go build -ldflags="-w -s -X main.version=$(git describe --tags --always)" 
    -o /app/server ./cmd/api

# Runtime stage
FROM scratch

COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/server /server
COPY --from=builder /app/migrations /migrations

USER 65534:65534

EXPOSE 8080

ENTRYPOINT ["/server"]
```

### Makefile

```makefile
.PHONY: build test lint run migrate

BINARY := server
VERSION := $(shell git describe --tags --always --dirty)

build:
	CGO_ENABLED=0 go build -ldflags="-w -s -X main.version=$(VERSION)" -o bin/$(BINARY) ./cmd/api

test:
	go test -race -coverprofile=coverage.out ./...
	go tool cover -func=coverage.out

test-short:
	go test -race -short ./...

lint:
	golangci-lint run

run:
	go run ./cmd/api

migrate-up:
	goose -dir migrations postgres "$(DATABASE_URL)" up

migrate-down:
	goose -dir migrations postgres "$(DATABASE_URL)" down

migrate-create:
	goose -dir migrations create $(NAME) sql

generate:
	sqlc generate
	mockery

docker-build:
	docker build -t $(BINARY):$(VERSION) .

ci: lint test build
```

### golangci-lint Configuration

```yaml
# .golangci.yml
run:
  timeout: 5m

linters:
  enable:
    - errcheck
    - govet
    - staticcheck
    - unused
    - gosimple
    - ineffassign
    - typecheck
    - gocritic
    - gofumpt
    - revive
    - misspell
    - prealloc
    - noctx         # Finds HTTP requests without context
    - bodyclose     # Checks HTTP response body is closed
    - sqlclosecheck # Checks sql.Rows is closed
    - contextcheck  # Checks function whether use a non-inherited context
    - errname       # Checks sentinel error names follow Go convention
    - exhaustive    # Checks exhaustiveness of enum switch statements
    - gosec         # Security-oriented linting
    - nilerr        # Finds code returning nil even on error
    - unparam       # Reports unused function parameters

linters-settings:
  gocritic:
    enabled-tags:
      - diagnostic
      - style
      - performance
  revive:
    rules:
      - name: unexported-return
        disabled: true
  gosec:
    excludes:
      - G104 # Unhandled errors — covered by errcheck

issues:
  exclude-rules:
    - path: _test.go
      linters:
        - gosec
        - errcheck
```

### GitHub Actions CI

```yaml
name: CI
on:
  push:
    branches: [main]
  pull_request:

jobs:
  ci:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_PASSWORD: test
          POSTGRES_DB: testdb
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-go@v5
        with:
          go-version: '1.23'
      
      - name: Lint
        uses: golangci/golangci-lint-action@v6
        with:
          version: latest
      
      - name: Test
        run: go test -race -coverprofile=coverage.out ./...
        env:
          DATABASE_URL: postgres://postgres:test@localhost:5432/testdb?sslmode=disable
      
      - name: Coverage
        run: |
          COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}')
          echo "Coverage: $COVERAGE"
      
      - name: Build
        run: go build -o /dev/null ./...
```

---

## Phase 12: Performance Optimization

### Priority Stack

| Priority | Technique | Impact |
|----------|-----------|--------|
| 1 | Connection pooling (pgx pool, HTTP client reuse) | 10-50x |
| 2 | Avoid unnecessary allocations (sync.Pool, pre-allocated slices) | 2-5x |
| 3 | Use `strings.Builder` for string concatenation | 5-20x |
| 4 | Batch database operations | 5-50x |
| 5 | Cache hot paths (sync.Map, local cache, Redis) | 10-100x |
| 6 | Profile before optimizing (`pprof`) | — |

### Profiling

```go
import _ "net/http/pprof"

// In main.go (debug server on separate port)
go func() {
    log.Println(http.ListenAndServe(":6060", nil))
}()

// Then: go tool pprof http://localhost:6060/debug/pprof/heap
// Or:   go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
```

### Common Optimizations

```go
// ✅ Pre-allocate slices when length is known
users := make([]User, 0, len(ids))

// ✅ strings.Builder for concatenation
var b strings.Builder
b.Grow(estimatedLen)
for _, s := range parts {
    b.WriteString(s)
}
result := b.String()

// ✅ Reuse HTTP clients (never create per-request)
var httpClient = &http.Client{
    Timeout: 10 * time.Second,
    Transport: &http.Transport{
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
    },
}

// ✅ sync.Pool for frequently allocated objects
var bufPool = sync.Pool{
    New: func() any {
        return new(bytes.Buffer)
    },
}

func process() {
    buf := bufPool.Get().(*bytes.Buffer)
    defer func() {
        buf.Reset()
        bufPool.Put(buf)
    }()
    // use buf...
}
```

---

## Phase 13: Security Hardening

### Security Checklist

| Category | Check | Priority |
|----------|-------|----------|
| Input | Validate all input with `validator/v10` | P0 |
| SQL | Use parameterized queries (sqlc/pgx) — NEVER string concat | P0 |
| Auth | JWT validation with proper key rotation | P0 |
| Secrets | Environment variables only, never hardcoded | P0 |
| Dependencies | `govulncheck` in CI, `go mod tidy` regularly | P1 |
| CORS | Strict origin allowlist, not `*` | P1 |
| Rate limiting | Per-IP and per-user limits | P1 |
| Headers | Security headers middleware | P1 |
| TLS | TLS 1.2+ only, strong ciphers | P1 |
| Logging | Never log secrets, PII, or tokens | P2 |

### Security Headers Middleware

```go
func SecurityHeaders(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("X-Content-Type-Options", "nosniff")
        w.Header().Set("X-Frame-Options", "DENY")
        w.Header().Set("X-XSS-Protection", "0")
        w.Header().Set("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
        w.Header().Set("Content-Security-Policy", "default-src 'none'")
        w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
        next.ServeHTTP(w, r)
    })
}
```

### Vulnerability Scanning

```bash
# Install
go install golang.org/x/vuln/cmd/govulncheck@latest

# Scan
govulncheck ./...

# In CI — fail build on vulnerabilities
govulncheck -show verbose ./...
```

---

## Phase 14: Advanced Patterns

### Generics (Go 1.18+)

```go
// Generic result type
type Result[T any] struct {
    Data  T
    Error error
}

// Generic repository
type Repository[T any] interface {
    GetByID(ctx context.Context, id string) (*T, error)
    List(ctx context.Context, filter Filter) ([]T, error)
    Create(ctx context.Context, entity *T) error
    Update(ctx context.Context, entity *T) error
    Delete(ctx context.Context, id string) error
}

// Generic pagination
type Page[T any] struct {
    Items      []T    `json:"items"`
    NextCursor string `json:"next_cursor,omitempty"`
    HasMore    bool   `json:"has_more"`
}
```

### Functional Options Pattern

```go
type ServerOption func(*Server)

func WithAddr(addr string) ServerOption {
    return func(s *Server) { s.addr = addr }
}

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

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

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

### Graceful Degradation

```go
// Circuit breaker pattern (simplified)
type CircuitBreaker struct {
    failures   atomic.Int64
    threshold  int64
    resetAfter time.Duration
    lastFail   atomic.Int64
}

func (cb *CircuitBreaker) Execute(fn func() error) error {
    if cb.isOpen() {
        return ErrCircuitOpen
    }
    
    err := fn()
    if err != nil {
        cb.failures.Add(1)
        cb.lastFail.Store(time.Now().UnixNano())
        return err
    }
    
    cb.failures.Store(0)
    return nil
}

func (cb *CircuitBreaker) isOpen() bool {
    if cb.failures.Load() < cb.threshold {
        return false
    }
    // Allow retry after reset period
    elapsed := time.Since(time.Unix(0, cb.lastFail.Load()))
    return elapsed < cb.resetAfter
}
```

---

## 10 Go Production Commandments

1. **`internal/` is the gatekeeper** — hide implementation details aggressively
2. **Errors are values** — wrap them, check them, never ignore them
3. **`-race` flag always** — data races are silent killers
4. **Interfaces at the consumer** — small, focused, implicit
5. **Context everywhere** — first param for anything doing I/O
6. **`errgroup` for goroutines** — bounded concurrency, clean error handling
7. **`sqlc` over ORMs** — type safety from actual SQL, zero runtime reflection
8. **Profile before optimizing** — `pprof` doesn't lie, intuition does
9. **Fail at startup** — validate config, check connections, panic early
10. **Graceful shutdown** — catch signals, drain connections, close cleanly

---

## 10 Common Go Mistakes

| Mistake | Impact | Fix |
|---------|--------|-----|
| Goroutine leak | Memory exhaustion | Always have termination path |
| Missing error check | Silent failures | `errcheck` linter |
| String concatenation in loop | O(n²) allocations | `strings.Builder` |
| Copy mutex | Silent data race | Pass by pointer, embedder beware |
| Ignoring context cancellation | Wasted resources | Check `ctx.Err()` |
| `init()` abuse | Hard to test, hidden side effects | Explicit initialization |
| Interface pollution | Over-abstraction | Only abstract at consumption point |
| Missing defer for cleanup | Resource leaks | `defer` immediately after acquire |
| Nil pointer on interface | Panic at runtime | Check concrete value, not interface |
| `go func()` in loop (pre-1.22) | Wrong variable captured | `item := item` or func param |

---

## Production Readiness Checklist

### Mandatory (P0)
- [ ] `-race` clean test suite
- [ ] >80% test coverage on business logic
- [ ] Structured logging (slog/zerolog)
- [ ] Graceful shutdown with signal handling
- [ ] Health check endpoints (`/healthz`, `/readyz`)
- [ ] Configuration validation at startup
- [ ] Error wrapping with context throughout
- [ ] golangci-lint clean (strict config)
- [ ] Multi-stage Docker build (scratch/distroless)
- [ ] `govulncheck` clean

### Recommended (P1)
- [ ] OpenTelemetry tracing
- [ ] Prometheus metrics
- [ ] Request ID propagation
- [ ] Rate limiting
- [ ] Security headers
- [ ] Integration tests with testcontainers
- [ ] Database migrations (goose/migrate)
- [ ] CI/CD pipeline (lint → test → build → deploy)

---

## Quality Scoring (0-100)

| Dimension | Weight | What to Evaluate |
|-----------|--------|-----------------|
| Error handling | 15% | Wrapping, sentinels, no swallowed errors |
| Concurrency | 15% | Race-free, context propagation, goroutine lifecycle |
| Testing | 15% | Coverage, table-driven, integration, -race |
| Code organization | 15% | Package boundaries, internal/, dependency direction |
| Observability | 10% | Structured logging, metrics, tracing |
| Security | 10% | Input validation, govulncheck, secrets management |
| Performance | 10% | Profiling, pooling, pre-allocation |
| Documentation | 10% | GoDoc, README, ADRs |

**Grade:** 0-40 = 🔴 Needs rewrite | 41-60 = 🟡 Significant gaps | 61-80 = 🟢 Production ready | 81-100 = 💎 Exemplary

---

## Natural Language Commands

When asked about Go projects, interpret these naturally:
- "Review this Go code" → Run quick health check, identify anti-patterns
- "Set up a new Go service" → Generate full project structure with all phases
- "Fix the error handling" → Apply Phase 2 patterns throughout
- "Add tests" → Generate table-driven tests following Phase 5
- "Make this production ready" → Run through production readiness checklist
- "Profile this" → Guide through pprof analysis
- "Add observability" → Apply Phase 10 (OTel + Prometheus)
- "Optimize performance" → Profile first, then apply Phase 12 priority stack
- "Set up CI" → Generate GitHub Actions + golangci-lint config
- "Add database" → pgx pool + sqlc + migration setup
- "Review architecture" → Evaluate against Phase 1 rules
- "Security audit" → Run through Phase 13 checklist