Go Types and Interfaces
Go has no classes, no inheritance, no implements keyword. It uses composition, small interfaces, and implicit satisfaction to achieve polymorphism.
Interface Design Rules
| Rule |
Rationale |
| Accept interfaces, return structs |
Callers define the contract they need; implementations stay concrete |
| Keep interfaces small (1-3 methods) |
io.Reader is the gold standard — one method, universally useful |
| Define interfaces at the consumer site |
The package that uses the interface defines it, not the package that implements it |
Implicit satisfaction — no implements |
Types satisfy interfaces by having the right methods, no declaration needed |
| Don't export interfaces from implementation packages |
Let consumers define what they need |
// Consumer defines the interface it needs
type UserStore interface {
FindByID(ctx context.Context, id string) (*User, error)
}
// Implementation satisfies it without knowing
type PostgresStore struct { db *sql.DB }
func (s *PostgresStore) FindByID(ctx context.Context, id string) (*User, error) {
// ...
}
Composition Decision Table
| Need |
Go pattern |
Not this |
| Reuse behavior |
Struct embedding |
Inheritance |
| Polymorphism |
Interfaces |
Abstract base class |
| Has-a relationship |
Regular field |
Embedding |
| Extend interface contract |
Interface embedding |
Interface inheritance |
| Share code across types |
Package-level functions |
Base class methods |
// Embedding — promoted methods
type Server struct {
http.Server // embeds net/http Server
logger *slog.Logger
}
// s.ListenAndServe() works — promoted from http.Server
// Interface embedding
type ReadCloser interface {
Reader
Closer
}
Generics Quick Guide (Go 1.18+)
| Use generics when |
Don't use generics when |
| Writing containers (stack, queue, set) |
An interface already solves it |
| Algorithms over any ordered/comparable type |
It adds complexity without reducing duplication |
| Reducing duplication across type-safe functions |
Only 1-2 concrete types exist |
func Map[T, U any](s []T, f func(T) U) []U {
result := make([]U, len(s))
for i, v := range s {
result[i] = f(v)
}
return result
}
names := Map(users, func(u User) string { return u.Name })
See Collections & Generics for type constraints (~int | ~float64, comparable) and the slices/maps/cmp packages.
Enums with Iota
Go has no enum keyword. Use typed constants with iota (sequential), or 1 << iota for bitmask flags. Reserve the zero value for "unknown" to catch uninitialized values, and give the type a String() method.
See Collections & Generics for full enum, bitmask, and stringer examples.
Anti-patterns
| Anti-pattern |
Problem |
Fix |
| Interface pollution (10+ methods) |
Weak abstraction, hard to implement/mock |
See Interface Design Rules — keep interfaces small (1-3 methods) |
| Premature interfaces |
Interface defined before second implementation exists |
Wait until you need polymorphism |
| Embedding for code reuse without is-a |
Promoted methods leak into API surface |
Use a regular field instead |
any / interface{} everywhere |
Erases type safety |
Use specific interfaces or generics |
| Generics for 1-2 concrete types |
Over-engineering |
See Generics Quick Guide — write the concrete functions |
Read On Demand
| Read When |
File |
| Struct embedding mechanics, promoted fields/methods, struct tags |
Structs & Composition |
| Standard library interfaces, type assertions, type switches |
Interfaces Deep Dive |
| Slice internals, map patterns, generics syntax, type constraints |
Collections & Generics |
Benchmark
Scenario: .benchmarks/scenarios/golang-types-and-interfaces-001-consumer-interface.md · Run: 2026-08-31 · Log: .benchmarks/runs/2026-08-31/golang-types-and-interfaces-001-consumer-interface.json
| Model |
Without |
With |
Delta |
| claude-opus-4-8 |
83% |
100% |
+17% |
| claude-sonnet-4-6 |
83% |
100% |
+17% |
| claude-haiku-4-5 |
100% |
100% |
+0% |
SOFT PASS (run 2026-08-31). Opus/sonnet +17; haiku at ceiling. Consumer-side interface definition is half-default already. Gate per .agents/skills/skill-optimizer/rules/release-gates.md.
1---2name: types-and-interfaces3description: Go type system — structs, interfaces, embedding, composition, generics, slices, maps, enums with iota. TRIGGER when: user asks about Go structs, Go interfaces, Go embedding, composition in Go, Go generics, type parameters, Go type constraints, Go slices, Go maps, Go iota, Go enum, Go bitmask, implicit interface, interface satisfaction, small interfaces, accept interfaces return structs, Go type assertion, Go type switch, when to use generics in Go, Go collections, Go struct tags, Go composition vs inheritance, Go polymorphism. DO NOT USE when: user asks about OOP design principles or composition/polymorphism in a language-agnostic way with no Go code involved — use `object-oriented-programming` instead.4---56# Go Types and Interfaces78Go has no classes, no inheritance, no `implements` keyword. It uses composition, small interfaces, and implicit satisfaction to achieve polymorphism.910---1112## Interface Design Rules1314| Rule | Rationale |15| ---------------------------------------------------- | ------------------------------------------------------------------------------------ |16| Accept interfaces, return structs | Callers define the contract they need; implementations stay concrete |17| Keep interfaces small (1-3 methods) | `io.Reader` is the gold standard — one method, universally useful |18| Define interfaces at the consumer site | The package that _uses_ the interface defines it, not the package that implements it |19| Implicit satisfaction — no `implements` | Types satisfy interfaces by having the right methods, no declaration needed |20| Don't export interfaces from implementation packages | Let consumers define what they need |2122```go23// Consumer defines the interface it needs24type UserStore interface {25 FindByID(ctx context.Context, id string) (*User, error)26}2728// Implementation satisfies it without knowing29type PostgresStore struct { db *sql.DB }3031func (s *PostgresStore) FindByID(ctx context.Context, id string) (*User, error) {32 // ...33}34```3536---3738## Composition Decision Table3940| Need | Go pattern | Not this |41| ------------------------- | ----------------------- | --------------------- |42| Reuse behavior | Struct embedding | Inheritance |43| Polymorphism | Interfaces | Abstract base class |44| Has-a relationship | Regular field | Embedding |45| Extend interface contract | Interface embedding | Interface inheritance |46| Share code across types | Package-level functions | Base class methods |4748```go49// Embedding — promoted methods50type Server struct {51 http.Server // embeds net/http Server52 logger *slog.Logger53}54// s.ListenAndServe() works — promoted from http.Server5556// Interface embedding57type ReadCloser interface {58 Reader59 Closer60}61```6263---6465## Generics Quick Guide (Go 1.18+)6667| Use generics when | Don't use generics when |68| ----------------------------------------------- | ----------------------------------------------- |69| Writing containers (stack, queue, set) | An interface already solves it |70| Algorithms over any ordered/comparable type | It adds complexity without reducing duplication |71| Reducing duplication across type-safe functions | Only 1-2 concrete types exist |7273```go74func Map[T, U any](s []T, f func(T) U) []U {75 result := make([]U, len(s))76 for i, v := range s {77 result[i] = f(v)78 }79 return result80}8182names := Map(users, func(u User) string { return u.Name })83```8485See [Collections & Generics](references/collections-generics.md) for type constraints (`~int | ~float64`, `comparable`) and the `slices`/`maps`/`cmp` packages.8687---8889## Enums with Iota9091Go has no `enum` keyword. Use typed constants with `iota` (sequential), or `1 << iota` for bitmask flags. Reserve the zero value for "unknown" to catch uninitialized values, and give the type a `String()` method.9293See [Collections & Generics](references/collections-generics.md) for full enum, bitmask, and `stringer` examples.9495---9697## Anti-patterns9899| Anti-pattern | Problem | Fix |100| ------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------- |101| Interface pollution (10+ methods) | Weak abstraction, hard to implement/mock | See Interface Design Rules — keep interfaces small (1-3 methods) |102| Premature interfaces | Interface defined before second implementation exists | Wait until you need polymorphism |103| Embedding for code reuse without is-a | Promoted methods leak into API surface | Use a regular field instead |104| `any` / `interface{}` everywhere | Erases type safety | Use specific interfaces or generics |105| Generics for 1-2 concrete types | Over-engineering | See Generics Quick Guide — write the concrete functions |106107---108109## Read On Demand110111| Read When | File |112| ---------------------------------------------------------------- | ------------------------------------------------------------ |113| Struct embedding mechanics, promoted fields/methods, struct tags | [Structs & Composition](references/structs-composition.md) |114| Standard library interfaces, type assertions, type switches | [Interfaces Deep Dive](references/interfaces.md) |115| Slice internals, map patterns, generics syntax, type constraints | [Collections & Generics](references/collections-generics.md) |116117---118119## Benchmark120121Scenario: `.benchmarks/scenarios/golang-types-and-interfaces-001-consumer-interface.md` · Run: 2026-08-31 · Log: `.benchmarks/runs/2026-08-31/golang-types-and-interfaces-001-consumer-interface.json`122123| Model | Without | With | Delta |124| ----------------- | ------- | ---- | ----- |125| claude-opus-4-8 | 83% | 100% | +17% |126| claude-sonnet-4-6 | 83% | 100% | +17% |127| claude-haiku-4-5 | 100% | 100% | +0% |128129> **SOFT PASS (run 2026-08-31)**. Opus/sonnet +17; haiku at ceiling. Consumer-side interface definition is half-default already. Gate per `.agents/skills/skill-optimizer/rules/release-gates.md`.