Stage 1 · Code
Advanced Runtime & Tooling
Reflection in Go
Learn how Go can inspect types and values while the program is already running, and why that power is useful for tools like JSON encoders but expensive to lean on everywhere.
What Reflection Is
Most Go code works like a labeled toolbox. At compile time, the compiler already knows whether a value is an int, a string, or a Customer struct, so your code can use the right tools directly. Reflection is what you reach for when that label is hidden and you need the program to examine the box for itself while it is running.
In plain language, reflection means inspecting or manipulating values and types at runtime instead of compile time. You are asking questions like 'what kind of thing is this?', 'how many fields does this struct have?', and 'what tag is attached to this field?' after the program has already started.
That sounds abstract until you connect it to something you already know: JSON struct tags. When encoding/json turns a struct into JSON, it has to discover field names, decide which ones are exported, and read tags like json:"name". It cannot hard-code those decisions for every struct in your codebase, so it uses reflection to inspect the struct at runtime.
If normal Go code is 'I know this is a User, so I will use it like a User', reflective Go code is 'I was handed some unknown value, so let me inspect it first and decide what it is.'
Meeting TypeOf and ValueOf
The reflect package gives you two starting points. reflect.TypeOf tells you about the value's type information. reflect.ValueOf gives you a reflective wrapper around the value itself so you can inspect data stored inside it.
type: int
kind: int
value: 98
as int: 98Nothing magical happened to score. Reflection wrapped it so your code could inspect it generically.
One subtle but important distinction: Type and Kind are related, but not identical. Type can be a specific named thing like main.InventoryItem. Kind is the broader category like struct, slice, or int. When you are writing generic inspection code, you usually branch on kind first.
Reading Struct Fields and Tags
Now for the part that makes reflection feel practical. Suppose a warehouse tool receives any struct and wants to print each field together with the JSON tag a serializer would look at. That is exactly the kind of job reflection was built for.
type: InventoryItem
ID = sku-404 | json="id" | label="Inventory ID"
Quantity = 12 | json="quantity" | label="Units in stock"
Fragile = true | json="fragile" | label="Needs careful packing"This is the same information a package like encoding/json inspects before deciding how to encode your struct.
reflect.TypeOf(item)starts with the concrete typeInventoryItem.NumField()tells us how many declared fields the struct has.- Each
StructFieldcarries metadata, including tags written in the source code. - Each
Valuelets us read the actual stored data, such as12forQuantity.
If you pass a pointer like &item, the code first unwraps it with Elem(). That is a common reflective pattern: inspect whether you were handed a pointer, then step through it so you can work with the underlying struct.
Why Reflection Is Powerful and Costly
Reflection is powerful because it lets one piece of code adapt to many types it did not know about when it was compiled. Frameworks, serializers, ORMs, config loaders, and test helpers all lean on that flexibility.
| Why people use it | Why experienced Go developers stay cautious |
|---|---|
| One function can inspect many unrelated struct types | The compiler can no longer protect you as well, because type checks move from compile time to runtime |
| Tags let you describe behavior in struct definitions once | Reflective code is slower than direct field access and adds allocation and lookup overhead |
| Libraries can build flexible APIs without code generation for every case | It makes code harder to read, harder to refactor, and easier to break accidentally |
If ordinary typed Go can solve the problem, prefer that. Reflection is usually best at the edges of a system: serialization, framework glue, generic inspection, and tooling. It is rarely the clearest way to write everyday business logic.
Quiz
What is reflection in Go really about?
Code Challenge
Write a function `JSONTags(v any) []string` that returns the declared struct fields in order as `FieldName=jsonTag`. Support either a struct value or a pointer to a struct. If a field has no `json` tag, use `-` in its place. If `v` is not a struct or pointer to struct, return `nil`.
Summary
- Reflection means inspecting types and values at runtime instead of relying only on compile-time knowledge.
reflect.TypeOfgives you metadata about a value's type, whilereflect.ValueOflets you inspect the data stored inside it.- Struct tags are just metadata attached to fields, and reflection is how packages like
encoding/jsondiscover them. - Reflection is most useful for framework-style glue and tooling, not as the default style for ordinary application logic.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.