Go's reflect Package: Three Patterns for Runtime Type, Value, and Method Access
Reflection shifts errors from compile time to runtime and adds overhead, so these three patterns separate safe reflect-based utilities from panic-prone code. Most production Go projects keep reflection confined to framework and serialization layers, and the advice to prefer interfaces and generics holds for everyday business logic.
Reflection is a standard-library power tool used by frameworks, ORMs, and JSON codecs. It trades compile-time safety for runtime flexibility, so Go's usual advice applies: prefer interfaces or generics for business logic, and reach for reflection only when a library or framework demands it.
Type inspection comes first. reflect.TypeOf returns a Type; Name() gives the declared name (like "Age"), while Kind() gives the underlying built-in kind (reflect.Int). Branch on Kind, because it stays stable across named types and aliases. Mutation is next: reflect.ValueOf(&x).Elem() yields a settable value; check CanSet, IsValid, and the expected Kind before calling SetInt. To get a typed value back, val.Interface().(Age) performs a type assertion. Method invocation is the third pattern: MethodByName("Say") finds an exported method, and Call takes a slice of reflect.Value arguments, returning a slice of reflect.Value results.
These patterns boil down to: use Type for reading, Value for writing; pass a pointer and call Elem to mutate; box values with ValueOf on the way in, and unbox with Interface() plus a type assertion on the way out. Those three moves cover the reflect usage that appears in real code.
The Name/Kind split is the first trap: Name reports the custom type identifier, but Kind reports the underlying representation, so any branch that switches on Kind works for all named types built on that same base.
Passing a pointer and calling Elem() is the only way to get a settable reflect.Value; skipping Elem() yields a copy that will panic on SetInt. That design forces developers to be explicit about mutating the original.
Preferring interfaces and generics over reflection is not just performance advice; it keeps type errors at compile time, where the compiler can catch them, instead of allowing them to surface as runtime panics.