Skip to main content
skill CX Codex

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.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add cxuu/golang-skills --skill go-generics -a claude-code
Open on GitHub

What 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-code

2Manually: put the files in place

~/.claude/skills/go-generics/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/go-generics/

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

Similar skills

All skills for Programming languages →

From the same set19

go-naming A skill that keeps Go names idiomatic: packages, types, functions, constants, receivers. Useful whenever an agent creates new types or exported APIs. CX Codex +1 1,200installs go-context Enforces idiomatic context.Context rules in Go, covering parameter placement, cancellation deadlines, and request data boundaries. CX Codex +1 1,200installs go-style-core A skill that lays out core Go style rules: formatting, nesting, naked returns, semicolons. Useful whenever the agent writes or edits Go code. CX Codex +1 1,200installs go-performance A skill that points your agent at Go hot paths: strconv over fmt, capacity hints for slices and maps, strings.Builder in loops. CX Codex +1 1,200installs go-interfaces Guides coding agents through Go interface design, type composition, and compile-time checks. Prevents premature abstractions in public APIs. CX Codex +1 1,200installs go-declarations A skill that tells your coding agent how to declare and initialize Go variables, constants, structs and maps. Useful the moment you write a new struct or const block. CX Codex +1 1,000installs go-functions Organizes Go file structure, formats long function signatures, and enforces Printf naming conventions. Helpful when writing or refactoring Go methods. CX Codex +1 1,000installs go-logging A skill that steers your coding agent toward sane Go logging: slog, levels, structured fields, request-scoped context. Useful once a Go service grows more log lines than anyone reads. CX Codex +1 1,000installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor