Go/
Lesson

Structs are how Go organizes data. No classes, no inheritance hierarchy, just fields grouped together. When AI generates Go code for an APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses., a database model, or a configuration object, it produces a struct. Your job is to evaluate whether the fields, tags, and pointer/value decisions make sense.

Defining and creating structs

type User struct {
    ID       int
    Username string
    Email    string
    IsActive bool
}

// Named field initialization - always use this
alice := User{
    ID:       1,
    Username: "alice",
    Email:    "alice@example.com",
    IsActive: true,
}

// Positional - fragile, breaks when fields are reordered
bob := User{2, "bob", "bob@example.com", true}
Initialization styleReadabilityBreaks on reorder?Use it?
Named fields User{Name: "x"}ClearNoYes, always
Positional User{"x", 1}OpaqueYesNo, except tiny structs
Zero value User{} or var u UserObviousNoWhen defaults are fine
Pointer &User{Name: "x"}ClearNoWhen you need a pointer
AI pitfall
AI frequently generates positional struct initialization for structs with many fields. This compiles, but any future field reordering or insertion silently assigns wrong values to wrong fields. Always rewrite positional initialization to named fields when reviewing AI output.
02

Structs are value types

This is the single most important thing to understand. When you assign a struct to another variable or pass it to a function, Go copies every field.

type Point struct {
    X, Y int
}

p1 := Point{X: 10, Y: 20}
p2 := p1       // copy
p2.X = 999
fmt.Println(p1) // {10 20} - unchanged

In functions:

func resetPoint(p Point) {
    p.X = 0
    p.Y = 0
    // This modifies a copy - caller's Point is unchanged
}

func resetPointPtr(p *Point) {
    p.X = 0
    p.Y = 0
    // This modifies the original through the pointer
}
AI pitfall
AI-generated code sometimes passes large structs by value to functions that are supposed to modify them. The function runs, modifies its local copy, and returns, but the original struct is unchanged. This is especially sneaky because there's no compiler error. Look for functions that take a struct (not *struct) parameter and modify its fields.
03

Exported vs unexported fields

Go uses capitalization for visibility:

type Config struct {
    Host     string  // exported - visible outside the package
    Port     int     // exported
    apiKey   string  // unexported - only visible within this package
    timeout  int     // unexported
}
First letterVisibilityJSON default behavior
Uppercase (Host)Exported, accessible from other packagesMarshaled
Lowercase (host)Unexported, package-privateIgnored by encoding/json
AI pitfall
AI sometimes generates structs with lowercase field names for JSON models. These fields are invisible to encoding/json, they won't marshal or unmarshal. If the AI-generated struct produces empty JSON or silently drops fields, check the capitalization.
04

Struct tags

Tags are string metadata on struct fields that libraries read at runtimeWhat is runtime?The environment that runs your code after it's written. Some languages need a runtime installed on the machine; others (like Go) bake it into the binary. via reflection. The most common use is JSONWhat is json?A text format for exchanging data between systems. It uses key-value pairs and arrays, and every programming language can read and write it. mapping.

type APIResponse struct {
    UserID    int       `json:"user_id"`
    FullName  string    `json:"full_name"`
    Email     string    `json:"email,omitempty"`
    Password  string    `json:"-"`
    CreatedAt time.Time `json:"created_at" db:"created_at"`
}

JSON tag options

TagEffectWhen to use
json:"name"Maps Go field to JSON key nameAlmost always, Go uses PascalCase, JSON uses snake_case
json:"-"Field is completely excluded from JSONPasswords, internal IDs, secrets
json:"name,omitempty"Excluded if zero valueOptional fields in API responses
json:",string"Encodes number as JSON stringWhen API consumers expect string numbers
user := APIResponse{
    UserID:   1,
    FullName: "Alice",
    Password: "secret123",
    // Email is zero value, IsActive is false
}
data, _ := json.Marshal(user)
// {"user_id":1,"full_name":"Alice","created_at":"0001-01-01T00:00:00Z"}
// Password excluded (-), Email excluded (omitempty + zero value)
AI pitfall
AI frequently generates struct tags with typos, josn instead of json, mismatched backticks, or missing commas. Go won't warn you. The tag is silently ignored, and your JSON output uses Go's default field names (UserID instead of user_id). Always verify the marshaled output matches what you expect.
05

Struct embedding (composition)

Go doesn't have inheritance. Instead, you embed one struct inside another. The embedded struct's fields and methods are "promoted", accessible directly on the outer struct.

type Timestamps struct {
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

type User struct {
    Timestamps          // embedded - fields promoted
    ID       int    `json:"id"`
    Username string `json:"username"`
}

u := User{
    Timestamps: Timestamps{
        CreatedAt: time.Now(),
    },
    ID:       1,
    Username: "alice",
}

// Access promoted fields directly
fmt.Println(u.CreatedAt)
// Also accessible via the embedded type
fmt.Println(u.Timestamps.CreatedAt)

Embedding vs named fields

ApproachSyntaxField accessUse case
EmbeddingTimestamps (no field name)u.CreatedAt (promoted)Shared field groups, method promotion
Named fieldStamps Timestampsu.Stamps.CreatedAtWhen you want explicit namespacing

Embedding conflict: shadowing

When two embedded structs have fields with the same name, neither is promoted, you must access them explicitly:

type A struct {
    Name string
}
type B struct {
    Name string
}
type C struct {
    A
    B
}

c := C{}
// c.Name       // compile error: ambiguous selector
c.A.Name = "a"  // explicit - works
c.B.Name = "b"  // explicit - works
AI pitfall
AI generates struct embedding to "inherit" methods, but it doesn't check for field name conflicts between embedded types. If two embedded structs share a field name, the code compiles but direct access fails. This is especially common when embedding multiple third-party types.
06

Constructor pattern

Go doesn't have constructors. The convention is a New function that returns a pointer:

type Server struct {
    host    string
    port    int
    routes  map[string]Handler
    logger  *log.Logger
}

func NewServer(host string, port int) *Server {
    return &Server{
        host:   host,
        port:   port,
        routes: make(map[string]Handler), // initialize the map!
        logger: log.Default(),
    }
}
The New function is critical because it initializes internal state (maps, channels, mutexes) that would panic if left at zero value. When AI generates a struct with map or channel fields but no constructor, that's a bug waiting to happen.
07

Comparing structs

Structs are comparable (with ==) if all their fields are comparable:

type Point struct {
    X, Y int
}
p1 := Point{1, 2}
p2 := Point{1, 2}
fmt.Println(p1 == p2) // true

// Structs with slices or maps are NOT comparable
type Data struct {
    Values []int
}
// d1 == d2  // compile error

If your struct has slice or map fields, you need reflect.DeepEqual() or a custom comparison function.