Skip to content
Development
Skill

/go-generics

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,

From plugin
gophers
926 skills4 agents

Claude Code

$ npx -y skills add muratmirgun/gophers --skill go-generics --agent claude-code

SKILL.md

go-generics.SKILL.md
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

// 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:

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

Constraint Composition

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

// 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

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

// Just use the interface
func Process(r io.Reader) error { ... }

Don't Over-Constrain

// Restrictive without reason
func Contains[T interface{ ~int | ~string }](xs []T, t T) bool { ... }

// comparable is enough
func Contains[T comparable](xs []T, t T) bool { ... }

> Read [references/generics-vs-interfaces.md](references/generics-vs-interfaces.md) when interfaces and generics both seem to fit, and you have to choose.

Type Aliases vs Definitions

type Old = pkg.New  // alias: same type, alternate name
type Old pkg.New    // definition: new type, fresh method set

Type aliases (`=`) are for **package migrations** and gradual API moves. For new types, use a definition.

Anti-Patterns

| Anti-pattern | Why it hurts | Do this instead | |---|---|---| | Generic for a single instantiation | Indirection without payoff | Concrete code | | Generic where an interface fits | Type parameter is just `io.Reader` in disguise | Accept the interface | | `interface{ ~int }` when `comparable` suffices | Restricts callers, no benefit | Loosen the constraint | | Custom `Numeric` constraint | `cmp.Ordered` exists | Standard constraint | | `Set[T]` wrapper around `map[T]struct{}` | Two-line struct, no methods | Use the map directly | | Generic function with two type params, neither used | The compiler can infer nothing | Drop one or both |

Verification Checklist

  • [ ] At least two real, current call sites benefit from the type parameter
  • [ ] An interface would not be a simpler model
  • [ ] Constraint is the loosest one that compiles (`any`, `comparable`, `cmp.Ordered` preferred)
  • [ ] Type parameter names are conventional letters unless clarity demands more
  • [ ] No `T` exists only to satisfy an interface — accept the interface instead
  • [ ] No generic wrapper added without methods that justify it
  • [ ] Doc comment explains what the type parameter must support

References

  • [references/constraints.md](references/constraints.md) — constraint catalogue, `~` and `|`,
Read more
@muratmirgun
Ships withgophers

26 production-grade Go skills for Claude Code, Gemini CLI, and opencode. Battle-tested patterns from the Go community — codified as triggerable AI skills.

926 skills4 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
9
Star history of muratmirgun/gophers on GitHub, a chart by star-history.com

Other skills on gophers.

go-clean-architecture

go-clean-architecture

Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase,…

@muratmirgun@muratmirgunInstall Skill
go-code-review

go-code-review

Invoke this skill to systematically review a Go change against community style standards before merging. Walks the diff topic by…

@muratmirgun@muratmirgunInstall Skill
go-code-style

go-code-style

Use when writing or reviewing Go code for clarity, formatting, control flow, variable declarations, switch usage, and function…

@muratmirgun@muratmirgunInstall Skill
go-concurrency

go-concurrency

Use when writing or reviewing concurrent Go code — goroutines, channels, select, mutexes, atomics, errgroup, singleflight, worker…

@muratmirgun@muratmirgunInstall Skill
go-context

go-context

Use when designing, propagating, or debugging context.Context flow in Go — first-parameter placement, deadlines and cancellation,…

@muratmirgun@muratmirgunInstall Skill
go-control-flow

go-control-flow

Use when writing conditionals, loops, switches, type switches, or blank-identifier patterns in Go. Covers if-with-initialization,…

@muratmirgun@muratmirgunInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills2w309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react5d252k
@facebook@facebookInstall Skill

Questions about go-generics.

Which coding agents can use go-generics?
go-generics installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.