← Plugin catalog
Developer Tools

Gophers

Murat Mirgün Ercan v0.1.0

Production-grade Go skills

Create a free account to save this plugin.

From the developer

Declared capabilities

  • Read
  • Write

Not specified in the available declarations: limitations, pricing & account requirements.

Onboarding example

Review this Go change for bugs, race conditions, and idiomatic design.
Sources & dates

Developer declarations; not independently tested. Listing last checked Nov 14, 2026. Package observed Nov 14, 2026.

View package source →

Description

From the marketplace listing

Get focused guidance for writing, reviewing, testing, and improving Go code. Use 26 skills covering APIs, concurrency, databases, error handling, observability, performance, documentation, and idiomatic design.

Language: English · Automatically detected from descriptions.

Matches for “writing code”

Exact text from the indicated source. A mention alone does not establish support for your task.

Publisher full description

Get focused guidance for writing, reviewing, testing, and improving Go code. Use 26 skills covering APIs, concurrency, databases, error handling, observability, performance, documentation, and idiomatic design.

Publisher keywords · listing

go golang skills code-review testing concurrency observability

What changed

Keywords

Files & skills

File archives

Plugin package142 files · 189 KBBrowse files →
Skill instructions
go-clean-architecture9.45 KB

View saved version →

---
name: go-clean-architecture
description: "Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase, Repository, Delivery layers, inward dependency rule, 'framework/database is a detail'. Apply when untangling a monolith or checking whether business logic is testable without HTTP or DB."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Requires Go 1.21+. Framework-agnostic: works with Gin, Echo, Fiber, Chi, or net/http; swap freely."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Clean Architecture

A Go service organized into four concentric layers — Domain, Usecase, Repository, Delivery — where source code depends *inward only*. Done well, the HTTP framework and the database are interchangeable details; the business logic is testable without either.

This skill is framework-agnostic. Swap Gin for Fiber, Echo, Chi, or `net/http` by replacing the delivery layer — zero changes elsewhere.

## Core Rules

1. **Dependency Rule.** Source depends inward: Delivery → Usecase → Domain. Repository implements interfaces declared in Domain. Domain depends on nothing.
2. **Framework is a detail.** Gin/Fiber/Echo/Chi/net-http types live only in `internal/delivery/`. Usecases see plain Go values.
3. **Database is a detail.** SQL, sqlx, sqlc, pgx, GORM live only in `internal/repository/`. Usecases see repository interfaces.
4. **Domain owns the interfaces; layers below provide implementations.** `UserRepository` is an interface in `internal/domain`; the Postgres struct is in `internal/repository` and unexported.
5. **DTOs at the edges.** Delivery layer maps HTTP request bodies to domain inputs and domain entities to response bodies. Usecases never see `*gin.Context`, `http.Request`, or DB rows.
6. **`cmd/<binary>/main.go` is the only place that knows the whole system.** Wiring (DI) is explicit, framework-free Go code.

## When This Pays Off

| Symptom | What clean architecture buys you |
|---|---|
| HTTP handlers contain SQL | Move SQL into a repository; handlers shrink to 5 lines |
| Tests need a running DB | Mock the repository interface; usecase tests run in milliseconds |
| Swapping web frameworks is a rewrite | Replace `internal/delivery/http`; nothing else touched |
| Business rules duplicated across handlers | Single usecase function, called by HTTP, gRPC, and a CLI |
| ORM hooks fire in surprising places | Repository methods are explicit; no hidden behavior |

If the service is a 200-line cron job, this skill is overkill. If it will live 3+ years and grow features, it's the cheapest insurance you can buy.

## Project Structure

```
myapp/
  cmd/
    api/main.go             # entry point: config → DI → start server
    worker/main.go          # different entry, same Domain & Usecase
  internal/
    domain/                 # entities, value objects, repository INTERFACES, domain errors
      user.go
      order.go
      errors.go
    usecase/                # business logic; depends only on domain
      user_usecase.go
      order_usecase.go
    repository/             # implementations of domain interfaces (Postgres, in-memory, ...)
      user_postgres.go
      order_postgres.go
    delivery/               # framework-specific adapters
      http/                 # Gin/Echo/Chi/net-http handlers and routes
        user_handler.go
        order_handler.go
      grpc/                 # gRPC server adapters (if applicable)
  pkg/                      # exported, importable from outside (if you publish a library)
  migrations/               # SQL migrations
  config/
  go.mod
```

> Read [references/domain.md](references/domain.md), [references/usecase.md](references/usecase.md), [references/repository.md](references/repository.md), and [references/delivery.md](references/delivery.md) for the per-layer responsibilities.

## The Four Layers

| Layer | Package | Can import | Must not import |
|---|---|--

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 6 — file references are metadata, not package contents.

go-code-review8.69 KB

View saved version →

---
name: go-code-review
description: "Invoke this skill to systematically review a Go change against community style standards before merging. Walks the diff topic by topic — formatting, errors, naming, concurrency, interfaces, data structures, security, declarations, functions, style, logging, imports, generics, testing — flagging issues with line references and severity (must-fix / should-fix / nit). Apply proactively before any Go PR ships."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Examples assume Go 1.21+ for log/slog references."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Code Review

A repeatable, opinionated review pass for Go code. Read the diff file by file, walk each topic in order, flag findings with file:line references, then group by severity.

## Core Rules

1. **Mechanical checks first.** Never start a human review until `gofmt`, `go vet`, and `golangci-lint` are clean. They free your attention for what tools cannot catch.
2. **One file at a time, topic by topic.** Walk the diff in order and apply each topic checklist below. Switching topics mid-file loses the thread.
3. **Every finding cites a rule.** `file:line` plus the rule name (`go-naming: initialisms`) — never a bare opinion.
4. **Severity is non-negotiable.** Must-Fix (correctness/security/data-loss/broken contract) blocks merge; Should-Fix (significant design or style issue); Nit (small preference, flag once).
5. **Drop what you cannot defend.** After flagging, re-read and remove any finding you would not stand behind in a thread.
6. **Praise non-trivial improvements.** A review without acknowledgement teaches only avoidance.

## Review Procedure

1. Run mechanical checks: `gofmt -d ./...`, `go vet ./...`, `golangci-lint run ./...`, `go test ./... -race -short`.
2. Read the diff one file at a time. For each file, walk the topic checklists below in order.
3. Flag every issue with `file:line` and the rule name that justifies it.
4. After all files are reviewed, re-read flagged items and drop any you cannot justify.
5. Group findings by **Must Fix / Should Fix / Nit** using the rubric below.

> Use [references/review-template.md](references/review-template.md) when writing up the review for consistent severity grouping and tone.

## Automated Checks

```bash
gofmt -l ./... && go vet ./... && golangci-lint run ./... && go test ./... -race -short
```

Fix anything the tools find before continuing. See [go-linting](../go-linting/SKILL.md) for setup.

## Formatting

- [ ] `gofmt`/`goimports` clean; long lines break by semantics, not column count

## Documentation

- [ ] Exported symbols documented (starts with name, ends with `.`); package comment adjacent to `package` clause; non-trivial unexported have intent comments; named returns only when they clarify

See [go-documentation](../go-documentation/SKILL.md).

## Error Handling

- [ ] No discarded errors (`_ = f()`) without a written justification
- [ ] Error strings are lowercase with no trailing punctuation
- [ ] Errors wrap with `%w` when the caller may want to inspect; `%v` only when hiding is deliberate
- [ ] No in-band magic values (`-1`, `""`, `nil`) for failure — multi-return with an `error` or `ok bool`
- [ ] Error path comes first; the success path stays unindented
- [ ] Each error is handled exactly once (log **or** return, not both)

See [go-error-handling](../go-error-handling/SKILL.md).

## Naming

- [ ] `MixedCaps` / `mixedCaps` only — no underscores or `SCREAMING_SNAKE`
- [ ] Initialisms are uniformly cased: `URL`, `ID`, `HTTP`, `XMLHTTPRequest`, `serveHTTP`
- [ ] Short names for short scopes (`i`, `r`, `ctx`); longer names for wider scopes
- [ ] Receiver names are one or two letters, consistent across methods; no `this`/`self`/`me`
- [ ] Packages don't stutter (`http.Server`, not `http.HTTPServer`)
- [ ] Package names avoid `util`, `helpers`, `common`, `misc`
- [ ] No identifiers shadow builtins (`len`, `erro

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-code-style7.14 KB

View saved version →

---
name: go-code-style
description: "Use when writing or reviewing Go code for clarity, formatting, control flow, variable declarations, switch usage, and function design. Covers the priority order (clarity > simplicity > concision > maintainability > consistency), gofmt rules, early-return / no-else patterns, switch over if-else chains, slice/map initialization, and value-vs-pointer choices. Apply proactively to every Go change, even when the user has not asked about style."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Works on any Go version supported by gofmt; range-over-int requires Go 1.22+."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Code Style

`gofmt` handles the mechanical layout. This skill covers the decisions a formatter cannot make: where to break complexity, when to invert an `if`, when a `switch` beats an `else if` chain, and how to pick value vs pointer arguments. The rule of thumb is the Go Proverb: **clear is better than clever**.

## Core Rules

1. **Run `gofmt`/`goimports`.** No exceptions, no debates.
2. **Handle errors and edge cases first**, return early, keep the happy path at minimum indentation.
3. **Eliminate unnecessary `else`** when the `if` body ends in `return`/`break`/`continue`.
4. **Prefer `switch` over `if`/`else if` chains** that compare the same value.
5. **Initialize slices and maps explicitly**, never let a nil map reach a write.
6. **Pass small values; pass pointers when mutating or when the type is large (~128B+).**

## Style Priority Order

Apply in this order when two principles collide.

| Priority | Question | Beats |
|---|---|---|
| 1. Clarity | Can a reader understand intent without extra context? | everything else |
| 2. Simplicity | Is this the simplest expression of the idea? | concision, consistency |
| 3. Concision | Does every line earn its place? | consistency |
| 4. Maintainability | Will this be safe to modify? | consistency |
| 5. Consistency | Does it match nearby code? | — |

A "consistent" piece of bad code is still bad code. Clarity wins.

> Read [references/formatting-and-layout.md](references/formatting-and-layout.md) for line-breaking, multi-line signatures, file/declaration order.

## Control Flow

### Early return: keep the happy path flat

```go
func process(data []byte) (*Result, error) {
    if len(data) == 0 {
        return nil, errors.New("empty data")
    }
    parsed, err := parse(data)
    if err != nil {
        return nil, fmt.Errorf("parsing: %w", err)
    }
    return transform(parsed), nil
}
```

### No unnecessary `else`

When the `if` body unconditionally exits (`return`/`break`/`continue`), drop the `else`. For assignments, prefer default-then-override:

```go
// Good
lvl := slog.LevelInfo
if debug {
    lvl = slog.LevelDebug
}
```

### Switch over if/else chains

When all branches compare the same value, `switch` makes intent explicit and `exhaustive` can verify completeness:

```go
switch status {
case StatusActive:
    activate()
case StatusInactive:
    deactivate()
case StatusPaused:
    pause()
default:
    panic(fmt.Sprintf("unexpected status: %d", status))
}
```

A tagless `switch` replaces a chain of unrelated `if/else if` conditions — first matching case wins.

### Extract complex conditions

When an `if` has 3+ operands, hoist into named booleans so the names document the business rule:

```go
isAdmin := user.Role == RoleAdmin
isOwner := resource.OwnerID == user.ID
if isAdmin || isOwner || permissions.Has(PermOverride) {
    allow()
}
```

> Read [references/control-flow.md](references/control-flow.md) for tagless switch, guard clauses, and labelled break/continue.

## Variable Declarations

Use `:=` for non-zero initializers, `var` when the zero value is the start.

```go
var count int          // start at 0
name := "default"      // non-zero
var buf bytes.Buffer   // zero value is ready to use
```

### Initialize slices and maps explicitly

A nil map pan

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-concurrency7.69 KB

View saved version →

---
name: go-concurrency
description: "Use when writing or reviewing concurrent Go code — goroutines, channels, select, mutexes, atomics, errgroup, singleflight, worker pools, or fan-out/fan-in pipelines. Apply proactively whenever a goroutine is spawned, a shared field is mutated, or a channel is created, even if the user has not asked about concurrency. Does not cover context.Context patterns (see go-context)."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Targets Go 1.21+ for typed atomics and slog. Notes Go 1.25 wg.Go, Go 1.26 testing/synctest, and Go 1.26 experimental goroutineleak profile where relevant."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Concurrency

Goroutines are cheap, but every one you spawn is a resource you must own. The goal is **structured concurrency**: each goroutine has a clear owner, a predictable exit, and a way for the caller to wait and collect errors.

## Core Rules

1. **Never start a goroutine without knowing how it will stop.** A blocked goroutine is not garbage-collected — it leaks.
2. **The caller must be able to wait.** Use `sync.WaitGroup`, `errgroup.Group`, or an explicit done channel.
3. **No goroutines in `init()`.** Expose `Start`/`Stop`/`Shutdown` so callers control the lifecycle.
4. **Share by communicating.** Default to channels; reach for `sync.Mutex` only when the problem is genuinely "protect a shared field".
5. **Only the sender closes a channel.** Closing from the receiver side panics on the next send.
6. **Specify channel direction** (`chan<-`, `<-chan`) at function boundaries — the compiler catches misuse.
7. **Always include `ctx.Done()` in `select`.** Without it, the goroutine cannot be cancelled.
8. **Test for leaks** with [`go.uber.org/goleak`](https://pkg.go.dev/go.uber.org/goleak).

## Primitive Decision

| Need | Use | Why |
|---|---|---|
| Pass a value from producer to consumer | Channel | Transfers ownership explicitly |
| Wait for N fire-and-forget goroutines | `sync.WaitGroup` (Go 1.25: `wg.Go`) | No error needed |
| Wait + collect first error + cancel siblings | `errgroup.WithContext` | Structured failure |
| Bound concurrency (worker pool) | `errgroup.SetLimit(n)` | Replaces hand-rolled pools |
| Protect a shared field | `sync.Mutex` / `sync.RWMutex` | Short critical section |
| Counter / flag | typed `sync/atomic` (`atomic.Int64`, `atomic.Bool`) | Lock-free, type-safe |
| Read-heavy concurrent map | `sync.Map` | Concurrent map read/write otherwise crashes |
| One-shot init | `sync.Once` (or `OnceFunc`/`OnceValue` in 1.21+) | Idempotent setup |
| Deduplicate concurrent calls | `x/sync/singleflight` | Cache stampede prevention |

> Read [references/sync-primitives.md](references/sync-primitives.md) when picking between mutex, atomic, `sync.Map`, `sync.Pool`, or `singleflight`, or when designing the field layout of a struct that protects shared state.

## Goroutine Lifetimes

```go
// Good: bounded WaitGroup, deterministic exit
var wg sync.WaitGroup
for item := range queue {
    wg.Add(1)
    go func(it Item) { defer wg.Done(); process(ctx, it) }(item)
}
wg.Wait()

// Bad: no stop signal, no wait — classic leak
go func() { for { flush(); time.Sleep(delay) } }()
```

Go 1.25+ exposes `wg.Go(fn)` which folds `Add`/`Done` into one call. Always call `wg.Add` **before** `go` — otherwise `wg.Wait` may return before the goroutine even starts.

## errgroup: Errors and Cancellation

`errgroup.WithContext` is the right default when sibling goroutines should cancel each other on the first failure:

```go
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(8)
for _, url := range urls {
    g.Go(func() error { return fetch(ctx, url) })
}
if err := g.Wait(); err != nil {
    return fmt.Errorf("fetching urls: %w", err)
}
```

`g.Wait` returns the first non-nil error; `ctx` is cancelled as soon as any worker fails. See [references/errgroup-and-pools.md](references/errgroup-and-pools.md).

## Channels

```go

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 5 — file references are metadata, not package contents.

go-context6.14 KB

View saved version →

---
name: go-context
description: "Use when designing, propagating, or debugging context.Context flow in Go — first-parameter placement, deadlines and cancellation, request-scoped values, WithoutCancel for fire-and-forget work, and key-collision-safe value patterns. Apply proactively whenever a function takes ctx, spawns work, or accepts request-scoped data, even if the user has not asked about context."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Requires Go 1.7+ (context in std lib). context.WithoutCancel needs Go 1.21+."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Context Usage

`context.Context` carries the cancellation, deadline, and request-scoped values for a single unit of work. Pass it explicitly through the entire call chain — never store it, never replace it with `Background()` mid-flight, never use it as a side-channel for ordinary parameters.

## Core Rules

1. **`ctx` is the first parameter**, named `ctx context.Context`. No exceptions outside interface stubs imposed by external APIs.
2. **Propagate the caller's `ctx`** all the way down. Do not start a new tree with `context.Background()` inside a request path.
3. **Do not store `Context` in a struct**. Pass it to each method that needs it.
4. **Always `defer cancel()`** after `WithCancel`/`WithTimeout`/`WithDeadline`, unless ownership is explicitly transferred.
5. **Context values are for request-scoped metadata only** (request ID, auth principal, trace). Never for optional function parameters or config.
6. **Value keys must be unexported named types** to prevent cross-package collisions.

## Where Does Data Belong?

Pick the most explicit option that fits — context values are the last resort.

| Option | Use for | Why |
|---|---|---|
| Function parameter | Anything the function *needs* to do its job | Type-checked, visible at call site |
| Method receiver | State that belongs to the type | Already in scope |
| Package-level config | Process-wide, immutable | One owner, no hidden flow |
| `context.Value` | Request-scoped metadata that crosses layers without being a function arg | Untyped — use sparingly |

> Read [references/values-and-keys.md](references/values-and-keys.md) for the unexported-key pattern, typed accessors, and OpenTelemetry/trace propagation.

## Constructors

| Situation | Use |
|---|---|
| `main`, `init`, top-level test | `context.Background()` |
| Placeholder while plumbing is incomplete | `context.TODO()` |
| Inside an HTTP handler | `r.Context()` |
| Need manual cancellation | `context.WithCancel(parent)` |
| Need a deadline / timeout | `context.WithTimeout(parent, d)` / `WithDeadline` |
| Background work that must outlive the request (Go 1.21+) | `context.WithoutCancel(parent)` |

## Propagation: The One Rule

```go
// Bad — breaks the chain, downstream cannot be cancelled
func (s *OrderService) Create(ctx context.Context, o Order) error {
    return s.db.ExecContext(context.Background(), insertSQL, o.ID)
}

// Good — same ctx flows HTTP handler -> service -> DB -> external API
func (s *OrderService) Create(ctx context.Context, o Order) error {
    return s.db.ExecContext(ctx, insertSQL, o.ID)
}
```

## Deriving and Cancelling

```go
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel() // release resources even on the happy path

select {
case <-ctx.Done():
    return ctx.Err()
case res := <-doAsync(ctx):
    return res
}
```

> Read [references/cancellation-and-deadlines.md](references/cancellation-and-deadlines.md) for `WithoutCancel`, `AfterFunc`, and long-running goroutine cancellation patterns.

## Don't Wrap `Context` in Custom Types

```go
// Bad — pollutes the standard signature
type MyCtx interface {
    context.Context
    UserID() string
}

// Good — keep the signature standard, extract via helper
func UserIDFrom(ctx context.Context) (string, bool) { /* ... */ }
```

## Enforce With Linters

Most context mistakes are mechanical and a 

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-control-flow5.31 KB

View saved version →

---
name: go-control-flow
description: "Use when writing conditionals, loops, switches, type switches, or blank-identifier patterns in Go. Covers if-with-initialization, guard clauses, early returns, the unified for loop, range over slices/maps/strings/channels, parallel assignment, labeled break, and `_` for discards and side-effect imports. Apply proactively to any new if/for/switch, even when the user does not mention scoping or shadowing. Does not cover error-flow specifics (see go-error-handling)."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Plain Go (any supported version)."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Control Flow

Go gives you `if`, `for`, and `switch` — and one looping construct that covers them all. The idioms are small but strict: scope variables tightly, return early, keep the happy path unindented.

## Core Rules

1. **Scope variables with if-init when they live only for the check.** `if x, err := f(); err != nil { ... }`.
2. **Guard clauses over nested `else`.** When the `if` body returns/breaks/continues, drop the `else`.
3. **`:=` redeclares only in the same scope.** In an inner scope it shadows — a frequent bug source.
4. **One `for`, three forms.** Condition-only (while), three-clause (C-style), and infinite (`for {}`).
5. **`range` over string yields runes**, over map yields **non-deterministic order**, over channel **drains until closed**.
6. **`break` inside `switch` only breaks the switch.** Use a labeled `break` to exit the enclosing `for`.
7. **The blank identifier `_` discards, but never errors.** Silent error dropping is a bug.

## Decision: var vs := vs = at a glance

| Situation | Use |
|---|---|
| New variable, scoped to the check | `if v, err := f(); err != nil` |
| Reusing an outer variable | plain `=` (avoid `:=` to prevent shadowing) |
| At least one new + reuse of outer | `:=` is fine (same scope only) |
| Wanted zero value | `var x T` |

## If with Initialization

```go
if err := file.Chmod(0664); err != nil {
    return err
}
```

If `err` is needed past the `if`, declare it separately:

```go
x, err := f()
if err != nil {
    return err
}
// use x freely
```

## Guard Clauses

```go
f, err := os.Open(name)
if err != nil {
    return err
}
d, err := f.Stat()
if err != nil {
    f.Close()
    return err
}
codeUsing(f, d)
```

Never bury the success path inside `else`.

## The Shadowing Trap

```go
// Bug: inner ctx never escapes the if block
if *shortenDeadlines {
    ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
    defer cancel()
}

// Fix: assign with =, declaring cancel separately
var cancel func()
ctx, cancel = context.WithTimeout(ctx, 3*time.Second)
defer cancel()
```

> Read [references/blank-identifier.md](references/blank-identifier.md) for `_` use cases (interface checks, side-effect imports, multi-return discards).

## For Loops

```go
// Condition-only (Go's "while")
for x > 0 { x = process(x) }

// Infinite
for {
    if done() { break }
}

// Three-clause
for i := 0; i < n; i++ { ... }
```

### Range

```go
for i, v := range slice { ... }   // index + value
for k, v := range myMap  { ... }  // non-deterministic order
for i, r := range "héllo" { ... } // i is byte offset; r is rune
for v := range ch { ... }         // drains until closed
```

### Parallel Assignment

Go has no comma operator. Use parallel assignment instead:

```go
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
    a[i], a[j] = a[j], a[i]
}
```

`++` and `--` are statements, not expressions — they cannot appear inside a parallel assignment.

## Switch and Labeled Break

```go
Loop:
    for _, v := range items {
        switch v.Type {
        case "done":
            break Loop // breaks the for, not just the switch
        }
    }
```

> Read [references/switch-patterns.md](references/switch-patterns.md) for expression-less switches, comma cases, fallthrough, and type switches.

## Anti-Patterns

| Anti-patt

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 3 — file references are metadata, not package contents.

go-database8.26 KB

View saved version →

---
name: go-database
description: "Use when writing, reviewing, or debugging Go code that talks to a SQL database (PostgreSQL, MySQL, MariaDB, SQLite). Covers library choice (database/sql, sqlx, sqlc, pgx, GORM trade-offs), parameterized queries, context propagation, NULL handling, scanning, transactions and isolation, connection pool tuning, and migration tooling. Apply when adding repository code, refactoring SQL, or auditing for missing rows.Close()/QueryContext."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Requires Go 1.21+. Library-agnostic: applies to database/sql, sqlx, sqlc, pgx."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Database

Go's `database/sql` is a thin, driver-pluggable foundation. Most projects layer one of `sqlx`, `sqlc`, or `pgx` on top for ergonomics. ORMs (GORM, ent) trade SQL visibility for one less line of code — a bad trade in production.

## Core Rules

1. **SQL is the source of truth.** It is reviewed, version-controlled, and explained in code. Magic ORM queries are the opposite.
2. **Always parameterize.** `$1`/`?` placeholders, never string concatenation. The driver handles escaping; you cannot.
3. **Every I/O call takes `ctx`.** `QueryContext`, `ExecContext`, `GetContext`. No context = no timeout = a stuck handler.
4. **Distinguish "not found" from "error".** `errors.Is(err, sql.ErrNoRows)` is a domain signal, not a failure.
5. **Close rows.** `defer rows.Close()` immediately after `QueryContext`. Forgetting it leaks a pool connection.
6. **Configure the pool.** Default `MaxOpenConns` is unlimited — a runaway request rate exhausts the DB.

## Library Decision

| Library | Best for | Struct scanning | Code-gen |
|---|---|---|---|
| `database/sql` | Minimal deps, multi-driver portability | Manual `Scan` | No |
| `sqlx` | Sweetens `database/sql` ergonomics | `StructScan`, `Get`, `Select` | No |
| `sqlc` | Type-safe queries derived from `.sql` files | Generated structs and funcs | Yes |
| `pgx` (v5) | PostgreSQL-only, 30-50% faster, native types | `pgx.RowToStructByName` | No |
| GORM / ent | **Avoid** in new code | Reflection | Yes |

**Why not ORMs.**

- Generated queries are unpredictable; N+1 problems are invisible at the call site.
- Hooks (`BeforeCreate`, `AfterUpdate`) create implicit state machines.
- Schema migrations entangle with application code.
- Learning the ORM API is harder than learning SQL, and the abstraction leaks at every interesting query.

> Read [references/library-tradeoffs.md](references/library-tradeoffs.md) when picking between sqlx, sqlc, and pgx for a new project.

## Parameterized Queries

```go
// VERY BAD — SQL injection.
q := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)

// Good — placeholder, driver-escaped.
err := db.GetContext(ctx, &u, "SELECT id, email FROM users WHERE email = $1", email)
```

### Dynamic `IN` clauses

```go
q, args, err := sqlx.In("SELECT * FROM users WHERE id IN (?)", ids)
if err != nil { return fmt.Errorf("expanding IN: %w", err) }
q = db.Rebind(q)                            // $1, $2, ... for Postgres
err = db.SelectContext(ctx, &users, q, args...)
```

### Dynamic column names

Placeholders cannot stand in for identifiers. Use an allowlist:

```go
allowed := map[string]bool{"name": true, "email": true, "created_at": true}
if !allowed[sortCol] {
    return fmt.Errorf("invalid sort column: %s", sortCol)
}
q := fmt.Sprintf("SELECT id, name FROM users ORDER BY %s", sortCol)
```

## Context Propagation

```go
// Bad — query runs to completion even if the client disconnected.
rows, err := db.Query("SELECT ...")

// Good — driver cancels the query on ctx.Done().
rows, err := db.QueryContext(ctx, "SELECT ...")
```

Every I/O method takes `ctx` first. Pass the request context through service → repository.

## Error Handling

```go
err := r.db.GetContext(ctx, &u, "SELECT ... WHERE id = $1", id)
switch {
case errors.Is(err, sql.ErrNoRows):
    return nil, 

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 5 — file references are metadata, not package contents.

go-data-structures8.16 KB

View saved version →

---
name: go-data-structures
description: "Use when choosing or operating on Go slices, maps, arrays, strings, or container/* types — including slice internals, capacity growth, preallocation, map buckets, sets via map[T]struct{}, strings.Builder vs bytes.Buffer, generic containers, and the slices/maps standard packages (Go 1.21+). Apply proactively whenever data is being collected, transformed, or copied, even if the user has not asked about allocation."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. slices/maps packages need Go 1.21+; iterator helpers need 1.23+; weak.Pointer needs 1.24+."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Data Structures

Pick the structure that fits the access pattern — not the most familiar one. Slices and maps are the workhorses; arrays, container types, and the `slices`/`maps` packages cover the rest. Understanding the **header layout**, **growth costs**, and **copy semantics** of each turns most performance questions into one-line decisions.

## Core Rules

1. **Slices and maps are reference types** — assigning copies the header, not the data. Use `slices.Clone` / `maps.Clone` for a true copy.
2. **Preallocate** with `make([]T, 0, n)` and `make(map[K]V, n)` whenever the size is known or estimable.
3. **Always assign the result of `append`** — the backing array may move.
4. **Use `slices` and `maps` packages** (Go 1.21+) instead of hand-rolled helpers.
5. **`map[K]struct{}` is the canonical set** — zero-byte values, no boolean ambiguity.
6. **`strings.Builder` for string building**, `bytes.Buffer` when you need `io.Reader`/`io.Writer`.

## Picking a Structure

```
What do you need?
├─ Ordered, fixed compile-time size      → [N]T  array
├─ Ordered, dynamic size                 → []T   slice
│  ├─ Known size               → make([]T, 0, n)
│  └─ JSON output must be []   → []T{} literal (not nil)
├─ Key/value lookup                      → map[K]V
│  ├─ Need a set            → map[K]struct{}
│  └─ Known size            → make(map[K]V, n)
├─ Priority queue / top-k                → container/heap
├─ Frequent middle insertion             → container/list
├─ Fixed-size rolling window             → container/ring
├─ Pure string building                  → strings.Builder
└─ Read+write of bytes                   → bytes.Buffer
```

## Slice Internals

A slice is a 3-word header: pointer, length, capacity. Multiple slices can alias the same backing array — `s[1:4]` shares memory with `s`.

### Capacity Growth

The exact algorithm has changed across versions; do **not** rely on it. As of recent Go:

- `len < 256` → capacity roughly doubles.
- `len ≥ 256` → grows by ~25%.
- Each growth allocates a new backing array and copies — O(n) per growth.

### Preallocation

```go
users := make([]User, 0, len(ids))         // exact size
results := make([]Result, 0, estimated)    // approximate
s = slices.Grow(s, additional)             // pre-grow before bulk append (Go 1.21+)
```

### `slices` Package (Go 1.21+)

| Function | Purpose |
|---|---|
| `Sort`, `SortFunc`, `SortStableFunc` | sorting |
| `BinarySearch`, `BinarySearchFunc` | sorted lookup |
| `Contains`, `Index`, `IndexFunc` | search |
| `Compact`, `CompactFunc` | dedupe adjacent equals |
| `Clone`, `Equal` | safe copy / comparison |
| `Delete`, `DeleteFunc` | removal preserving order |
| `Grow` | preallocate before append |
| `Concat` (1.22+) | concatenate slices |

Prefer these over hand-rolled loops — they're tested, generic, and use the fastest available paths.

> Read [references/slices-and-maps.md](references/slices-and-maps.md) for capacity growth, aliasing pitfalls, and 2-D slice patterns.

## nil vs Empty Slice: The JSON Trap

Both have `len == 0` and `cap == 0`, but they encode differently:

```go
var nilSlice []string         // → JSON: null
emptySlice := []string{}      // → JSON: []
```

API contracts almost always want `[]`. **Initialise the slice explicitly** in any s

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-declarations6.11 KB

View saved version →

---
name: go-declarations
description: "Use when declaring or initializing Go variables, constants, structs, or maps. Covers var vs :=, grouped declaration blocks, iota enums starting at 1, struct/map/slice composite literals, raw string literals, `any` over `interface{}`, and avoiding shadowed builtins. Apply proactively to any new struct, const block, or top-level var, even if the user did not ask about declaration style. Does not cover identifier naming (see go-naming)."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. `any` requires Go 1.18+."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Declarations and Initialization

Pick the simplest declaration form that expresses your intent: scope variables tightly, group related declarations, and let the zero value do its job.

## Core Rules

1. **`:=` for locals with values; `var` for intentional zero values or top-level declarations.**
2. **Group related declarations in parenthesized blocks.** Separate unrelated ones into distinct blocks.
3. **Start enums at `iota + 1`** so the zero value is "invalid/unset" — unless zero is genuinely meaningful.
4. **Initialize structs with field names.** Omit zero-value fields; let defaults speak.
5. **Use `any`, not `interface{}`,** in all new code.
6. **Never shadow builtins** (`len`, `cap`, `error`, `new`, `make`, `copy`, `any`, `nil`, ...).

## Decision: var vs :=

| Context | Use | Example |
|---|---|---|
| Package-level | `var` (always) | `var startTime = time.Now()` |
| Local with computed value | `:=` | `s := "foo"` |
| Local zero-value, intentional | `var` | `var filtered []int` |
| Declared type differs from RHS | `var T = expr` | `var e error = f()` |

> Read [references/scope-and-shadowing.md](references/scope-and-shadowing.md) when fighting subtle bugs caused by `:=` redeclaring an outer variable.

## Group Related Declarations

```go
// Bad
const a = 1
const b = 2

// Good
const (
    a = 1
    b = 2
)
```

Inside functions, group adjacent vars even if loosely related:

```go
var (
    caller  = c.name
    format  = "json"
    timeout = 5 * time.Second
)
```

## Constants and iota

Zero is the default; reserve it for "uninitialized" by starting enums at `iota + 1`:

```go
type Operation int

const (
    Add      Operation = iota + 1 // 1
    Subtract                      // 2
    Multiply                      // 3
)
```

Use plain `iota` only when the zero value is the sensible default (e.g., `LogToStdout = iota`).

> Read [references/iota-and-literals.md](references/iota-and-literals.md) for bitmask enums, `String()` methods, raw strings, and composite-literal formatting.

## Initializing Structs

- **Always use field names.** Positional struct literals break on field reordering and are caught by `go vet`.
- **Omit zero-value fields** — clarity beats explicit zeros.
- **`var u User`** for a zero-value struct (not `u := User{}`).
- **`&T{...}` over `new(T)`** when you want a pointer.

```go
u := User{Name: "Ada", Email: "ada@example.com"}
sptr := &Config{Timeout: 5 * time.Second}
var empty Buffer // zero value, ready to use
```

Test tables with ≤3 fields may use positional literals when the meaning is obvious.

## Initializing Maps

| Scenario | Use | Example |
|---|---|---|
| Empty, will be populated | `make(map[K]V)` | `m := make(map[string]int)` |
| Nil, lazily allocated | `var` | `var m map[string]int` |
| Known entries up front | Literal | `m := map[string]int{"a": 1}` |

`make` signals "initialized but empty" — different from a nil map (which panics on write). Provide a size hint when the count is known: `make(map[K]V, n)`.

## Raw String Literals

Use backticks to avoid escape gymnastics:

```go
// Bad
re := "^\\s*name:\\s*\"(.*)\""

// Good
re := `^\s*name:\s*"(.*)"`
```

Ideal for regex, SQL, JSON, and multi-line text.

## `any`, not `interface{}`

```go
// Old
func Print(v interface{}) { ... }

// New
func Print(v any) { ... }
```

`any` is an alias

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-defensive6.5 KB

View saved version →

---
name: go-defensive
description: "Use when hardening Go code at API boundaries: copy slices/maps on entry and return, defer cleanup, verify interface compliance at compile time, model time with time.Time/time.Duration, design enum zero values, prefer crypto/rand, and inject clocks for testability. Apply proactively when reviewing for robustness. Error-handling strategy: see go-error-handling."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. `crypto/rand.Text` examples assume Go 1.24+."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Defensive Programming

Hardening Go code is not paranoia — it is the discipline of making your boundaries honest. Copy what crosses them, clean up what you opened, model time and randomness honestly, and never let a panic escape a package.

## Core Rules

1. **Copy slices and maps at API boundaries.** They are reference types — leaking the backing array leaks mutation.
2. **`defer` the cleanup right after the acquire.** `f, err := os.Open(...); defer f.Close()`.
3. **Verify interface compliance at compile time:** `var _ I = (*T)(nil)`.
4. **Model time and durations with `time.Time` and `time.Duration`,** never raw ints.
5. **Inject `now func() time.Time`** instead of calling `time.Now()` directly in production code.
6. **Enums start at `iota + 1`** so the zero value is invalid.
7. **`crypto/rand` for secrets, never `math/rand`.**
8. **Panics never cross package boundaries.** Convert to errors at the edge.
9. **Avoid mutable package-level state.** Inject dependencies instead.

## Boundary Hardening Checklist

When you touch an exported function or method, walk this list in order:

| # | Check |
|---|---|
| 1 | Return errors, don't panic across boundaries |
| 2 | Copy slices/maps you'll retain |
| 3 | Copy slices/maps you'll return if internal state aliases them |
| 4 | `defer` Close / Unlock / cancel right after the acquire |
| 5 | Compile-time interface satisfaction check |
| 6 | `time.Time` / `time.Duration` types, injected clock |
| 7 | Enum zero = invalid (`iota + 1`) |
| 8 | `crypto/rand` for any secret material |

## Copy at API Boundaries

```go
// Receiving: copy a slice we'll retain
func (d *Driver) SetTrips(trips []Trip) {
    d.trips = make([]Trip, len(trips))
    copy(d.trips, trips)
}

// Returning: copy a map so callers can't mutate our state
func (s *Stats) Snapshot() map[string]int {
    out := make(map[string]int, len(s.counters))
    for k, v := range s.counters {
        out[k] = v
    }
    return out
}
```

> Read [references/boundary-copying.md](references/boundary-copying.md) when deciding which boundaries actually need copies (and when copying is wasted work).

## Defer Cleanup

`defer` evaluates arguments at the `defer` statement and runs the call when the surrounding function returns (LIFO order):

```go
f, err := os.Open(name)
if err != nil {
    return err
}
defer f.Close()
```

Place `defer` immediately after the acquire — the proximity makes pair-correctness reviewable at a glance.

For locks:

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

Beware of `defer` inside loops — accumulated defers run only when the function returns, not when the iteration ends.

## Verify Interface Compliance

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

If `(*Handler)` ever stops satisfying `http.Handler`, the build fails. The line costs nothing at runtime and gives you a free contract.

## Time Modeling

```go
// Bad — what unit is timeout?
type Config struct {
    Timeout int
}

// Good
type Config struct {
    Timeout time.Duration
}
```

For wall-clock work, inject the clock so tests can pin time:

```go
type Signer struct {
    now func() time.Time
}

func NewSigner() *Signer {
    return &Signer{now: time.Now}
}

// In tests:
s := &Signer{now: func() time.Time { return fixedTime }}
```

> Read [references/time-and-enums.md](references/time-and-enums.md) for monotonic time, time zones, struct tags, and embedding tradeo

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-documentation7.73 KB

View saved version →

---
name: go-documentation
description: "Use when writing or reviewing Go documentation — godoc comments on packages, types, functions, methods, sentinel errors; runnable Example tests; README/CONTRIBUTING/CHANGELOG. Covers the project-type detection (library vs application) that decides which docs are needed, comment grammar (start with name, full sentences), what to document vs what to skip, and Example test conventions. Apply proactively when introducing exported names, even if documentation was not requested."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Works on any Go version; new-style godoc headings/links need Go 1.19+."
allowed-tools: Read Edit Write Glob Grep Bash(go:*)
---

# Go Documentation

Documentation in Go is part of the API. Doc comments compile into `go doc`, `pkg.go.dev`, and IDE tooltips, so the rules exist to make those views readable. Code says *what*; comments say *why*, *when*, and *what can go wrong*.

## Core Rules

1. **Every exported name has a doc comment.** Packages, types, functions, methods, constants, variables.
2. **Doc comments start with the name** (`// Encode writes ...`). Full sentences, capitalised, end with a period.
3. **Document non-obvious behaviour.** Restating the signature is noise.
4. **Mark deprecations explicitly** with a `Deprecated:` paragraph.
5. **Examples are tests** — runnable `Example*` functions in `_test.go` files with `// Output:` blocks are verified by `go test`.
6. **Every package has exactly one package comment** above the `package` clause in one file (`doc.go` if long).

## What Project Are You Documenting?

Detect this first — it changes the doc surface area.

| Signal | Project type | Docs to focus on |
|---|---|---|
| No `main` package, intended to be imported | **Library** | godoc, `Example*` tests, README usage examples, pkg.go.dev rendering |
| Has `main` package, `cmd/` directory, ships a binary or Docker image | **Application/CLI** | Install instructions, `--help` text, config docs |
| Both (libraries that ship CLI tools) | Both | All of the above |

Universal: doc comments on exported names, package comment, README, LICENSE, CONTRIBUTING (recommended), CHANGELOG (recommended).

> Read [references/godoc-grammar.md](references/godoc-grammar.md) for the comment grammar, headings/lists, deprecation markers, and full examples.

## Comment Grammar

```go
// Encode writes the JSON encoding of req to w.
// It returns an error if req contains a non-serialisable field.
func Encode(w io.Writer, req *Request) error
```

Rules:

- Start with the name of the thing.
- Use a verb phrase for functions/methods, a noun phrase for types/values.
- Articles (`A`, `An`, `The`) may precede the name.
- Full sentences, punctuation included.
- Wrap at ~80 columns for diff comfort; no hard limit.

Unexported names with non-obvious behaviour should also be commented — those comments are for maintainers, not godoc.

## What to Document

| Topic | Document when | Skip when |
|---|---|---|
| Parameters | edge cases, units, ranges | type/name already say it |
| Context | behaviour differs from standard cancellation | standard `ctx.Err()` propagation |
| Concurrency | ambiguous (e.g., a read that mutates internal state) | read-only is safe by default; mutation is unsafe by default |
| Cleanup | always — `defer Close()`, `Stop()` requirements | — |
| Errors | sentinel values (`ErrNotFound`), error types (use `*PathError` pointer) | — |
| Named returns | multiple values of the same type | type alone is clear |
| Side effects | always — file writes, network calls, init-time work | — |

Restating signatures is the most common waste:

```go
// Bad — restates what you can see
// SetName sets the name.
func (u *User) SetName(name string) { ... }

// Good — explains the why and the constraints
// SetName sets the user's display name. Returns ErrInvalidName
// if name is empty or longer than MaxNameLen.
func (u *User) SetName(name string) error { ... }
```

#

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-error-handling6.76 KB

View saved version →

---
name: go-error-handling
description: "Use when writing, wrapping, inspecting, or logging Go errors. Covers strategy choice (sentinel vs typed vs opaque), wrapping with %w/%v, errors.Is/As/Join, the log-or-return rule, error strings, and panic/recover boundaries. Apply proactively whenever a function returns or accepts an error, even if the user has not asked about error handling."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Requires Go 1.20+ for errors.Join. Wrapping (%w, errors.Is/As) requires Go 1.13+."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Error Handling

Errors in Go are values. Treat them as part of the API: choose a strategy per failure mode, propagate with wrapping, inspect with `errors.Is`/`As`, and handle each error **exactly once**.

## Core Rules

1. **Errors are values, not exceptions.** Return them; do not panic across API boundaries.
2. **Handle each error exactly once.** *Either* log it *or* return it — never both.
3. **The caller decides what is exceptional.** Library code returns; binaries (or top-level handlers) decide whether to log, retry, or exit.
4. **Wrap only when you add real context.** A wrap that just repeats the function name is noise. Use `%w` to preserve identity; `%v` to deliberately hide an unstable type.

## Strategy Decision

Pick the simplest strategy that meets the caller's needs:

| Strategy | When to use | Example |
|---|---|---|
| **Opaque error** (default) | Caller only needs to know *something* failed | `errors.New("invalid input")` |
| **Sentinel error** | Caller needs to test for a specific named condition | `io.EOF`, `sql.ErrNoRows` |
| **Typed error** | Caller needs structured fields (path, code, retry-after) | `*os.PathError`, `*url.Error` |
| **Joined errors** | A single operation produced several independent failures | `errors.Join(errA, errB)` |

> Read [references/strategy-decision.md](references/strategy-decision.md) when the caller's needs are unclear or when migrating between strategies without breaking callers.

## Writing Errors

### Strings

- **Lowercase, no trailing punctuation.** Errors are composed: `fmt.Errorf("write %s: %w", path, err)` reads as one sentence.
- **Be specific.** `"open config: permission denied"` beats `"failed to open file"`.
- **Do not include the function name.** Stack context is added by wrapping at each layer.

### Creating

```go
// Opaque — the caller only checks != nil
return errors.New("invalid character in token")

// Sentinel — exported, package-level, named ErrXxx
var ErrNotFound = errors.New("user: not found")

// Typed — when callers need structured fields
type ValidationError struct {
    Field string
    Rule  string
}
func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation: %s violates %s", e.Field, e.Rule)
}
```

## Wrapping and Inspection

### Wrap with `%w` to add context while preserving identity

```go
if err := db.Get(id); err != nil {
    return fmt.Errorf("loading user %d: %w", id, err)
}
```

### Inspect with `errors.Is` (identity) and `errors.As` (type)

```go
if errors.Is(err, sql.ErrNoRows) { /* handled */ }

var ve *ValidationError
if errors.As(err, &ve) {
    return reply.BadRequest(ve.Field)
}
```

**Never** compare error strings (`err.Error() == "..."`) — strings are not stable API.

### Join independent failures

```go
errs := errors.Join(
    validate(name),
    validate(email),
    validate(password),
)
if errs != nil {
    return errs // errors.Is/As walks both branches
}
```

> Read [references/wrapping-vs-shadowing.md](references/wrapping-vs-shadowing.md) when deciding between `%w` (expose) and `%v` (hide), or when wrapping would leak an implementation detail.

## Error Flow

### Log or return — not both

```go
// Bad: caller will log it again, producing duplicate lines
if err := svc.Do(ctx); err != nil {
    slog.ErrorContext(ctx, "svc.Do failed", "err", err)
    return err
}

// Good: log only at the 

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-functional-options6.08 KB

View saved version →

---
name: go-functional-options
description: "Use when designing a Go constructor or factory with 3+ optional parameters, or an API expected to grow new options over time. Covers the canonical Option interface pattern with unexported apply method, With* constructors, default values, and the interface-vs-closure tradeoff. Apply proactively when reviewing a New* function that takes many settings, even if the user didn't ask about functional options. Does not cover general function design (see go-functions)."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Plain Go (any supported version)."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Functional Options

The functional options pattern lets a constructor stay backward compatible while accepting an open-ended set of optional settings. Callers pass only what differs from the defaults; new options never break old call sites.

## Core Rules

1. **Reach for functional options at 3+ optional parameters** or whenever the API will grow.
2. **The `options` struct is unexported.** Only the package owns its shape.
3. **The `Option` interface has an unexported `apply` method.** No external package can forge an option.
4. **Defaults go inside the constructor**, before options are applied.
5. **Required parameters stay positional;** only the optional ones go through `...Option`.
6. **Prefer the interface form over closures** — it composes better with testing, debugging, and `fmt.Stringer`.

## When to Use What

| Situation | Pattern |
|---|---|
| 0–2 optional params, stable API | Plain positional or named args |
| Config that callers usually pass whole | Config struct |
| 3+ optional params, growing API | **Functional options** |
| Mix of "must set together" + "rare overrides" | Config struct + small `Option` set |

> Read [references/options-vs-struct.md](references/options-vs-struct.md) when choosing between options and a plain config struct, or designing a hybrid.

## The Canonical Pattern

```go
package db

import "go.uber.org/zap"

// options is the package's private bag of settings.
type options struct {
    cache  bool
    logger *zap.Logger
}

// Option configures Open.
type Option interface {
    apply(*options)
}

// --- cacheOption -----------------------------------------------------------

type cacheOption bool

func (c cacheOption) apply(o *options) { o.cache = bool(c) }

// WithCache enables or disables the in-memory cache.
func WithCache(enabled bool) Option { return cacheOption(enabled) }

// --- loggerOption ----------------------------------------------------------

type loggerOption struct{ log *zap.Logger }

func (l loggerOption) apply(o *options) { o.logger = l.log }

// WithLogger sets the logger used by the connection.
func WithLogger(log *zap.Logger) Option { return loggerOption{log: log} }

// --- constructor -----------------------------------------------------------

// Open dials addr using the given options.
func Open(addr string, opts ...Option) (*Connection, error) {
    o := options{
        cache:  true,
        logger: zap.NewNop(),
    }
    for _, opt := range opts {
        opt.apply(&o)
    }
    // ... build the connection from o
    return &Connection{}, nil
}
```

### Caller Experience

```go
db.Open(addr)
db.Open(addr, db.WithLogger(log))
db.Open(addr, db.WithCache(false), db.WithLogger(log))
```

Compare to the alternative where all defaults must be repeated:

```go
db.Open(addr, db.DefaultCache, zap.NewNop()) // tedious
```

## Why an Interface, Not a Closure?

```go
// The closure variant — discouraged
type Option func(*options)
```

The interface form wins on:

1. **Testability** — option values can be compared in tests.
2. **Debuggability** — option types can implement `fmt.Stringer`.
3. **Documentation** — `godoc` lists each option type explicitly.
4. **Extensibility** — options can implement additional interfaces (e.g., `Validate()`).

Closures are shorter to write; they pay 

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 3 — file references are metadata, not package contents.

go-functions5.91 KB

View saved version →

---
name: go-functions
description: "Use when organising functions in a Go file, formatting signatures, designing return values, or naming Printf-style helpers. Covers in-file ordering (type → ctor → exported → unexported → utils), multi-line signature shape, naked-parameter clarity, pointer-vs-value receivers, and the `f`-suffix rule. Apply proactively to any new function. Functional options: see go-functional-options."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Plain Go (any supported version)."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Function Design

A function's surface is read more often than its body. Optimize for the reader: predictable ordering in the file, signatures that scan, no hidden bool flags.

## Core Rules

1. **Order by use, not alphabet.** Types → constructors → exported methods → unexported → utilities.
2. **Keep signatures on one line when reasonable.** When wrapping, every parameter on its own line with a trailing comma.
3. **Never pass `*Interface`.** Pass the interface value; the underlying data can already be a pointer.
4. **Replace naked `bool`/`int` parameters with named types** or add `/* name */` comments at call sites.
5. **Printf-style functions end in `f`** so `go vet` can check the format.
6. **Prefer `%q` over `%s` plus manual quoting** when formatting strings for errors and logs.

## File Ordering

```go
type Server struct{ ... }

func NewServer(...) *Server { ... }     // constructor next to type

func (s *Server) Start(ctx context.Context) error { ... } // exported
func (s *Server) Stop() error           { ... }

func (s *Server) acceptLoop() { ... }   // unexported

func parseAddr(s string) (string, error) { ... } // file-local helper
```

Rules:

1. Types and their constructors sit together at the top.
2. Exported methods come before unexported ones.
3. File-local helpers go at the bottom.
4. Within a section, follow rough call order.

## Signature Formatting

```go
// Fits on one line — keep it on one line
func Sum(xs []int) int

// Too long — break with every param on its own line
func (r *Repo) SaveTransaction(
    ctx context.Context,
    userID string,
    tx Transaction,
    opts ...SaveOption,
) (string, error) {
    ...
}
```

The trailing comma is required and `gofmt`-stable.

### Avoid Naked Bool/Int Parameters

```go
// Bad — what does `true` mean?
NewServer(":8080", true, 30, false)

// Better — call-site comments
NewServer(":8080", true /* tls */, 30 /* maxConn */, false /* readonly */)

// Best — named types or options
NewServer(":8080", WithTLS(), WithMaxConn(30))
```

When a single bool is genuinely binary and obvious from the function name (`SetVerbose(true)`), it's fine.

> Read [references/signatures.md](references/signatures.md) for return-value styles, naked returns, function-as-parameter formatting, and the variadic-options call-site shape.

## Pointers to Interfaces

```go
// Bad
func process(r *io.Reader) { ... }

// Good
func process(r io.Reader) { ... }
```

An interface value already carries a pointer-sized data word. `*io.Reader` is a pointer to an interface — almost always a mistake.

## Printf and Stringer

Functions that accept a format string should end in `f`:

```go
func Logf(format string, args ...any)
```

`go vet` then checks that `%s`, `%d`, etc. match the argument types.

When formatting strings into errors or logs, prefer `%q`:

```go
return fmt.Errorf("unknown key %q", key) // unknown key "foo\nbar"
```

`%q` quotes and escapes; `%s` plus manual quoting (`"key \"" + key + "\""`) is fragile.

> Read [references/printf-and-stringer.md](references/printf-and-stringer.md) for `%v` vs `%s` vs `%q`, implementing `fmt.Stringer` safely, avoiding `String()` infinite recursion, and `fmt.Formatter`.

## Variadic Options at the Call Site

```go
db.Open(addr,
    db.WithCache(false),
    db.WithLogger(log),
    db.WithRetries(3),
)
```

Each option on its own line, trailing comm

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 3 — file references are metadata, not package contents.

go-generics6.05 KB

View saved version →

---
name: go-generics
description: "Use when deciding whether to introduce Go generics, writing generic functions or types, composing type constraints, or choosing between type aliases and type definitions. Apply proactively when a user is writing a utility function that could conceivably work with multiple types, even if they didn't mention generics. Does not cover interface-only designs (see go-interfaces)."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Generics require Go 1.18+; `cmp.Ordered` requires Go 1.21+."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Generics

Generics are a powerful but easy-to-misuse feature. The Go answer is pragmatic: write concrete code first, then generalize only when you have a real second caller.

## Core Rules

1. **Write concrete first.** Reach for generics only when a second type actually needs the same logic.
2. **If an interface already models the behavior, use the interface.** Don't pile type parameters on top.
3. **Prefer standard constraints** (`comparable`, `cmp.Ordered`, `any`) over hand-rolled unions.
4. **Don't over-constrain.** `comparable` is usually enough; the narrower the constraint, the fewer callers benefit.
5. **Name type parameters with a single uppercase letter** (`T`, `K`, `V`, `E`) unless a longer name genuinely helps.
6. **Don't use generics for interface satisfaction.** `func F[T io.Reader](r T)` is just `func F(r io.Reader)`.
7. **Don't wrap stdlib containers** "for generic convenience" unless you eliminate real duplication.

## Decision Flow

```
Multiple types need the same logic?
├─ No  → concrete type
├─ Yes → do they share a useful interface?
│        ├─ Yes → use the interface
│        └─ No  → use generics
```

## When NOT to Use Generics

```go
// Premature: only ever called with int
func Sum[T constraints.Integer | constraints.Float](xs []T) T {
    var t T
    for _, x := range xs { t += x }
    return t
}

// Better
func SumInts(xs []int) int {
    var t int
    for _, x := range xs { t += x }
    return t
}
```

> "Write code, don't design types." — Griesemer & Taylor

## When Generics Pay Off

- A library function the standard library would have written generically: `slices.Index`, `maps.Keys`, `slices.SortFunc`.
- Concurrent-safe data structures (typed sets, ordered maps) where boxing into `any` would be both ugly and slow.
- Map/Reduce-style helpers that genuinely apply to many element types.

## Type Parameter Naming

| Name | Typical use |
|---|---|
| `T` | General element / first type |
| `K` | Map key |
| `V` | Map value |
| `E` | Element of a collection |
| `R` | Result of a transform |

Multi-letter names are reserved for constraints where the meaning is non-obvious:

```go
func Marshal[Opts encoding.MarshalOptions](v any, opts Opts) ([]byte, error)
```

## Constraint Composition

```go
type Numeric interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~float32 | ~float64
}

func Sum[T Numeric](xs []T) T {
    var t T
    for _, x := range xs { t += x }
    return t
}
```

- `~int` means "anything whose underlying type is `int`" — covers `type Celsius int`.
- `|` unions widen the set.
- Prefer `cmp.Ordered` (Go 1.21+) over rolling your own.

> Read [references/constraints.md](references/constraints.md) for the constraint catalogue, when `~` matters, and how type inference interacts with constraints.

## Common Pitfalls

### Don't Wrap Stdlib Types Generically

```go
// Adds complexity, eliminates no duplication
type Set[T comparable] struct {
    m map[T]struct{}
}

// Use the builtin
seen := map[string]struct{}{}
seen["a"] = struct{}{}
```

A generic wrapper around `map[T]struct{}` is only worth it if you keep it for many call sites *and* provide methods that pay for the indirection (e.g., `Union`, `Intersect`).

### Don't Use Generics for Interface Satisfaction

```go
// Pointless type parameter
func Process[T io.Reader](r T) error { ... }

// Just use the inter

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 3 — file references are metadata, not package contents.

go-graphql8.5 KB

View saved version →

---
name: go-graphql
description: "Use when building or reviewing a GraphQL API in Go. Covers library choice (gqlgen vs graph-gophers), schema design (nullability, pagination, mutation envelopes), thin resolver pattern, per-request DataLoaders for N+1, authentication via context plus schema directives, error presenters, subscription lifecycle (context cancellation), and production hardening (complexity limits, introspection gating). Apply when working with github.com/99designs/gqlgen or github.com/graph-gophers/graphql-go."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Requires Go 1.21+. gqlgen v0.17+ or graph-gophers/graphql-go v1.5+."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go GraphQL

Both production-grade Go GraphQL libraries are schema-first: write SDL (`.graphql`), bind Go resolvers. Pick the library, write the schema deliberately, and treat DataLoaders + complexity limits as non-optional.

## Core Rules

1. **Schema is the contract.** Design nullability and pagination once; clients depend on it forever. A change from nullable to non-null is a breaking change.
2. **Resolvers are thin.** Translate GraphQL input → domain call → GraphQL output. No SQL, no business logic.
3. **DataLoaders are per-request.** Construct in HTTP middleware, stash in `context`. A package-level DataLoader is a cross-tenant data leak.
4. **Authenticate in middleware, authorize in the schema.** HTTP middleware extracts identity; schema directives (or resolver checks) enforce per-field rules.
5. **Subscriptions respect context.** Every subscription goroutine selects on `ctx.Done()` and `defer close(ch)`. Otherwise a disconnected client leaks a goroutine forever.
6. **Production limits are non-optional.** Set complexity caps; gate introspection by environment; never expose raw internal errors.

## Library Decision

| Library | Approach | Type safety | Build step | Pick when |
|---|---|---|---|---|
| `github.com/99designs/gqlgen` | Codegen | Compile-time | `go generate` | Large schemas, Federation, strict types |
| `github.com/graph-gophers/graphql-go` | Reflection | Parse-time | None | Small/medium schemas, simple pipeline |
| `github.com/graphql-go/graphql` | Code-first | Runtime | None | **Avoid** — verbose, no SDL |

> Read [references/gqlgen.md](references/gqlgen.md) for the codegen workflow, `gqlgen.yml`, DataLoaders, and Federation.
> Read [references/graph-gophers.md](references/graph-gophers.md) for the reflection model, type mapping, and tracing.

## Schema Design

```graphql
type User {
  id: ID!                # opaque scalar; never expose Int
  email: String!         # server can always return this → non-null
  bio: String            # may be unset → nullable
  posts(first: Int = 10, after: String): PostConnection!
}

type CreateUserPayload {  # mutation envelope: business errors as data
  user: User
  errors: [UserError!]!
}

type PostConnection {     # Relay cursor pagination
  edges: [PostEdge!]!
  pageInfo: PageInfo!
}
```

**Nullability rule.** A field is `!` only when the server can *always* return a value. A resolver error on a non-null field nulls the parent object — cascade failures. Nullable fields null only themselves.

**Pagination.** Cursor connections beat offset pagination on large or write-heavy datasets — cursors are stable under concurrent inserts.

**Mutation envelopes.** Wrap mutation results so business-level errors (validation, conflict) become first-class data instead of polluting the top-level `errors` array.

## Thin Resolvers

```go
// Good — resolver translates and delegates.
func (r *mutationResolver) CreateUser(ctx context.Context, in CreateUserInput) (*CreateUserPayload, error) {
    user, err := r.users.Create(ctx, in.Email, in.Name)
    if err != nil {
        return nil, presentError(err)
    }
    return &CreateUserPayload{User: toGQLUser(user)}, nil
}

// Bad — SQL inside the resolver.
func (r *queryResolver) User(ctx context.Co

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 5 — file references are metadata, not package contents.

go-grpc8.42 KB

View saved version →

---
name: go-grpc
description: "Use when implementing or reviewing gRPC servers/clients in Go. Covers .proto organisation, code generation with protoc/buf, server bootstrap (interceptors, health, graceful shutdown), client patterns (reuse, deadlines, retries), status.Code error handling, streaming, TLS/mTLS, and bufconn testing. Apply when writing .proto files, adding interceptors, or auditing a service for production readiness."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Requires Go 1.21+, protoc (or buf), and google.golang.org/grpc v1.60+."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go gRPC

Treat gRPC as a transport. Keep `.proto`-generated code and business logic separated. The official Go implementation is `google.golang.org/grpc`; pair it with `protoc-gen-go` + `protoc-gen-go-grpc` (or `buf generate`).

## Core Rules

1. **One concern per layer.** `.proto` defines the contract; generated code lives in `gen/`; service implementation lives in `internal/`. Never edit generated files.
2. **Always wrap RPC arguments in Request/Response messages.** Bare scalars (`string`, `int32`) cannot be evolved without breaking callers.
3. **Return typed status codes, never raw errors.** A `fmt.Errorf` becomes `codes.Unknown` on the wire — the client cannot decide whether to retry.
4. **Every client call has a deadline.** No `context.Background()` to a remote service. Set `context.WithTimeout` per call.
5. **Reuse connections.** HTTP/2 multiplexes; creating a new `grpc.ClientConn` per request is a TLS handshake leak.
6. **Disable reflection in production.** Reflection is a developer convenience that doubles as an API enumeration tool for attackers.

## When to Use What

| Need | Use |
|---|---|
| Define service | `.proto` file in `proto/<service>/v1/` |
| Generate stubs | `buf generate` or `protoc --go_out --go-grpc_out` |
| Cross-cutting (auth, logging, recovery) | `grpc.ChainUnaryInterceptor` / `ChainStreamInterceptor` |
| Health probes (Kubernetes) | `grpc_health_v1` from `google.golang.org/grpc/health` |
| Errors with details | `status.Errorf(codes.X, ...)` + `WithDetails(errdetails.BadRequest{...})` |
| Tests | `google.golang.org/grpc/test/bufconn` |
| Service mesh / mTLS | `credentials.NewTLS` or delegate to Istio/Linkerd |

> Read [references/proto-and-codegen.md](references/proto-and-codegen.md) when organizing `.proto` packages or wiring `buf`.
> Read [references/status-and-errors.md](references/status-and-errors.md) when mapping domain errors to gRPC codes.

## Server Bootstrap

```go
import (
    "google.golang.org/grpc"
    "google.golang.org/grpc/health"
    healthpb "google.golang.org/grpc/health/grpc_health_v1"
)

srv := grpc.NewServer(
    grpc.ChainUnaryInterceptor(recoveryUnary, loggingUnary, authUnary),
    grpc.ChainStreamInterceptor(recoveryStream, loggingStream),
)
pb.RegisterUserServiceServer(srv, &userService{...})
healthpb.RegisterHealthServer(srv, health.NewServer())

go func() { _ = srv.Serve(lis) }()

// Graceful shutdown bounded by a hard timeout.
<-shutdownSignal
stopped := make(chan struct{})
go func() { srv.GracefulStop(); close(stopped) }()
select {
case <-stopped:
case <-time.After(15 * time.Second):
    srv.Stop()
}
```

Three pieces are non-negotiable: interceptors for cross-cutting concerns, health service for Kubernetes probes, and a bounded graceful shutdown.

## Client Bootstrap

```go
conn, _ := grpc.NewClient("dns:///user-service:50051",
    grpc.WithTransportCredentials(credentials.NewTLS(tlsCfg)),
    grpc.WithDefaultServiceConfig(`{
      "loadBalancingPolicy": "round_robin",
      "methodConfig": [{
        "name": [{"service": "user.v1.UserService"}],
        "timeout": "5s",
        "retryPolicy": {
          "maxAttempts": 3, "initialBackoff": "0.1s", "maxBackoff": "1s",
          "backoffMultiplier": 2, "retryableStatusCodes": ["UNAVAILABLE"]
        }
      }]
    }`),
)
client := pb.NewUserServiceClient(conn)
ct

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 5 — file references are metadata, not package contents.

go-interfaces8.71 KB

View saved version →

---
name: go-interfaces
description: "Use when defining or implementing Go interfaces, composing types through embedding, designing dependency-injection seams, or deciding between pointer and value receivers. Apply proactively whenever a new abstraction is introduced or a constructor returns an abstract type, even if the user has not asked about interfaces. Does not cover generics (see go-generics)."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Targets Go 1.21+. Generics guidance is delegated to go-generics."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Interfaces and Composition

Interfaces in Go are *consumer contracts*, not implementation hierarchies. They should be **small**, **discovered late**, and **owned by the package that uses them** — not the package that satisfies them.

## Core Rules

1. **Accept interfaces, return concrete types.** Consumers state what they need; producers expose what they have.
2. **Interfaces belong in the consumer package.** Defining an interface next to its sole implementation is almost always wrong.
3. **Don't design with interfaces — discover them.** Wait for a second implementation or a test mock to demand one.
4. **The bigger the interface, the weaker the abstraction.** Aim for 1–3 methods; compose larger contracts from smaller ones.
5. **Receiver consistency:** if any method needs a pointer receiver, give *every* method a pointer receiver.
6. **Verify satisfaction at compile time** with `var _ I = (*T)(nil)` when the relationship must not break silently.
7. **Use the comma-ok idiom for every type assertion.** A bare assertion panics on mismatch.

## Decision: Should I Introduce an Interface?

| Situation | Verdict |
|---|---|
| Single implementation, no tests need to swap it | No interface. Use the concrete type. |
| Second implementation appears (or is imminent) | Extract an interface in the consumer package. |
| Test needs to fake an external dependency | Define a small interface in the consumer; pass a fake. |
| You want to expose optional behaviour (`Flusher`, `ReaderFrom`) | Define a tiny interface; check with `_, ok := v.(Iface)`. |
| You want a stable plugin/SPI boundary | Yes, but keep it minimal and version it explicitly. |

> Read [references/consumer-owned-interfaces.md](references/consumer-owned-interfaces.md) when migrating a producer-defined interface back to the consumer, or when designing a new package boundary.

## Accept Interfaces, Return Concrete Types

```go
// Good — consumer defines what it needs
package notify
type Sender interface { Send(to, body string) error }
type Service struct{ s Sender }
func NewService(s Sender) *Service { return &Service{s: s} }

// Good — producer returns a concrete type
package email
type Client struct{ /* ... */ }
func New(cfg Config) *Client { /* ... */ }
func (c *Client) Send(to, body string) error { /* ... */ }
```

```go
// Bad — producer defines and returns its own interface,
// forcing every consumer to depend on email.Sender.
func New(cfg Config) Sender { return &client{...} }
```

The exception is "expose an interface, hide the implementation": when a type has no exported methods beyond what the interface promises, returning the interface (`func NewHash() hash.Hash32`) is fine.

## Keep Interfaces Small

Standard library interfaces are the model: `io.Reader`, `io.Writer`, `io.Closer`, `fmt.Stringer`, `error` — one or two methods each. Compose larger contracts:

```go
type ReadWriteCloser interface { io.Reader; io.Writer; io.Closer }
```

If you find yourself writing a five-method interface, split it until each piece has a single reason to exist.

## Compile-Time Satisfaction Check

```go
var _ io.ReadWriter = (*MyBuffer)(nil)
```

Use when the type must satisfy an interface for correctness (custom JSON marshalling, `http.Handler`) and no other static use already enforces it. Don't add one for every interface.

## Type Assertions and Type Switches

Alwa

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-linting7.41 KB

View saved version →

---
name: go-linting
description: "Use when setting up linting for a Go project, configuring golangci-lint, picking a linter set, suppressing findings with //nolint, or wiring lint checks into CI. Apply proactively whenever a project lacks .golangci.yml, when lint output is unclear, or when a new package needs the project's quality bar. Does not cover code review process (see go-code-review)."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Targets golangci-lint v2.x and Go 1.21+. Some commands (golangci-lint fmt) require v2."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Linting

The single most important property of a linting setup is **consistency**: every contributor and every CI run uses the same rules. `golangci-lint` is the tool; a checked-in `.golangci.yml` is the contract.

## Core Rules

1. **Every Go project has a `.golangci.yml`** at the repository root. It is the source of truth for which linters run.
2. **Lint runs in CI on every PR.** A green build means lint is green.
3. **Lint runs locally before commit.** A pre-commit hook or `make lint` keeps the feedback loop fast.
4. **Suppress with reasons.** `//nolint:linter // why` — never bare `//nolint`.
5. **Fix the cause first.** A suppression should be the last resort, not the default reaction.
6. **Never silence security linters** (`gosec`, `bodyclose`, `sqlclosecheck`) without a strong, documented reason.

## Setup Procedure

1. Install: `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest` (or `brew install golangci-lint`).
2. Drop a baseline [`.golangci.yml`](assets/.golangci.yml) at the repo root.
3. Run `golangci-lint run ./...`.
4. Fix the findings in order — formatting first, `govet` next, style last.
5. Re-run until clean. Commit `.golangci.yml` and any source fixes together.
6. Add the CI workflow (see [references/ci-integration.md](references/ci-integration.md)).

## Minimum Linter Set

These five catch the most common issues and have the lowest noise rate. Start here:

| Linter | Catches |
|---|---|
| `errcheck` | Unchecked error returns |
| `govet` | Mistakes that compile but are wrong (printf args, shifts, etc.) |
| `staticcheck` | Bug-prone patterns, dead code, simplifications |
| `ineffassign` | Assignments whose value is never read |
| `revive` | Style issues (modern replacement for `golint`) |

Add formatting on top: `gofmt` / `goimports` (or `gofumpt` for stricter rules). With golangci-lint v2 these run via `golangci-lint fmt`.

## Recommended Additional Linters

Enable these once the baseline is clean:

| Linter | When to enable |
|---|---|
| `gosec` | Any service that handles untrusted input |
| `bodyclose` | Any code that calls `http.Client.Do` |
| `sqlclosecheck` | Any code using `database/sql` |
| `nilerr` | Any code that does `if err != nil { return nil }` style returns |
| `misspell` | Always — comments and strings |
| `unconvert` | Always — flags useless type conversions |
| `nolintlint` | Always — enforces the `//nolint` rules below |
| `paralleltest` | If most tests can use `t.Parallel()` |
| `thelper` | If you write test helpers (enforces `t.Helper()`) |
| `testifylint` | If the project uses `testify` |
| `gocyclo` / `gocognit` | When you want a complexity ceiling |
| `exhaustive` | When you use `iota`-based enums and want full `switch` coverage |

> Read [references/linter-catalog.md](references/linter-catalog.md) when picking from the long tail of correctness, style, security, and complexity linters, or when deciding which ones to enable on legacy code.

## Development Workflow

```makefile
lint:
	golangci-lint run ./...

lint-fix:
	golangci-lint run --fix ./...

fmt:
	golangci-lint fmt ./...

ci-lint:
	golangci-lint run --new-from-rev=origin/main ./...
```

| Task | Command |
|---|---|
| Run all enabled linters | `golangci-lint run ./...` |
| Auto-fix everything fixable | `golangci-lint run --fix ./...` |
| Format the tree (v2+) | `golangci

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 5 — file references are metadata, not package contents.

go-logging8.48 KB

View saved version →

---
name: go-logging
description: "Use when choosing a Go logger, configuring slog, writing structured log statements, picking log levels, or attaching request-scoped fields. Apply proactively whenever code calls log/fmt to emit operational information, migrates off log/logrus/zap/zerolog, or sets up production logging. Covers structured logging only — metrics, traces, profiling, and RUM belong to a separate observability skill."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Requires Go 1.21+ for log/slog. Go 1.26 slog.NewMultiHandler is noted where relevant."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Logging

Logs are written for **operators** — the human who will be paged at 3 a.m. and needs to know what happened. Every log line either helps diagnose a production issue or it is noise. `log/slog` from the standard library is the default; reach for anything else only after measuring.

> This skill covers **logging only**. Metrics, distributed tracing, profiling, and RUM are a separate concern — they belong to a future `go-observability` skill. Do not confuse them with logging here.

## Core Rules

1. **Use `log/slog`** for new code. Structured, leveled, in the standard library since Go 1.21.
2. **Static message, structured fields.** The message describes what happened; data goes in key-value attributes.
3. **Log or return, never both.** Logging a wrapped error makes the same failure appear at every layer.
4. **Log at the boundary.** HTTP handlers, job runners, and `main` log. Library code wraps and returns.
5. **Use snake_case keys** consistently across the codebase (`user_id`, `request_id`, `elapsed_ms`).
6. **`slog.Error` always carries an `"err"` attribute.** Without it, you logged a sentence, not an error.
7. **Never log secrets, PII, or unbounded data.** Tokens, full credit cards, request bodies — none of it.

## Choosing a Logger

| Situation | Use |
|---|---|
| New production service | `log/slog` |
| Trivial CLI / one-off script | `log` (the standard package) |
| Measured hot-path bottleneck where slog dominates the flame graph | `zap` or `zerolog`, but keep the structured style |
| Existing zap/logrus/zerolog code | Migrate to `slog` with a bridge handler; see [references/slog-handler-ecosystem.md](references/slog-handler-ecosystem.md) |

`slog`'s API is stable, the ecosystem has consolidated around it, and JSON output works with every log shipper. Do not introduce a third-party logger without a benchmark showing the win.

## Structured Logging

Build log messages from a **static message** plus typed fields:

```go
// Good — static message, structured fields
slog.Info("order placed", "order_id", orderID, "total_cents", totalCents)

// Bad — dynamic data baked into the message string
slog.Info(fmt.Sprintf("order %d placed for $%.2f", orderID, total))
```

The aggregator (Loki, Elastic, CloudWatch) can index `order_id`. It cannot index a sprintf'd sentence.

For hot paths, typed constructors avoid allocations:

```go
slog.LogAttrs(ctx, slog.LevelInfo, "request handled",
    slog.String("method", r.Method),
    slog.Int("status", code),
    slog.Duration("elapsed", elapsed),
)
```

## Log Levels

| Level | When | Default |
|---|---|---|
| `Debug` | Developer-only diagnostics; tracing internal state | Disabled in prod |
| `Info` | Notable lifecycle events: startup, shutdown, config loaded | Enabled |
| `Warn` | Unexpected but recoverable: retry succeeded, deprecated flag used | Enabled |
| `Error` | Operation failed; someone should look | Enabled |

Rules of thumb:

- If nobody should act on it, it is not `Error` — use `Warn` or `Info`.
- If it is only useful with a debugger attached, it is `Debug`.
- `slog.Error` must include an `"err"` attribute.

```go
slog.Error("payment failed", "err", err, "order_id", id)
slog.Warn("retry succeeded", "attempt", n, "endpoint", url)
slog.Info("server started", "addr", addr)
slog.Debug("cache lookup", "key", 

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-naming8.06 KB

View saved version →

---
name: go-naming
description: "Use when naming any Go identifier — packages, types, functions, methods, receivers, variables, constants, errors, options. Covers MixedCaps, scope-based length, initialism casing, the no-`Get` rule, `-er` interfaces, sentinel `ErrX` vs typed `XError`, and the most commonly missed conventions (constructors, boolean fields, enum zero values, lowercase error strings). Apply proactively whenever new identifiers are introduced, even if the user has not asked about naming."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Go 1.0+ for the core rules; iota/enum guidance is version-neutral."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Naming Conventions

Go uses naming to encode visibility (`UpperCamelCase` = exported, `lowerCamelCase` = unexported), so naming is load-bearing — not cosmetic. Names should be **short, contextual, and non-repetitive**. The package name is always present at the call site; pretending otherwise is the single biggest source of bad Go names.

## Core Rules

1. **MixedCaps only.** No underscores, no `SCREAMING_SNAKE_CASE`, no `kHungarian`. Exceptions: test subtests (`TestFoo_BadInput`), generated code, cgo.
2. **Capitalization is visibility.** `Exported`, `unexported`. Do not invent other conventions.
3. **No stuttering.** The package name is at the call site; `http.HTTPClient` is wrong, `http.Client` is right.
4. **Scope drives length.** `i` is fine in a 3-line loop; package-level vars need descriptive names.
5. **Initialisms keep one case.** `userID`, `HTTPServer`, `ParseURL` — never `userId`, `HttpServer`, `ParseUrl`.
6. **Receivers are 1-2 letter abbreviations**, consistent across all methods of the type. Never `this`/`self`.

## Naming Decision Flow

```
What are you naming?
├─ Package        → lowercase single word, singular, specific (not util/common/helper)
├─ File           → lowercase, underscores OK (user_handler.go)
├─ Interface      → method + "-er" when single-method (Reader, Closer, Stringer)
├─ Struct/Type    → MixedCaps noun (Request, FileHeader)
├─ Constructor    → New() if package has one primary type; NewThing() if multiple
├─ Constant       → MixedCaps; never ALL_CAPS; role-based not value-based
├─ Enum (iota)    → type-prefix + Unknown/Invalid at position 0
├─ Sentinel error → ErrXxx (var ErrNotFound = errors.New("..."))
├─ Error type     → XxxError (type PathError struct{})
├─ Boolean field  → is/has/can prefix (isReady, hasPerm)
├─ Getter         → field name only (Owner()), never GetOwner()
├─ Setter         → SetXxx (SetOwner)
├─ Option         → WithXxx (WithLogger, WithPort)
├─ Variant        → WithContext suffix, In suffix (in-place), Must prefix (panics)
└─ Variable       → length proportional to scope distance
```

## Quick Reference Table

| Element | Convention | Example |
|---|---|---|
| Package | lowercase, singular | `http`, `tabwriter` |
| Exported | `UpperCamelCase` | `ReadAll`, `HTTPClient` |
| Unexported | `lowerCamelCase` | `parseToken`, `userCount` |
| Receiver | 1-2 letters | `func (s *Server)` |
| Constant | MixedCaps | `MaxRetries`, `defaultTimeout` |
| Initialism | uniform case | `URL`, `HTTPServer`, `xmlParser` |
| Sentinel error | `Err` prefix | `ErrNotFound` |
| Error type | `Error` suffix | `*PathError` |
| Boolean field | `is`/`has`/`can` | `isConnected` |
| Option func | `With` + field | `WithPort(8080)` |
| Format func | `f` suffix | `Errorf`, `Wrapf` |

## Frequently Missed Conventions

These are correct but non-obvious — they account for most naming mistakes in code review.

### Constructor: `New` vs `NewThing`

If the package exports **one primary type**, the constructor is `New()`. Callers write `apiclient.New()`, not `apiclient.NewClient()`. Only use `NewThing` when the package builds several things (`http.NewRequest`, `http.NewServeMux`).

### Boolean Fields Get a Prefix

Unexported boolean fields use `is`/`has`/`can`. A bare adjective is ambiguou

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-observability8.26 KB

View saved version →

---
name: go-observability
description: "Use when instrumenting Go services with metrics and distributed traces, or wiring exemplars and request-id propagation. Covers Prometheus patterns (Counter/Gauge/Histogram, low-cardinality labels), OpenTelemetry tracing (TracerProvider, span attributes, errors, context propagation), and metric ↔ trace correlation so a P99 spike jumps to the offending trace. Logging: see go-logging."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Requires Go 1.21+ (for log/slog context variants used in correlation snippets). Prometheus client_golang and OpenTelemetry Go SDK."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Observability — Metrics, Traces, and Correlation

Production Go services need at least two always-on signals to be debuggable: **metrics** (aggregated measurements for alerting and SLOs) and **traces** (per-request flow showing where time went). The third deliverable is **correlation**: a P99 metric spike must lead, in one click, to the trace that caused it.

> **Logging is not in this skill.** Structured logging with `log/slog`, log levels, and zap/logrus/zerolog migration belong to the **go-logging** skill. This skill only references logs in the context of correlating them with traces.

## Core Rules

1. **A feature is not done until it is observable.** New code MUST export at least: one Counter for operations, one Counter for errors, one Histogram for latency.
2. **Histograms, not Summaries, for latency.** Summaries cannot be aggregated across instances; Histograms support `histogram_quantile()` server-side.
3. **Label cardinality is bounded.** Never put unbounded values (user IDs, full URLs, request IDs) in Prometheus labels. Use route patterns, status classes, method.
4. **Context flows everywhere.** A function that does I/O takes `ctx context.Context` as its first argument. No context = no trace propagation = no correlation.
5. **Record errors on the span.** When a span ends in failure, call `span.RecordError(err)` and `span.SetStatus(codes.Error, ...)`. A green span hides a real failure.
6. **Correlate or it didn't happen.** Inject `trace_id` into logs, attach exemplars to histograms. Otherwise the three signals are three different products.

## Signal Decision

Pick the signal that matches the question. Do not log what should be a metric.

| Question | Signal | Tool |
|---|---|---|
| How often does X happen? Error rate? Rate-per-second? | Metric (Counter) | Prometheus |
| What is the P99 latency of endpoint /orders? | Metric (Histogram) | Prometheus + `histogram_quantile` |
| Where did this one slow request spend its time? | Trace | OpenTelemetry |
| Why does latency spike at 14:32? | Metric → exemplar → trace | Prometheus + OTel + exemplars |
| What concrete error message did this request hit? | Log (see go-logging) | `log/slog` |

> Read [references/metrics.md](references/metrics.md) for Counter/Gauge/Histogram patterns, naming, and PromQL-as-comments.
> Read [references/tracing.md](references/tracing.md) for TracerProvider setup, span attributes, and `otelhttp` middleware.

## Metrics — the 60-Second Setup

```go
import "github.com/prometheus/client_golang/prometheus"

// rate(http_requests_total{code=~"5.."}[5m]) / rate(http_requests_total[5m])
var httpRequests = prometheus.NewCounterVec(
    prometheus.CounterOpts{
        Name: "http_requests_total",
        Help: "Total HTTP requests by method, route, status class.",
    },
    []string{"method", "route", "code"}, // ALL bounded
)

// histogram_quantile(0.99, sum by (le, route) (rate(http_request_duration_seconds_bucket[5m])))
var httpLatency = prometheus.NewHistogramVec(
    prometheus.HistogramOpts{
        Name:    "http_request_duration_seconds",
        Help:    "HTTP request latency in seconds.",
        Buckets: prometheus.DefBuckets,
    },
    []string{"method", "route"},
)
```

The comment above each metric is the PromQL it is designed to a

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 5 — file references are metadata, not package contents.

go-packages6.27 KB

View saved version →

---
name: go-packages
description: "Use when creating Go packages, organizing imports, managing dependencies, or structuring a Go project. Covers meaningful package names, package size, import grouping (stdlib first, then external), blank/dot imports, the run() pattern in main, init() restrictions, and CLI flag conventions. Apply proactively when starting a new module or splitting a growing codebase, even if the user did not explicitly ask about package layout. Does not cover identifier naming inside packages (see go-naming)."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Plain Go (any supported version)."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Packages and Imports

A package is a unit of meaning, not a folder of files. Name it for what it provides, keep imports tidy, and put startup logic where it belongs.

## Core Rules

1. **Package names describe what the package provides.** `util`, `helper`, `common`, `misc` are not names.
2. **Imports are grouped: stdlib first, then external.** `goimports` will keep this honest.
3. **Avoid `init()`** — and when unavoidable, keep it deterministic and I/O-free.
4. **`os.Exit` / `log.Fatal` only inside `main`.** Library code returns errors.
5. **Use the `run()` pattern** so `main` has a single exit point and deferred cleanup runs.
6. **CLI flags belong in `package main`.** Libraries take configuration as parameters.
7. **Blank imports** belong in `main` or tests. **Dot imports** are essentially never appropriate.

## Decision: How to Split a Package

| Question | If "yes" |
|---|---|
| Can you state the package's purpose in one sentence? | Probably right-sized |
| Do its files never share unexported symbols? | Likely two packages glued by directory |
| Do distinct caller groups touch distinct files? | Split along caller boundaries |
| Is the godoc index so long callers cannot find things? | Split for discoverability |
| Does splitting create import cycles? | Don't split |

> Read [references/package-layout.md](references/package-layout.md) when deciding how to split a growing package, organizing `cmd/`, `internal/`, or designing a library API surface.

## Naming Packages

```go
// Good — meaningful
db := spannertest.NewDatabaseFromFile(...)
_, err := f.Seek(0, io.SeekStart)

// Bad — vague
db := test.NewDatabaseFromFile(...)
_, err := f.Seek(0, common.SeekStart)
```

Generic words may appear as part of a name (`stringutil`, `iotest`) but not as the whole name. Match the package to a concept the caller already knows.

## Imports

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

    "github.com/foo/bar"
    "rsc.io/goversion/version"
)
```

| Rule | Guidance |
|---|---|
| Group order | stdlib, then external; extended order may also separate protos and side-effect imports |
| Renaming | Avoid unless there is a collision; rename the more-local import |
| Blank import (`import _`) | Only `main` and tests |
| Dot import (`import .`) | Effectively never; rare in test files for circular deps |

> Read [references/imports-and-main.md](references/imports-and-main.md) for extended import grouping, proto `pb` suffixes, the `run()` pattern, and CLI flag conventions.

## Avoid `init()`

When you must use `init()`, make it:

1. Deterministic — same result every run.
2. Independent of the order of other `init()`s.
3. Free of environment state (env vars, working dir, args).
4. Free of I/O (filesystem, network, syscalls).

Acceptable uses:

- Precomputing a constant that cannot fit in a single expression.
- Registering pluggable hooks (`database/sql` drivers).

If your `init` reads a file or calls a network API, refactor it into an explicit `Setup()` the caller invokes.

## Exit Only in `main`

```go
func main() {
    if err := run(); err != nil {
        log.Fatal(err)
    }
}

func run() error {
    // all the real work
    return nil
}
```

Why:

- `log.Fatal` and `os.Exit` skip `defer`. Anywhere except `main`, that means leaked files, ha

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-performance7.59 KB

View saved version →

---
name: go-performance
description: "Use when profiling, benchmarking, or optimizing Go code — includes the measure-first methodology, the pprof-driven decision tree (which symptom maps to which fix), allocation reduction, capacity hints, hot-path patterns (strconv vs fmt, repeated string→byte conversions, strings.Builder), and runtime tuning. Apply proactively whenever a user mentions slowness, allocations, GC pressure, or asks for benchmarks, even if no specific pattern is named."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Methodology is Go-version-neutral; `b.Loop()` and PGO require Go 1.21+/1.24+."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Performance

Performance work in Go follows one rule: **measure first**. Intuition about bottlenecks is wrong roughly 80% of the time. Profile, hypothesise, change *one thing*, re-measure. The patterns in this skill apply only on hot paths — premature optimisation makes code worse without making it faster.

## Core Rules

1. **Profile before optimising.** `go test -bench`, `pprof`, `fgprof` — never guess.
2. **One change at a time.** Multi-change "optimisation" passes are unreviewable.
3. **Compare with `benchstat`.** Single runs lie; you need ≥6 runs to see signal.
4. **Allocation reduction usually beats CPU micro-optimisation** — the GC is fast but not free.
5. **Rule out external bottlenecks first.** If 90% of latency is the DB, faster Go code is irrelevant.
6. **Document optimisations in comments.** Future readers will revert "ugly" code without context.

## Iterative Methodology

The cycle is: **define goal → write benchmark → measure baseline → diagnose → improve one thing → re-measure → commit with the diff.**

```bash
# baseline
go test -bench=BenchmarkHotPath -benchmem -count=6 ./pkg/... | tee /tmp/report-1.txt

# (apply ONE change)

# compare
go test -bench=BenchmarkHotPath -benchmem -count=6 ./pkg/... | tee /tmp/report-2.txt
benchstat /tmp/report-1.txt /tmp/report-2.txt
```

If `benchstat` shows no statistically significant change, the optimisation didn't work — revert it. Keep the `/tmp/report-*.txt` files as an audit trail; paste the `benchstat` output in the commit body.

> Read [references/benchmarking-and-pprof.md](references/benchmarking-and-pprof.md) for benchmark writing, pprof workflow, and `b.Loop()` (Go 1.24+).

## Rule Out External Bottlenecks First

Before optimising any Go code, check that the bottleneck is actually in your process:

- **`fgprof`** — captures on-CPU and off-CPU (I/O wait) time. If off-CPU dominates, the issue is elsewhere.
- **Goroutine profile** — many goroutines blocked in `net.(*conn).Read` or `database/sql` means external I/O is the limit.
- **Distributed tracing** — span breakdown shows which upstream is slow.

If the bottleneck is external (DB, downstream API, disk), fix that — query tuning, indexes, connection pools, caching. No Go-level change will help.

## Decision Tree: Where Is Time Spent?

| Symptom (from pprof) | Action |
|---|---|
| High `alloc_objects` / `alloc_space` | reduce allocations (preallocate, pool, struct fields) |
| One function dominates CPU profile | inline-friendly rewrite, avoid reflection, simpler algorithm |
| High GC% / OOM kills | tune `GOMEMLIMIT`, `GOGC`; reduce live heap |
| Goroutines blocked on I/O | concurrency, batching, connection pool tuning |
| Same computation many times | memoise / `singleflight` / cache |
| Wrong algorithm (O(n²) where O(n) exists) | fix algorithm before anything else |
| Mutex profile hot | reduce critical section, sharded locks, `sync.Pool` |

> Read [references/allocation-and-memory.md](references/allocation-and-memory.md) for allocation patterns, `sync.Pool`, struct alignment, and escape analysis.

## Concrete High-ROI Patterns

These are the small changes that consistently show up in profiles. Apply them when the symptom matches — not preemptively.

### 1. `strconv` over `fmt` for primitives

``

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 4 — file references are metadata, not package contents.

go-swagger7.79 KB

View saved version →

---
name: go-swagger
description: "Use when adding or maintaining OpenAPI/Swagger documentation for a Go HTTP API. Covers swaggo/swag annotation comments (@Summary, @Param, @Success, @Router, @Security), the swag CLI workflow, framework integration for Gin/Echo/Fiber/Chi/net-http, security definitions (Bearer/JWT, OAuth2, API key), and struct tags (example, enums, swaggertype, swaggerignore). Apply when a project imports github.com/swaggo/swag or any of the swaggo UI adapters, or when you need to expose /swagger/index.html."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Requires Go 1.21+, swaggo/swag v1.16+ (CLI: `swag`)."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Swagger / OpenAPI with swaggo

`github.com/swaggo/swag` is the de-facto annotation-driven OpenAPI generator for Go. You annotate handlers with `// @...` comments, run the `swag` CLI, and get `docs/swagger.json`, `docs/swagger.yaml`, and `docs/docs.go` for the UI.

## Core Rules

1. **Docs are a contract.** A field documented as required is the API's promise; a mismatch with the implementation is a bug.
2. **Annotations live next to handlers.** Not in a separate `docs/` folder — comments rot when separated from code.
3. **Regenerate on every change.** `swag init` is part of the build (`go generate` or a Makefile target). Stale `docs/` is worse than no docs.
4. **The `docs` package must be imported.** A blank import (`_ "yourmod/docs"`) registers the spec at process start.
5. **Use named structs for request/response bodies.** swag cannot derive a schema from `map[string]any` or a primitive type.
6. **Security definitions match implementation.** If the API enforces JWT, declare `@securityDefinitions.apikey Bearer` and annotate every protected endpoint with `@Security Bearer`.

## Install and Bootstrap

```bash
go install github.com/swaggo/swag/cmd/swag@latest
swag init                              # general info from main.go
swag init -g cmd/api/main.go           # custom main path
swag fmt                               # format annotation comments like gofmt
```

Wire the UI for your framework — choose one:

```go
// Gin
import (
    swaggerFiles "github.com/swaggo/files"
    ginSwagger  "github.com/swaggo/gin-swagger"
)
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

// Echo
r.GET("/swagger/*", echoSwagger.WrapHandler)

// Fiber
app.Get("/swagger/*", fiberSwagger.WrapHandler(swaggerFiles.Handler))

// Chi / net/http
mux.Handle("/swagger/", httpSwagger.Handler(swaggerFiles.Handler))
```

Import the generated spec:

```go
import _ "github.com/acme/myapi/docs"          // blank: just register
import docs "github.com/acme/myapi/docs"       // named: override host at runtime
```

> Read [references/swag-cli.md](references/swag-cli.md) for the CLI flag inventory and Makefile patterns.

## General API Info

Place in the file passed via `-g` (usually `main.go`):

```go
// @title           Orders API
// @version         1.0
// @description     Orders, customers, shipments.
// @contact.name    API Support
// @contact.email   api@acme.example
// @license.name    Apache-2.0
// @host            api.acme.example
// @BasePath        /api/v1
// @schemes         https http

// @securityDefinitions.apikey Bearer
// @in   header
// @name Authorization
// @description Use "Bearer <token>"
```

For multi-environment deployments, set host/basepath at runtime instead of hard-coding:

```go
import docs "github.com/acme/myapi/docs"

func main() {
    docs.SwaggerInfo.Host     = os.Getenv("API_HOST")
    docs.SwaggerInfo.BasePath = "/api/v1"
    // ...
}
```

## Operation Annotations

```go
// GetOrder godoc
// @Summary      Get an order by ID
// @Tags         orders
// @Produce      json
// @Param        id   path  string  true  "Order ID (UUID)"
// @Success      200  {object}  api.OrderResponse
// @Failure      404  {object}  api.ErrorResponse
// @Router       /orders/{id} [get]
// @Security 

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 5 — file references are metadata, not package contents.

go-testing9.05 KB

View saved version →

---
name: go-testing
description: "Use when writing or fixing Go tests — table-driven cases, parallel safety, helpers, fakes, fuzzing, deterministic time (testing/synctest), goroutine leak detection (goleak), HTTP handlers. Apply proactively when a function gets a new test or a test is flaky. Benchmark methodology: see go-benchmark."
license: MIT
compatibility: "Designed for Claude Code or similar AI coding agents. Targets Go 1.21+. Uses Go 1.25+ testing/synctest and Go 1.24+ b.Loop() where relevant."
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
---

# Go Testing

Tests are executable specifications. Their job is to **fail usefully** when behaviour regresses — and to keep failing in the same way until the bug is fixed. Tests that are passing-or-flaky teach the team to ignore them, which is worse than no test at all.

## Core Rules

1. **Failures must be diagnosable from the log alone.** Every `t.Errorf` includes the function under test, the inputs, what we got, and what we wanted, in that order.
2. **No assertion libraries by default.** Use the standard `t.Errorf` / `t.Fatalf` plus `go-cmp` for structural comparison. `testify` is acceptable when adopted consistently — pick one and stick with it.
3. **Test observable behaviour, not implementation details.** If a refactor that preserves behaviour breaks the test, the test was wrong.
4. **Each test runs independently.** No execution-order dependencies, no shared global state without `t.Cleanup`.
5. **`t.Parallel()` whenever the test is safe to run in parallel.** Most are.
6. **`t.Helper()` is the first line of any helper function** that calls `t.Errorf`/`t.Fatalf`. Reserve `t.Fatal` for "next line is meaningless without this value"; everything else uses `t.Error`. Never call `t.Fatal`/`t.FailNow` from a non-test goroutine — send the failure back via channel.

## "Useful Failures" Format

The failure message is the test's user interface. The canonical shape:

```
FunctionUnderTest(input) = got, want want
```

```go
// Good
t.Errorf("Add(2, 3) = %d, want %d", got, 5)

// Bad — no function, no inputs, reversed
t.Errorf("expected %d but got %d", 5, got)
```

Always print **got before want**. With `cmp.Diff(want, got)`, the diff shows `(-want +got)` — echo that direction in your message.

## "No Asserts" Philosophy

Standard-library testing with `if` + `t.Errorf` reads as plain Go and produces messages you control. Assertion libraries shorten call sites but trade away message quality and reorder the `got`/`want` convention.

- **Default** — `if got != want { t.Errorf("...") }` plus `cmp.Diff` for structs, slices, maps, protos.
- **Permitted** — `testify` if the team agrees and `testifylint` is enabled. `require` only when continuing is meaningless.
- **Avoid** — mixing styles within the same package.

For protocol buffers, add `protocmp.Transform()` as a `cmp` option. Don't diff serialised JSON strings — decode and `cmp.Diff` instead.

## t.Error vs t.Fatal

Use `t.Error` by default; reserve `t.Fatal` for "the next line is meaningless without this value" (failed setup, failed decode before use). **Never** call `t.Fatal`/`t.FailNow` from a goroutine other than the test goroutine — it does not stop the test. Send the failure back via channel.

> Read [references/assertions-and-helpers.md](references/assertions-and-helpers.md) when designing helpers, custom comparers, or migrating between stdlib testing and `testify`.

## Table-Driven Tests

```go
func TestCalculatePrice(t *testing.T) {
    tests := []struct {
        name      string
        quantity  int
        unitPrice float64
        want      float64
    }{
        {"single item",   1,   10.0, 10.0},
        {"bulk discount", 100, 10.0, 900.0},
        {"zero quantity", 0,   10.0, 0.0},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            t.Parallel()
            got := CalculatePrice(tt.quantity, tt.unitPrice)
            if got != tt.want {
                t.Er

Preview of the first 4,000 characters. Read full instructions →

Referenced files: 5 — file references are metadata, not package contents.

Package details

Publisher declarations from the archived package. These are separate from our research and the live service's terms.

Package license
MIT
Package author
muratmirgun
Keywords
See keywords

Declared capabilities

  • Read
  • Write

Package observed Nov 14, 2026.

Technical details
First seen
Sep 30, 2026 · 22:02 UTC
Last seen
Nov 14, 2026 · 06:00 UTC
Collection status
Collected

plugins_6a7b1e3e30948191aea92f131b0f6ca9

Download plugin data (JSON)

Plugin details: served from cache. Prepared . Refreshed when plugin data changes or after 6 hours. Your account and Follow status are live.