Go Reflection: Inspecting Types at Runtime
Go's `reflect` package lets your program inspect and manipulate variables of unknown types at runtime. This is the engine behind JSON marshaling and generic frameworks. Misuse leads to slow code and runtime panics; always prefer interfaces when possible.
WHY IT EXISTS Go is a statically typed language, which provides safety and performance. However, some problems require operating on types that aren't known at compile time, like a function that serializes any struct to JSON. Reflection provides a mechanism to inspect and manipulate these dynamic types at runtime.
THE MENTAL MODEL Think of reflection as a program looking in a mirror. It can examine its own structure—types, values, fields—while it's running. You start with a concrete value inside an empty interface (interface{}), then use reflect.TypeOf() to get its "blueprint" (the Type) and reflect.ValueOf() to get a handle to its data (the Value).
HOW IT WORKS An interface{} variable stores a pair of things: the value itself and that value's concrete type. The reflect package gives you access to this pair. reflect.TypeOf(i) returns the type. reflect.ValueOf(i) returns the value, wrapped in a reflect.Value object. This Value object has methods to inspect (.Kind(), .NumField()) and modify (.Set(), .FieldByName()) the original data. A crucial detail: you can only modify values that are "addressable"—you must pass a pointer to ValueOf to be able to change the original value.
WHEN TO USE IT Reflection is the right tool for writing code that must operate on data structures it knows nothing about at compile time. This is common in standard library packages like encoding/json and fmt, as well as in database drivers, ORMs, and dependency injection frameworks that need to be generic.
WHEN NOT TO USE IT Avoid reflection for application logic where types are known. If you can solve a problem with an interface, use an interface; it's faster, safer, and clearer. Reflection is slower than direct code and bypasses compile-time type safety, moving potential errors from compile-time to runtime panics. Don't use it just to avoid writing boilerplate.
ONE CANONICAL EXAMPLE The json.Marshal function takes an interface{}. It uses reflection to iterate through the fields of a given struct, read their json:"..." struct tags (also via reflection), and build a JSON object. Without reflection, you would need to write a custom marshaling method for every single type you wanted to serialize.
Read the original → pkg.go.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.