go-generics — Claude Code skill: when to use Go generics
A skill that helps you decide when Go generics are worth it, how to pick constraints, and when a plain type or interface is the better call.
npx skills add cxuu/golang-skills --skill go-generics -a claude-codeWhat go-generics does
The skill walks through the decisions around type parameters in Go: whether to write a generic at all, how to name the parameter, how to compose a constraint. The starting rule is concrete first — generalize only once a second type shows up. Multiple types sharing identical logic with no useful interface means generics; if an interface already models the behavior, the generic is just indirection.
It also covers the usual mistakes: wrapping standard library types for a single call site, using a type parameter only to satisfy an interface, over-constraining when comparable would do. Constraints are built with ~ for underlying types and | for unions, and the skill points at cmp.Ordered from the cmp package instead of hand-rolled versions. Type aliases (type Old = new.Name) are kept for package migration and gradual API refactoring.
Generics need Go 1.18+, and cmp.Ordered needs Go 1.21+. Check the toolchain version in go.mod before copying examples, or the build fails. Interface design without generics is out of scope here — that lives in the separate go-interfaces skill.
What it helps with
- decide whether a function actually needs a type parameter
- pick a constraint for a generic function
- choose between an interface and a generic
- collapse a utility duplicated for every type
- tell a type alias from a type definition
Example prompts for the agent
- Write a slice filter that works across several types — should this be generic?
- Review my constraint: is it narrower than it needs to be?
- Swap my custom constraint for the standard one from cmp if that fits
How to install go-generics
1Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-generics -a claude-code2Manually: put the files in place
~/.claude/skills/go-generics/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/go-generics/
1Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-generics -a codex2Manually: put the files in place
.agents/skills/go-generics/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-generics -a cursor2Manually: put the files in place
.cursor/skills/go-generics/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install cxuu/golang-skills go-generics2Manually: put the files in place
.github/skills/go-generics/SKILL.md1Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-generics -a gemini-cli2Platform CLI
gemini skills install https://github.com/cxuu/golang-skills --path skills/go-genericsManually: put the files in place
.devin/skills/go-generics/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-generics -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about go-generics
How do I install go-generics in Claude?
Run npx skills add cxuu/golang-skills --skill go-generics -a claude-code. The skill file then sits at ~/.claude/skills/go-generics/SKILL.md.
What does go-generics do?
It helps you judge when Go type parameters are worth the complexity, how to choose constraints, and where aliases differ from definitions. It also kicks in when you write a utility meant for several types without mentioning generics.
Which Go version does it need?
Generics landed in Go 1.18, and cmp.Ordered in Go 1.21. With an older version in go.mod, some examples won't compile.
Source text
SKILL.md · Apache-2.0
---
name: go-generics
description: Use when deciding whether to use Go generics, writing generic functions or types, choosing constraints, or picking between type aliases and type definitions. Also use when a user is writing a utility function that could work with multiple types, even if they don't mention generics explicitly. Does not cover interface design without generics (see go-interfaces).
---
# Go Generics and Type Parameters
> Compatibility: Generics require Go 1.18+.
## Resource Routing
- `references/CONSTRAINTS.md` - Read when composing constraints, using type sets, or choosing between generics and interfaces.
## When to Use Generics
Start with concrete types. Generalize only when a second type appears.
### Prefer Generics When
- Multiple types share identical logic (sorting, filtering, map/reduce)
- You would otherwise rely on `any` and excessive type switching
- You are building a reusable data structure (concurrent-safe set, ordered map)
### Avoid Generics When
- Only one type is being instantiated in practice
- Interfaces already model the shared behavior cleanly
- The generic code is harder to read than the type-specific alternative
> "Write code, don't design types." — Robert Griesemer and Ian Lance Taylor
### Decision Flow
```
Do multiple types share identical logic?
├─ No → Use concrete types
├─ Yes → Do they share a useful interface?
│ ├─ Yes → Use an interface
│ └─ No → Use generics
```
**Bad:**
```go
// Premature generics: only ever called with int
func Sum[T constraints.Integer | constraints.Float](vals []T) T {
var total T
for _, v := range vals {
total += v
}
return total
}
```
**Good:**
```go
func SumInts(vals []int) int {
var total int
for _, v := range vals {
total += v
}
return total
}
```
---
## Type Parameter Naming
| Name | Typical Use |
|------|-------------|
| `T` | General type parameter |
| `K` | Map key type |
| `V` | Map value type |
| `E` | Element/item type |
For complex constraints, a short descriptive name is acceptable:
```go
func Marshal[Opts encoding.MarshalOptions](v any, opts Opts) ([]byte, error)
```
---
## Type Aliases vs Type Definitions
Type aliases (`type Old = new.Name`) are rare — use only for package migration
or gradual API refactoring.
---
## Constraint Composition
Combine constraints with `~` (underlying type) and `|` (union):
```go
type Numeric interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~float32 | ~float64
}
func Sum[T Numeric](vals []T) T {
var total T
for _, v := range vals {
total += v
}
return total
}
```
Use the `constraints` package or `cmp` package (Go 1.21+) for standard constraints
like `cmp.Ordered` instead of writing your own.
---
## Common Pitfalls
### Don't Wrap Standard Library Types
```go
// Bad: generic wrapper adds complexity without value
type Set[T comparable] struct {
m map[T]struct{}
}
// Better: use map[T]struct{} directly when the usage is simple
seen := map[string]struct{}{}
```
Generics justify their complexity when they eliminate duplication across
**multiple call sites**. A single-use generic is just indirection.
### Don't Use Generics for Interface Satisfaction
```go
// Bad: T is only used to satisfy an interface — just use the interface
func Process[T io.Reader](r T) error { ... }
// Good: accept the interface directly
func Process(r io.Reader) error { ... }
```
### Avoid Over-Constraining
```go
// Bad: constraint is more restrictive than needed
func Contains[T interface{ ~int | ~string }](slice []T, target T) bool { ... }
// Good: comparable is sufficient
func Contains[T comparable](slice []T, target T) bool { ... }
```
---
## Quick Reference
| Topic | Guidance |
|-------|----------|
| When to use generics | Only when multiple types share identical logic and interfaces don't suffice |
| Starting point | Write concrete code first; generalize later |
| Naming | Single uppercase letter (`T`, `K`, `V`, `E`) |
| Type aliases | Same type, alternate name; use only for migration |
| Constraint composition | Use `~` for underlying types, `|` for unions; prefer `cmp.Ordered` over custom |
| Common pitfall | Don't genericize single-use code or when interfaces suffice |
---
## Related Skills
- **Interfaces vs generics**: See [go-interfaces](../go-interfaces/SKILL.md) when deciding whether an interface already models the shared behavior without generics
- **Type declarations**: See [go-declarations](../go-declarations/SKILL.md) when defining new types, type aliases, or choosing between type definitions and aliases
- **Documenting generic APIs**: See [go-documentation](../go-documentation/SKILL.md) when writing doc comments and runnable examples for generic functions
- **Naming type parameters**: See [go-naming](../go-naming/SKILL.md) when choosing names for type parameters or constraint interfaces
© cxuu · Apache-2.0 · cxuu/golang-skills