Go, by example
Go by example - annotated code snippets covering the language from first principles to production.
85 lessons ยท Updated 2026-04-26
- 01Hello, WorldThe smallest runnable Go program - package main, the main function, and fmt.Println.
- 02ValuesGo's built-in scalar types - bool, integer variants, float32/float64, byte, rune, and string.
- 03Variablesvar declarations, short variable declaration :=, zero values, and multiple assignment.
- 04Constantsconst, iota, typed vs untyped constants, and constant expressions evaluated at compile time.
- 05ForGo's single loop construct - the for statement covers basic loops, while-style loops, infinite loops, and range iteration.
- 06If/ElseConditional branching with if, else if, and else - including the idiomatic init-statement form used for error checks.
- 07SwitchSwitch expressions with no fall-through, multi-value cases, init statements, and type switches for interface dispatch.
- 08ArraysFixed-length typed arrays - declaration, zero initialisation, array literals, comparison, and multi-dimensional arrays.
- 09SlicesDynamic sequences built on arrays - make, literals, append, copy, sub-slicing, and the len vs cap distinction.
- 10MapsKey-value stores with make, literals, get/set/delete, the comma-ok idiom, and range iteration.
- 11FunctionsDeclaration, multiple return values, named return values, variadic functions, and functions as first-class values.
- 12ClosuresFunctions that capture and retain their lexical environment - the factory pattern and the goroutine-loop closure pitfall.
- 13PointersThe & and * operators, pointer to struct, nil pointer, and the pointer vs value semantics decision.
- 14StructsStruct definition, field access, struct literals, anonymous structs, embedding, and promoted fields.
- 15MethodsMethod declaration, pointer vs value receivers, method sets, and implicit interface satisfaction.
- 16Multiple Return ValuesGo functions can return more than one value - the canonical pattern is returning a result alongside an error.
- 17Variadic FunctionsA variadic function accepts a variable number of arguments via the ...T parameter - the basis of fmt.Println, append, and ergonomic APIs.
- 18RecursionFunctions that call themselves - Go supports recursion with automatic goroutine stack growth, though deep trees are better handled with an explicit stack.
- 19Range over Built-in TypesThe range keyword iterates over slices, maps, strings, channels, and integers - each with different iteration semantics worth knowing.
- 20Strings and RunesGo strings are immutable byte slices encoded in UTF-8. A rune is a Unicode code point (int32) - the distinction matters on any non-ASCII input.
- 21InterfacesGo interfaces are satisfied implicitly - any type that implements the required methods satisfies the interface, no declaration needed.
- 22EnumsGo has no built-in enum type - idiomatic enums use typed integer constants with iota to generate sequential values.
- 23Struct EmbeddingGo uses struct embedding instead of inheritance - embedding promotes another type's fields and methods to the outer struct.
- 24GenericsGo 1.18 added generics - type parameters let you write functions and types that work across multiple types without losing type safety.
- 25Range over IteratorsGo 1.23 allows range to iterate over functions - enabling lazy, composable iterators without allocating intermediate slices.
- 26Errorserrors.New, fmt.Errorf with %w, errors.Is, errors.As, and sentinel errors - Go errors are values.
- 27Custom ErrorsImplementing the error interface with a struct to carry structured context callers can extract.
- 28GoroutinesLightweight concurrent functions launched with the go keyword - the foundation of Go's concurrency model.
- 29ChannelsTyped conduits for sending values between goroutines - channels communicate and synchronise.
- 30Channel BufferingBuffered channels accept sends without a waiting receiver - a bounded queue between goroutines.
- 31Channel SynchronizationUse channels as done signals to wait for goroutines to finish before continuing.
- 32Channel DirectionsRestrict channels to send-only or receive-only in function signatures to document and enforce data flow.
- 33SelectWait on multiple channel operations simultaneously - select proceeds with whichever case is ready first.
- 34TimeoutsBound how long you wait for a channel operation using time.After or context.WithTimeout inside a select.
- 35Non-Blocking Channel OperationsUse select with a default case to attempt a channel send or receive without blocking.
- 36Closing ChannelsClose a channel to broadcast completion; only senders should close.
- 37Range over ChannelsIterate over channel values until the channel is closed using a for-range loop.
- 38TimersSchedule a one-shot event with time.NewTimer and cancel it early with Stop.
- 39TickersFire repeatedly at a fixed interval with time.NewTicker; stop it to release resources.
- 40Worker PoolsFan out work across N goroutines reading from a shared jobs channel to bound concurrency.
- 41WaitGroupsUse sync.WaitGroup to wait for a collection of goroutines to finish.
- 42Rate LimitingControl the rate of operations using tickers and the golang.org/x/time/rate token bucket.
- 43Atomic CountersUse sync/atomic for lock-free single-variable operations across goroutines.
- 44MutexesUse sync.Mutex to safely share state across goroutines when atomic operations are not enough.
- 45Stateful GoroutinesConfine mutable state to a single goroutine and expose it through channels to avoid data races by design.
- 46SortingSorting slices with slices.Sort (generic, Go 1.21+), slices.SortFunc for custom order, and sort.Search for binary search.
- 47Sorting by FunctionsCustom sorting with slices.SortFunc, multi-key sorting with cmp.Compare, and stable vs unstable sort.
- 48PanicThe panic built-in and when the runtime panics - programmer errors vs expected failures and the difference from exceptions.
- 49DeferDefer schedules a function call to run when the enclosing function returns - LIFO execution order and idiomatic resource cleanup.
- 50RecoverRecover from panics in deferred functions, re-panicking, and converting panics to errors at package boundaries.
- 51String FunctionsThe strings package - searching, splitting, trimming, replacing, and building strings efficiently with strings.Builder.
- 52String Formattingfmt verbs - %v, %+v, %#v, %T, width and precision, and choosing between Printf, Sprintf, and Fprintf.
- 53Text Templatestext/template and html/template - template actions, custom functions, parsing once at startup, and avoiding XSS with html/template.
- 54Regular Expressionsregexp.Compile, MustCompile, MatchString, FindString, FindAllString, ReplaceAllString, and named capture groups.
- 55JSONjson.Marshal and Unmarshal, struct tags with omitempty, streaming with Encoder/Decoder, custom marshalers, and json.RawMessage for deferred decoding.
- 56XMLencoding/xml Marshal and Unmarshal, struct tags for element and attribute mapping, streaming with Encoder/Decoder, and handling namespaces.
- 57Timetime.Time, time.Now, Duration arithmetic, time.Since and time.Until, Add and Sub, and comparisons with Before, After, and Equal.
- 58EpochUnix epoch seconds and nanoseconds, converting back with time.Unix, and best practices for storing timestamps in databases.
- 59Time Formatting / Parsingtime.Format with Go's reference time, time.Parse, time.RFC3339 and other constants, and location-aware parsing.
- 60Random Numbersmath/rand/v2 for general-purpose randomness, global functions vs local Source, and crypto/rand for cryptographically secure randomness.
- 61Number Parsingstrconv.Atoi and ParseInt for integers, ParseFloat for floats, FormatInt and Itoa for the reverse, and error handling on invalid input.
- 62URL Parsingnet/url.Parse to decompose URLs, url.URL fields for scheme, host, path, and query, url.Values for safe query parameter encoding.
- 63SHA256 Hashescrypto/sha256 for one-shot and streaming SHA-256 hashing, hex encoding the digest, and when not to use SHA256 for passwords.
- 64Base64 Encodingencoding/base64 StdEncoding and URLEncoding for encode/decode, RawURLEncoding for padding-free tokens, and streaming encoders.
- 65Reading Filesos.ReadFile for small files, os.Open with bufio.Scanner for line-by-line reading, the io.Reader interface, and reading from stdin.
- 66Writing Filesos.WriteFile for small writes, os.Create with bufio.Writer for large or streaming writes, os.OpenFile with flags, and fsync for durability.
- 67Line FiltersReading from os.Stdin with bufio.Scanner, writing to os.Stdout, processing lines with strings operations, and the Unix filter pattern.
- 68File Pathsfilepath.Join, filepath.Dir, filepath.Base, filepath.Ext, filepath.Abs, and filepath.WalkDir for directory traversal.
- 69Directoriesos.Mkdir, os.MkdirAll, os.ReadDir, os.Remove, os.RemoveAll, os.Getwd, and os.Chdir for directory operations.
- 70Temporary Files and Directoriesos.CreateTemp and os.MkdirTemp for creating temporary files and directories, deferred cleanup, and t.TempDir in tests.
- 71Embed DirectiveThe //go:embed directive bundles static files and directories into the compiled binary using embed.FS, enabling single-binary deployments.
- 72Testing and BenchmarkingGo's built-in testing package provides TestXxx functions, table-driven tests, BenchmarkXxx functions, and the testdata directory convention without external frameworks.
- 73Command-Line Argumentsos.Args provides raw access to command-line arguments as a string slice, with os.Args[0] holding the binary name and os.Args[1:] holding the user-supplied arguments.
- 74Command-Line FlagsThe flag package parses named flags (--name=value or --name value), generates help text automatically, and provides typed accessors for string, int, bool, and duration values.
- 75Command-Line Subcommandsflag.NewFlagSet creates a per-subcommand flag set, enabling tools with subcommands like "git commit" or "docker run" where each subcommand has its own flags.
- 76Environment Variablesos.Getenv reads env vars; os.LookupEnv distinguishes "not set" from "set to empty string" - critical for required config validation at startup.
- 77Logginglog/slog (Go 1.21) provides structured logging with JSON output for production and human-readable text output for development - attach request-scoped fields with slog.With.
- 78HTTP ClientAlways set Timeout on http.Client - the zero-value client has no timeout and holds goroutines open indefinitely when a downstream server is slow or unresponsive.
- 79HTTP ServerUse http.Server explicitly with ReadTimeout, WriteTimeout, and IdleTimeout - the zero-value server has no timeouts and is vulnerable to slowloris and connection exhaustion.
- 80TCP Servernet.Listen + Accept in a loop; handle each net.Conn in a goroutine and set deadlines - without them a stalled client holds a goroutine open for the process lifetime.
- 81Contextcontext.WithCancel / WithTimeout / WithDeadline propagate cancellation and deadlines through the call stack - pass context as the first argument to any function that does I/O.
- 82Spawning Processesexec.Command runs a subprocess - use separate string arguments, not shell interpolation, to prevent command injection from user-supplied input.
- 83Exec'ing Processessyscall.Exec (or unix.Exec) replaces the current process image entirely - deferred functions never run, so use it only when you intentionally want to hand off to another binary.
- 84Signalssignal.NotifyContext ties a context to SIGINT/SIGTERM - cancel it to propagate graceful shutdown to every goroutine that respects context cancellation.
- 85ExitPrefer returning an error from main over os.Exit so deferred cleanup runs -- use non-zero exit codes consistently because CI/CD pipelines and shell scripts rely on them.
Enjoyed this? Get more essays on software craft delivered to your inbox.
Subscribe free