← GophersCONTENT HISTORY

Update to Gophers

Snapshot Sep 30, 2026 · 23:14 UTC · version 0.1.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "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.",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 221
    },
    {
      "relative_path": "references/control-flow.md",
      "size_in_bytes": 4643
    },
    {
      "relative_path": "references/formatting-and-layout.md",
      "size_in_bytes": 3643
    },
    {
      "relative_path": "references/function-and-data-init.md",
      "size_in_bytes": 4504
    }
  ],
  "name": "go-code-style",
  "skill_md_contents": "---\nname: go-code-style\ndescription: \"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.\"\nlicense: MIT\ncompatibility: \"Designed for Claude Code or similar AI coding agents. Works on any Go version supported by gofmt; range-over-int requires Go 1.22+.\"\nallowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)\n---\n\n# Go Code Style\n\n`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**.\n\n## Core Rules\n\n1. **Run `gofmt`/`goimports`.** No exceptions, no debates.\n2. **Handle errors and edge cases first**, return early, keep the happy path at minimum indentation.\n3. **Eliminate unnecessary `else`** when the `if` body ends in `return`/`break`/`continue`.\n4. **Prefer `switch` over `if`/`else if` chains** that compare the same value.\n5. **Initialize slices and maps explicitly**, never let a nil map reach a write.\n6. **Pass small values; pass pointers when mutating or when the type is large (~128B+).**\n\n## Style Priority Order\n\nApply in this order when two principles collide.\n\n| Priority | Question | Beats |\n|---|---|---|\n| 1. Clarity | Can a reader understand intent without extra context? | everything else |\n| 2. Simplicity | Is this the simplest expression of the idea? | concision, consistency |\n| 3. Concision | Does every line earn its place? | consistency |\n| 4. Maintainability | Will this be safe to modify? | consistency |\n| 5. Consistency | Does it match nearby code? | — |\n\nA \"consistent\" piece of bad code is still bad code. Clarity wins.\n\n> Read [references/formatting-and-layout.md](references/formatting-and-layout.md) for line-breaking, multi-line signatures, file/declaration order.\n\n## Control Flow\n\n### Early return: keep the happy path flat\n\n```go\nfunc process(data []byte) (*Result, error) {\n    if len(data) == 0 {\n        return nil, errors.New(\"empty data\")\n    }\n    parsed, err := parse(data)\n    if err != nil {\n        return nil, fmt.Errorf(\"parsing: %w\", err)\n    }\n    return transform(parsed), nil\n}\n```\n\n### No unnecessary `else`\n\nWhen the `if` body unconditionally exits (`return`/`break`/`continue`), drop the `else`. For assignments, prefer default-then-override:\n\n```go\n// Good\nlvl := slog.LevelInfo\nif debug {\n    lvl = slog.LevelDebug\n}\n```\n\n### Switch over if/else chains\n\nWhen all branches compare the same value, `switch` makes intent explicit and `exhaustive` can verify completeness:\n\n```go\nswitch status {\ncase StatusActive:\n    activate()\ncase StatusInactive:\n    deactivate()\ncase StatusPaused:\n    pause()\ndefault:\n    panic(fmt.Sprintf(\"unexpected status: %d\", status))\n}\n```\n\nA tagless `switch` replaces a chain of unrelated `if/else if` conditions — first matching case wins.\n\n### Extract complex conditions\n\nWhen an `if` has 3+ operands, hoist into named booleans so the names document the business rule:\n\n```go\nisAdmin := user.Role == RoleAdmin\nisOwner := resource.OwnerID == user.ID\nif isAdmin || isOwner || permissions.Has(PermOverride) {\n    allow()\n}\n```\n\n> Read [references/control-flow.md](references/control-flow.md) for tagless switch, guard clauses, and labelled break/continue.\n\n## Variable Declarations\n\nUse `:=` for non-zero initializers, `var` when the zero value is the start.\n\n```go\nvar count int          // start at 0\nname := \"default\"      // non-zero\nvar buf bytes.Buffer   // zero value is ready to use\n```\n\n### Initialize slices and maps explicitly\n\nA nil map panics on write; a nil slice JSON-encodes to `null` (a UX surprise for API consumers).\n\n```go\nusers := []User{}                       // explicit empty\nm := map[string]int{}                   // explicit empty\nusers = make([]User, 0, len(ids))       // preallocate when size is known\nm = make(map[string]int, len(items))    // preallocate map buckets\n```\n\nDo not speculatively preallocate large capacities — `make([]T, 0, 1000)` wastes memory when the common case is 10.\n\n### Composite literals: name the fields\n\n```go\nsrv := &http.Server{\n    Addr:         \":8080\",\n    ReadTimeout:  5 * time.Second,\n    WriteTimeout: 10 * time.Second,\n}\n```\n\nPositional fields break the moment the type adds or reorders a field.\n\n## Function Design and Argument Passing\n\n- **Short and focused**, ≤ 4 parameters. Beyond that, use an options struct or functional options.\n- **Parameter order:** `ctx context.Context` first, then inputs, then output destinations.\n- **`range` over index loops** (`range n` since Go 1.22).\n- **Pass small values, pointers for mutation / large structs (~128B+) / meaningful `nil`.** `*string`, `*int` parameters add indirection with no real saving. `sync.Mutex` and types embedding one are never copied — `go vet copylocks` catches it.\n\n## Anti-Patterns\n\n| Anti-pattern | Why it hurts | Do this instead |\n|---|---|---|\n| Deeply nested `if` chains | Happy path scrolls off-screen | Invert, return early |\n| `if ok { return a } else { return b }` | `else` is dead weight | Drop the `else` |\n| `if x == A else if x == B else if x == C` | Reader has to verify all comparisons share `x` | `switch x { ... }` |\n| Positional struct literal | Breaks silently on field reorder | Named fields |\n| Nil map write | Runtime panic | `make(...)` or `map literal` |\n| `*string`, `*int` parameter to \"save copy\" | Adds indirection, no real saving | Pass value |\n| Long parameter lists | Hard to call, hard to extend | Options struct or `WithXxx` opts |\n\n## Verification Checklist\n\n- [ ] `gofmt -l .` produces no output and `goimports -l .` is clean.\n- [ ] No function body indented past three tab stops without justification.\n- [ ] No `if ... return; else ...` in modified code.\n- [ ] All maps and slices declared without literal use `make` with a sensible capacity.\n- [ ] Composite literals use named fields for non-trivial struct types.\n- [ ] `ctx context.Context` is the first parameter on every function that takes one.\n- [ ] `golangci-lint run` passes with `gocritic`, `revive`, and `gocyclo` enabled.\n\n## Enforce With Linters\n\nThese rules are largely mechanical and a linter will catch them in CI:\n\n- `gofmt`, `gofumpt`, `goimports` — formatting.\n- `gocritic`, `revive` — style heuristics, including `if-return`, `early-return`.\n- `gocyclo`, `funlen` — function complexity / length.\n- `wsl_v5` — whitespace lines for separation between blocks.\n\nAdd to `.golangci.yml` and run `golangci-lint run` in CI.\n\n## References\n\n- [references/formatting-and-layout.md](references/formatting-and-layout.md) — gofmt, line breaks, multi-line signatures, file order\n- [references/control-flow.md](references/control-flow.md) — early returns, switch patterns, labelled loops\n- [references/function-and-data-init.md](references/function-and-data-init.md) — declarations, composite literals, value-vs-pointer\n"
}

SHA-256 of public snapshot: 3311a9c7e888f4952cdd882cfe5bd13ddd66c599816ad120cd0624dece0b86c5