From gophers
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.
How this skill is triggered — by the user, by Claude, or both
Slash command
/gophers:go-code-styleThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
`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**.
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.
gofmt/goimports. No exceptions, no debates.else when the if body ends in return/break/continue.switch over if/else if chains that compare the same value.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 for line-breaking, multi-line signatures, file/declaration order.
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
}
elseWhen the if body unconditionally exits (return/break/continue), drop the else. For assignments, prefer default-then-override:
// Good
lvl := slog.LevelInfo
if debug {
lvl = slog.LevelDebug
}
When all branches compare the same value, switch makes intent explicit and exhaustive can verify completeness:
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.
When an if has 3+ operands, hoist into named booleans so the names document the business rule:
isAdmin := user.Role == RoleAdmin
isOwner := resource.OwnerID == user.ID
if isAdmin || isOwner || permissions.Has(PermOverride) {
allow()
}
Read references/control-flow.md for tagless switch, guard clauses, and labelled break/continue.
Use := for non-zero initializers, var when the zero value is the start.
var count int // start at 0
name := "default" // non-zero
var buf bytes.Buffer // zero value is ready to use
A nil map panics on write; a nil slice JSON-encodes to null (a UX surprise for API consumers).
users := []User{} // explicit empty
m := map[string]int{} // explicit empty
users = make([]User, 0, len(ids)) // preallocate when size is known
m = make(map[string]int, len(items)) // preallocate map buckets
Do not speculatively preallocate large capacities — make([]T, 0, 1000) wastes memory when the common case is 10.
srv := &http.Server{
Addr: ":8080",
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
Positional fields break the moment the type adds or reorders a field.
ctx context.Context first, then inputs, then output destinations.range over index loops (range n since Go 1.22).nil. *string, *int parameters add indirection with no real saving. sync.Mutex and types embedding one are never copied — go vet copylocks catches it.| Anti-pattern | Why it hurts | Do this instead |
|---|---|---|
Deeply nested if chains | Happy path scrolls off-screen | Invert, return early |
if ok { return a } else { return b } | else is dead weight | Drop the else |
if x == A else if x == B else if x == C | Reader has to verify all comparisons share x | switch x { ... } |
| Positional struct literal | Breaks silently on field reorder | Named fields |
| Nil map write | Runtime panic | make(...) or map literal |
*string, *int parameter to "save copy" | Adds indirection, no real saving | Pass value |
| Long parameter lists | Hard to call, hard to extend | Options struct or WithXxx opts |
gofmt -l . produces no output and goimports -l . is clean.if ... return; else ... in modified code.make with a sensible capacity.ctx context.Context is the first parameter on every function that takes one.golangci-lint run passes with gocritic, revive, and gocyclo enabled.These rules are largely mechanical and a linter will catch them in CI:
gofmt, gofumpt, goimports — formatting.gocritic, revive — style heuristics, including if-return, early-return.gocyclo, funlen — function complexity / length.wsl_v5 — whitespace lines for separation between blocks.Add to .golangci.yml and run golangci-lint run in CI.
npx claudepluginhub muratmirgun/gophers --plugin gophersProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.