Go Struct Tags: A Blessing or a Burden?

Go Struct Tags: A Blessing or a Burden?
Photo by Júlia Borges / Unsplash

Golang (Go) is often praised for its simplicity, performance, and predictability. But if you’ve worked with Go for any serious web or API development, you’ve probably encountered something like this:

type User struct {
    ID        int    `json:"id"`
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
}

At first glance, this might seem redundant. Why do you need to explicitly tell Go how to marshal the struct into JSON, when the field names already tell the story?

That’s one of the most common criticisms of Go — the need for manual struct tags.


Why Go Demands Tags

Go doesn’t try to guess. It’s a language built on the principle of no surprises.

When converting a struct to JSON, Go’s encoding/json package will use the exact field name (capitalized, if exported) unless told otherwise. So:

type Example struct {
    HelloWorld string
}

Would produce:

{
    "HelloWorld": "..."
}

This isn’t always ideal — especially if you're working with snake_case JSON, which is common in APIs. Hence, the need for:

HelloWorld string `json:"hello_world"`

The Good: Explicit is Safe

  • You control exactly what’s going on — no magic, no inference, no “oh, I didn’t know that’s how it works.”
  • It allows for customization: you can omit fields, rename them, or exclude them conditionally (omitempty, -, etc.).
  • Keeps with Go’s core value: readable code that behaves exactly as written.

The Annoying: Boilerplate and Human Error

But let’s be real — it gets repetitive and noisy, especially in large structs. You may find yourself:

  • Repeating the same field name in lowercase dozens of times.
  • Copy-pasting json:"..." just to follow a naming convention.
  • Missing a tag and spending 20 minutes debugging a "why is this field missing in the response?" issue.

This adds friction, especially for those coming from languages that offer reflection, annotations, or naming inference.


What You Can Do About It

You're not stuck. Here are a few things to ease the pain:

🔧 1. Use Tools to Generate Tags

There are tools like:

These can auto-generate or update struct tags, saving time and reducing human error.

🚀 2. Consider a Layered DTO Approach

Sometimes it's cleaner to have separate DTO structs (for JSON input/output) and domain structs (for internal logic). Yes, it’s more code, but it can decouple concerns and improve clarity in complex apps.

3. Explore Alternative Libraries

Libraries like go-json or easyjson offer:

  • Faster performance
  • Some form of automatic code generation
  • Potentially fewer tag headaches (but at a cost of added complexity)

A Real Trade-Off

Yes, Go's approach is more verbose than other languages. But in return, you get:

  • Predictable behavior
  • Cleaner stack traces
  • No hidden surprises

That’s a trade-off some love, and others find painful.

If you value explicitness and long-term maintainability, Go’s struct tagging system is a small price to pay. But if you're all about rapid iteration and less boilerplate, this could feel like a friction point in your workflow.


Finally

Like many Go design decisions, this one is intentional. It leans toward clarity over cleverness, and control over convenience.

So is it a blessing or a burden?

It’s both. And like most things in software engineering — it depends on your priorities.

Support Us