Lesson 9 · Methods, interfaces & generics

Generics

Type parameters — and the interview question "generics or an interface?"

Your win: read and write a generic function, explain what a constraint is, and answer the question interviewers actually care about — when to reach for generics instead of an interface.

A function parameterised by a type

Since Go 1.18, functions and types can take type parameters in square brackets. A constraint (an interface, or the built-ins any / comparable) says which types are allowed.1

Our sliceutils is the cleanest example:

internal/golibs/sliceutils/slice_utils.go
func Map[T any, R any](in []T, f func(T) R) []R { ... }   // :38
func Filter[T any](in []T, keep func(T) bool) []T { ... } // :64
func Contains[T comparable](s []T, v T) bool { ... }        // :123, needs ==

// Instantiated with type inference — looks like any call:
ids := sliceutils.Map(emails, func(e *Email) string { return e.EmailID.String })

comparable on Contains is a constraint doing real work: it permits only types you can compare with ==, so the compiler rejects, say, a slice of func values.

The bootstrap framework is generic over your config

The other pattern in our repo: generics let one framework work with each service's own config type T without falling back to interface{} and casts.

internal/golibs/bootstrap/kafka.go:16 · grpc.go:35 · command.go:240
type KafkaServicer[T any] interface {
    InitKafkaConsumers(context.Context, T, *Resources) error
}
type bootstrapper[T any] struct { config *T; ... }
func RegisterJob[T any](name string, s JobFunc[T]) *JobBuilder[T] { ... }
Two jobs, two reasons sliceutils uses generics for algorithms that don't care about element type (map/filter/contains). bootstrap uses generics so each service keeps a strongly-typed config T end-to-end — no interface{}, no runtime casts. Both avoid the pre-1.18 tax of losing type safety.

The interview question: generics or interface?

The decision rule Use an interface when different types need different behaviour (call a method — polymorphism). Use generics when the logic is identical and only the type varies — containers and algorithms.2 If you're writing interface{} + a type assertion, you probably want generics.

Concretely: our repository ports are interfaces (each adapter behaves differently — hits a different table). sliceutils.Map is generic (the loop is the same for every element type). Naming that split is a strong senior-level answer.

Don't over-reach Generics aren't free readability. The Go team's guidance: don't reach for a type parameter until you'd otherwise duplicate code across types or lose type safety. "Write the concrete version first; generalise when the duplication is real."
Read this next

The Go Blog — "An Introduction To Generics" + "When To Use Generics"

The official pair: the first teaches the syntax, the second answers the interview question directly. Then the Tour's generics section for hands-on.

go.dev/blog/intro-generics
go.dev/blog/when-generics · Tour — generics

Check yourself (from memory)

Q1. A constraint like comparable on a type parameter specifies…

A constraint is a type set — comparable allows only ==-comparable types, so Contains can use ==.

Q2. Generics are the right tool when…

Same logic, varying type → generics (like Map). Different behaviour per type → interface.

Q3. Why is bootstrap declared as KafkaServicer[T any]?

T is the service's own config type, carried type-safely end-to-end — no interface{}, no casts.
Generics vs interfaces — how do you choose?
recall, then click to reveal
Use an interface when types need different behaviour (polymorphism via methods). Use generics when the logic is identical and only the type varies — containers/algorithms like Map/Filter/Contains — keeping compile-time type safety without interface{} + casts. Our repo: ports = interfaces, sliceutils = generics.
🎓 That's Part 2 You can now reason about methods & method sets, design with small implicit interfaces, dodge the nil-interface trap, compose via embedding, and choose generics when they fit. Part 3 is errors & idiomatic Go — including the real %w-vs-%v bug hiding in our own handlers.
Ready for Part 3 (errors & idiomatic Go), or want a mixed quiz across Lessons 5–9 first? Ask me.

1. The Go Blog — An Introduction To Generics; Go spec — type parameters.

2. The Go Blog — When To Use Generics.