Road Go Ever On

An original Go-language curriculum, from fundamentals through concurrency, tooling, and idiomatic production Go.

21 parts · written by Pratik Dhanave. Start with Part 1 →

← All series · All posts

Part 2 · ·10 min read

Types, Values, and Zero Values

How Go's type system actually behaves — predeclared types, the zero-value guarantee that removes a whole class of null bugs, the "no implicit conversions" rule and why it exists, and the difference between a named type and a mere alias.

How Go's type system actually behaves — predeclared types, the zero-value guarantee that removes a whole class of null bugs, the "no implicit conversions" rule and why it exists, and the difference between...

Part 3 · ·12 min read

Variables, Constants, and iota

How Go's declaration forms, scope rules, and its unusual constant system fit together — including the untyped-constant model that makes numeric literals feel effortless, and the `iota` patterns that turn enums and bit-flags into a few tidy lines.

How Go's declaration forms, scope rules, and its unusual constant system fit together — including the untyped-constant model that makes numeric literals feel effortless, and the `iota` patterns that turn...

Part 4 · ·12 min read

Control Flow and defer

Go's control flow is deliberately small — one loop keyword, a switch that doesn't fall through, an `if` that can scope its own variable — and then there's `defer`, the one construct that repays close reading. A tour of the whole surface, with the sharp edges labelled.

Go's control flow is deliberately small — one loop keyword, a switch that doesn't fall through, an `if` that can scope its own variable — and then there's `defer`, the one construct that repays close...

Part 5 · ·12 min read

Functions, Closures, and Variadics

How Go treats functions as ordinary values — and what that buys you: the (result, error) idiom, variadic APIs, closures over shared state, and the decorator/middleware/option patterns that fall out of passing functions around.

How Go treats functions as ordinary values — and what that buys you: the (result, error) idiom, variadic APIs, closures over shared state, and the decorator/middleware/option patterns that fall out of...

Part 6 · ·11 min read

Pointers and Addressability

What a Go pointer actually is, why there's no pointer arithmetic, `new(T)` versus `&T{}`, the addressability rules that decide what `&` will even compile against, and when reaching for a pointer helps versus when it just adds indirection and GC pressure.

What a Go pointer actually is, why there's no pointer arithmetic, `new(T)` versus `&T{}`, the addressability rules that decide what `&` will even compile against, and when reaching for a pointer helps...

Part 7 · ·12 min read

Strings, Runes, Bytes, and UTF-8

What a Go string actually is under the hood — an immutable read-only slice of bytes, not a sequence of characters — and how bytes, runes, and code points relate, so you stop shipping the classic multibyte bugs.

What a Go string actually is under the hood — an immutable read-only slice of bytes, not a sequence of characters — and how bytes, runes, and code points relate, so you stop shipping the classic multibyte bugs.

Part 8 · ·12 min read

Arrays and Slices

Why an array is a value and a slice is a view — the three-word header, how `append` really grows, the aliasing trap that silently corrupts data, and the small habits (three-index slices, `copy`, pre-sizing) that keep it from biting you.

Why an array is a value and a slice is a view — the three-word header, how `append` really grows, the aliasing trap that silently corrupts data, and the small habits (three-index slices, `copy`, pre-sizing)...

Part 9 · ·13 min read

Maps

How Go's built-in hash table really behaves — reference semantics, the nil-write panic, comma-ok, randomized iteration, why `&m[k]` is illegal, and the presizing and concurrency rules that separate correct map code from the code that bites you at 2 a.m.

How Go's built-in hash table really behaves — reference semantics, the nil-write panic, comma-ok, randomized iteration, why `&m[k]` is illegal, and the presizing and concurrency rules that separate correct...

Part 10 · ·13 min read

Structs, Embedding, and Tags

How Go builds aggregate types from value semantics up — why a struct is a copy, when it stops being comparable, what embedding actually promotes (and what it deliberately doesn't), and how a backtick string in a field definition ends up steering `encoding/json`.

How Go builds aggregate types from value semantics up — why a struct is a copy, when it stops being comparable, what embedding actually promotes (and what it deliberately doesn't), and how a backtick string...

Part 11 · ·12 min read

Methods and Receivers

How Go attaches behavior to types without classes — the receiver, the value-versus-pointer decision, method sets and what they mean for interfaces, and the addressability rules that trip people up when a value lives in a map.

How Go attaches behavior to types without classes — the receiver, the value-versus-pointer decision, method sets and what they mean for interfaces, and the addressability rules that trip people up when a...

Part 12 · ·13 min read

Interfaces

How Go turns "what a value can do" into a first-class type — with implicit satisfaction, small contracts, the consumer-defined-interface rule, and the typed-nil trap that catches everyone once.

How Go turns "what a value can do" into a first-class type — with implicit satisfaction, small contracts, the consumer-defined-interface rule, and the typed-nil trap that catches everyone once.

Part 13 · ·11 min read

Type Assertions, Type Switches, and Interface Internals

How Go recovers a concrete type from an interface value, why the comma-ok form exists, and the two-word memory layout that explains the single most surprising bug in the language — the non-nil interface holding a nil pointer.

How Go recovers a concrete type from an interface value, why the comma-ok form exists, and the two-word memory layout that explains the single most surprising bug in the language — the non-nil interface...

Part 14 · ·11 min read

Generics: Type Parameters and Constraints

How type parameters and constraints actually work in Go 1.18+ — writing functions and data structures that are type-safe across many types, when the compiler can infer type arguments for you, and the harder question of when a plain interface is still the better tool.

How type parameters and constraints actually work in Go 1.18+ — writing functions and data structures that are type-safe across many types, when the compiler can infer type arguments for you, and the harder...

Part 15 · ·13 min read

Errors, Wrapping, and errors.Is / errors.As

Go treats errors as ordinary values, not exceptions — which means everything you know about passing, comparing, and inspecting values applies. This is a working guide to sentinel errors, wrapping with %w, and the two verbs that make error chains navigable: errors.Is and errors.As.

Go treats errors as ordinary values, not exceptions — which means everything you know about passing, comparing, and inspecting values applies. This is a working guide to sentinel errors, wrapping with %w,...

Part 16 · ·11 min read

panic, recover, and defer's Role

Go handles ordinary failure with values, not exceptions — so what are panic and recover actually for? A working guide to how panic unwinds the stack through your defers, why recover only fires inside a deferred function, and the narrow set of places where catching a panic is the right call rather than a code smell.

Go handles ordinary failure with values, not exceptions — so what are panic and recover actually for? A working guide to how panic unwinds the stack through your defers, why recover only fires inside a...

Part 17 · ·11 min read

Goroutines

What a goroutine actually is, why it is cheaper than a thread, and the one rule that separates working concurrent code from a program that quietly leaks itself to death: every goroutine you start must have a way to stop.

What a goroutine actually is, why it is cheaper than a thread, and the one rule that separates working concurrent code from a program that quietly leaks itself to death: every goroutine you start must have...

Part 18 · ·12 min read

Channels and select

A working guide to Go's channels — unbuffered rendezvous vs buffered capacity, send/receive/close semantics, directional types in APIs, and how `select` multiplexes, times out, and disables cases with a nil channel.

A working guide to Go's channels — unbuffered rendezvous vs buffered capacity, send/receive/close semantics, directional types in APIs, and how `select` multiplexes, times out, and disables cases with a nil...

Part 19 · ·12 min read

The sync Package

When channels are the wrong tool — a working guide to shared memory and locks in Go: Mutex, RWMutex, WaitGroup, Once, Cond, Map, Pool, and the race detector that keeps you honest.

When channels are the wrong tool — a working guide to shared memory and locks in Go: Mutex, RWMutex, WaitGroup, Once, Cond, Map, Pool, and the race detector that keeps you honest.

Part 20 · ·13 min read

Atomics and the Go Memory Model

What a data race actually is, why it is undefined behavior rather than "just a wrong number," the happens-before rules that make concurrent code correct, and when `sync/atomic` is the right tool — and when it quietly is not.

What a data race actually is, why it is undefined behavior rather than "just a wrong number," the happens-before rules that make concurrent code correct, and when `sync/atomic` is the right tool — and when...

Part 21 · ·11 min read

context: Cancellation, Deadlines, and Values

How Go's `context` package carries a cancellation signal, a deadline, and a small bag of request-scoped values across every API and goroutine boundary in a request — and the handful of rules that keep it from leaking or lying to you.

How Go's `context` package carries a cancellation signal, a deadline, and a small bag of request-scoped values across every API and goroutine boundary in a request — and the handful of rules that keep it...

This series is part of a larger body of work by Pratik Dhanave, an Agentic AI Architect writing about production AI systems, distributed systems, and cloud-native engineering. Explore all course series, browse every post, or find topics via the tag index.