Go — Reference
Source: https://go.dev/doc/
Go
- Created: 2009 by Robert Griesemer, Rob Pike, Ken Thompson at Google
- Latest stable: Go 1.26.2 (April 2026); Go 1.26.0 released 2026-02-10
- Paradigms: imperative, concurrent, structurally-typed, lightly object-oriented (no inheritance), generic (since 1.18)
- Typing: static, strong, structural for interfaces, type inference at declaration
- Memory: GC (concurrent low-latency tri-color mark-sweep, sub-ms STW)
- Compilation: AOT to single static native binary; very fast compile times; no runtime dependency on libc by default
- Primary domains: backend services, microservices, CLI tools, infrastructure (Docker, Kubernetes, Terraform), networking, observability, build tooling
- Official docs: https://go.dev/doc/
1. At a glance
- Stewarded by the Go team at Google, governed by the open-source proposal process on GitHub.
- 6-month release cadence (February + August). Go 1.x is stable: the Go 1 compatibility promise means working code keeps compiling.
- One implementation:
gc(the official compiler).gccgoexists but is not actively developed at the same pace. TinyGo for embedded/WASM. - Cross-compilation is built in:
GOOS=linux GOARCH=arm64 go build.
2. Getting started
-
Install: Official tarball/installer at https://go.dev/dl/. Linux: extract to
/usr/local/go, add toPATH. macOS:brew install go. Windows:winget install GoLang.Goor MSI. -
Version manager: none official.
g(https://github.com/stefanmaric/g),gvm,asdf. Orgo install golang.org/dl/go1.26@latest && go1.26 download. -
Hello, world:
package main import "fmt" func main() { fmt.Println("Hello, world!") }Run:
go run .orgo build -o hello && ./hello. -
Project layout:
go.modat module root;cmd/<name>/main.gofor binaries,internal/for non-exported packages,pkg/(rare) for reusable libs. -
Build tool:
goCLI is everything:go build,go test,go run,go install,go mod,go work,go vet,go fmt,go generate,go tool pprof,go tool trace. -
REPL/playground: The Go Playground (https://go.dev/play/) — sandboxed, sharable.
gore(community REPL).
3. Basics
- Primitives:
bool,string,int/int8/int16/int32/int64(intis platform width — 64-bit on modern systems),uint*,uintptr,byte(=uint8),rune(=int32Unicode codepoint),float32/float64,complex64/complex128. Nochartype; iterate strings withfor i, r := range sto getrunes. Literals:0x,0b,0o,_separators (Go 1.13+), backtick raw strings (no escapes; can include newlines). - Variables:
var x int = 1, short declarationx := 1(function scope only,:=requires at least one new var on LHS),const(compile-time, untyped by default),iotafor enum-like sequences (const (A = iota; B; C)gives 0, 1, 2). Block scope. No unused imports/variables (compile error — use_to discard, or_ = xto keep). Zero-value default: numeric0, boolfalse, string"", slice/map/chan/func/pointernil. - Control flow:
if/else(with init:if v, err := f(); err == nil { ... }—vanderrare if-scoped),switch(no fallthrough by default;switch x.(type)for type switch on interface values; tag-lessswitch { case cond: }replaces long if/else-if chains),for(only loop keyword:for i := 0; i < n; i++,for cond,for { }infinite,for k, v := range coll,for i := range 10since 1.22 ranges int),goto(rare; permitted),defer fn()(LIFO, runs on function return).rangeover functions (Go 1.23+) for custom iterators viaiter.Seq[V]/iter.Seq2[K,V]. - Functions: multiple return values (
func f() (int, error)), named returns (func f() (v int, err error) { ... return }returns named vars), variadicfunc f(xs ...int)(call withf(1,2,3)orf(slice...)), closures (capture-by-reference), first-class. Methods are functions with a receiver:func (r Rect) Area() float64(value receiver — copies) vsfunc (r *Rect) Scale(f float64)(pointer receiver — mutates). Interface methods can be value- or pointer-receiver. - Strings: immutable UTF-8 byte sequences (stored as 2-word header: pointer + length). Indexing
s[i]returns abyte;for i, r := range siterates runes (iis byte offset). No interpolation; usefmt.Sprintf("%v %v", a, b)orfmt.Sprintf("%d-%s", n, name).strings.Builderfor efficient O(n) concat (avoids quadratic copying). Convert:string([]byte{...}),[]byte(s),[]rune(s). UTF-8 utilities:unicode/utf8,unicode. - Collections: arrays
[N]T(fixed size, value type — copied on assignment), slices[]T(the workhorse — pointer + len + cap header, lightweight reference into a backing array), mapsmap[K]V(hash table, randomized iteration order), channelschan T(typed FIFO with optional buffer). No tuples (use multiple returns or structs). No sets (idiomatic:map[T]struct{}{}— zero-byte value). No generics on methods (top-level funcs + type defs only as of 1.26).len(),cap(),make(),new(),append(),copy(),delete()are builtins.
4. Intermediate
-
Generics (1.18+):
func Map[T, U any](s []T, f func(T) U) []U. Type sets via interfaces with~int | ~float64(the~allows defined types liketype MyInt int).anyis alias forinterface{}(1.18+).comparablebuilt-in constraint covers types usable as map keys.golang.org/x/exp/constraintsprovidesOrdered,Integer,Float, etc. Generic type aliases (1.24+):type Set[T comparable] = map[T]struct{}. -
Modules/packages:
go modis the dependency system. Module = repo +go.mod(declares module path + Go version + dependencies); package = directory of.gofiles with samepackagedecl. Workspaces (go.work, 1.18+) for multi-module local development. Module graph:go mod graph. Minimum-version selection (MVS) — go picks the minimum version satisfying constraints, not the maximum like SemVer-resolvers in npm/Cargo.go.sumrecords cryptographic hashes; verified against GOSUMDB (sum.golang.org by default).GOPRIVATE/GONOPROXY/GONOSUMDBto bypass for private modules. -
Error handling: errors are values (
type error interface { Error() string }). Convention:func f() (T, error); checkif err != nil.errors.Is(err, sentinel)/errors.As(err, &target)for wrapping (Go 1.13+);fmt.Errorf("loading config: %w", err)to wrap with attached context.panic/recoverfor unrecoverable / library-boundary recovery; not for control flow.errors.Join(1.20+) combines multiple errors into one.func work() error { cfg, err := loadConfig() if err != nil { return fmt.Errorf("loading config: %w", err) } return process(cfg) } // Caller: if err := work(); err != nil { var nf *NotFoundError if errors.As(err, &nf) { /* typed handling */ } log.Printf("work failed: %v", err) } -
Concurrency: goroutines (
go f()— multiplexed onto OS threads by GMP scheduler; 8 KiB initial stack, grows on demand), channels (ch := make(chan T)unbuffered,make(chan T, 16)buffered,<-chsend/receive,close(ch)signal end; receiving from a closed channel returns zero value +ok=false),selectfor multiplexing (case <-ch:/case ch <- x:/case <-ctx.Done():/default:),sync.WaitGroup(counter for fan-out/in),sync.Mutex/RWMutex,sync.Once(lazy init),sync.Map(specialized — usually a plainmap + mutexis better),sync/atomic(typedatomic.Int64/Pointer[T]since 1.19),context.Contextfor deadlines / cancellation / request-scoped values. Higher-level:golang.org/x/sync/errgroupfor fan-out with first-error cancellation,golang.org/x/sync/semaphorefor weighted concurrency limits,uber-go/ratelimitfor leaky-bucket rate limiting. -
I/O & networking:
os,io,bufio(always wrap rawio.Reader/Writerfor line-oriented or hot-path code),io/fs(1.16+ — abstractFSinterface, basis forembed.FS),os/exec(subprocesses),net(TCP/UDP/Unix sockets),net/http(production-grade server + client in stdlib),crypto/tls,database/sql.net/http.ServeMuxgot pattern matching with method + host + path wildcards in 1.22 (mux.HandleFunc("POST /users/{id}", h)). Context propagation: every long-running call should accept and checkctx.Done(). -
Stdlib highlights:
context(deadlines / cancellation propagation; idiomatic first arg),encoding/json(slow but ubiquitous —json.RawMessagefor lazy parsing; struct tagsjson:"name,omitempty"control marshal),encoding/json/v2(1.25+ experimental — faster, cleaner API, opt-in),time(time.Timewith monotonic clock;time.Tick,time.After,time.NewTicker),log/slog(structured logging, 1.21+ — JSON or text handlers;slog.Info("msg", "key", val)),slices/maps/cmp(1.21+ — generic utilities for slice/map ops),iter(1.23+ —iter.Seq[V]/iter.Seq2[K,V]),crypto/*(constant-time comparison, AEAD ciphers, ed25519),regexp(RE2-based, no PCRE backreferences),text/template/html/template(HTML version is context-aware-auto-escaping),embed(1.16+ —//go:embed assets/*bundles files into binary),testing(t.Runsubtests,t.Parallel(), table-driven idioms).
5. Advanced
- Memory & GC: generational-ish concurrent tri-color mark-sweep with write barrier; goal is sub-millisecond stop-the-world (typical pauses ~100μs-1ms in production). Tune via
GOGC(target heap growth %, default 100 — meaning collect when heap doubles since last GC),GOMEMLIMIT(1.19+, soft memory ceiling — the runtime adjusts GC aggression to stay under).runtime/debug.SetGCPercent,SetMemoryLimit. GC pacer algorithm fully rewritten in 1.18 (Knuth’s exponentially weighted heap-growth model). The runtime returns memory to the OS viamadvise(MADV_DONTNEED)(Linux) /MEM_DECOMMIT(Windows) after idle periods. - Concurrency deep dive: GMP scheduler — Goroutines (G) multiplexed onto Logical Processors (P, defaults to
NumCPU), executed on OS threads (M). Work-stealing across P-local run queues + global queue.runtime.GOMAXPROCScontrols P count (auto-respects cgroup CPU quotas since 1.25). Goroutines start with 8 KiB stack, grow as needed (was segmented stacks pre-1.3, now contiguous with copy-on-grow).runtime.LockOSThreadfor cgo / GUI loops. Asynchronous preemption (signal-based, 1.14+) prevents loop-without-function-call from monopolizing a P. Race detector:go run -race(built on ThreadSanitizer, 5-10x slowdown but invaluable in CI).goleak(Uber) detects leaked goroutines in tests. - FFI/interop: cgo (
import "C"+// #include <foo.h>comment). Crossing the cgo boundary is expensive (~150-200 ns each way pre-1.21, ~80 ns post optimizations) and breaks goroutine scheduling on that thread (the M is parked). Avoid in hot loops.go:linknameto access unexported symbols (1.22 added restrictions),go:noescapeto lie about pointer escape,go:nosplitto skip stack growth check (kernel/runtime only). WebAssembly target:GOOS=js GOARCH=wasmfor browsers,GOOS=wasip1 GOARCH=wasm(1.21+, WASI Preview 1) for serverless edge (Fastly Compute, fermyon Spin, wasmtime). Component Model + WASI 0.2 support landing through 2025-26. - Reflection:
reflectpackage —reflect.TypeOf,reflect.ValueOf,Kind(). Used byencoding/json,text/template. Slow (10-100x slower than direct access); codegen viago generateis preferred for hot paths.encoding/json/v2experimental (1.25+) is faster and addresses the long list of v1 footguns; opt-in viaGOEXPERIMENT=jsonv2. - Performance tooling:
pprof(CPU, heap, allocs, block, mutex, goroutine profiles vianet/http/pprofHTTP endpoint),go tool trace(execution traces, see scheduler behavior, GC events, syscalls),go test -bench+testing.B,-race,-cpuprofile/-memprofile/-blockprofile/-mutexprofile,go test -gcflags='-m -m'for escape-analysis output (see what allocates),benchstat(golang.org/x/perf) for statistical benchmark comparison,perf-toolfor Linux perf integration. PGO (1.21+ stable):go build -pgo=default.pgo— auto-picksdefault.pgonext to main package. Typical 2-7% wins, up to 14% on interpreter loops.Continuous Profilingstacks: Pyroscope / Grafana Phlare, Polar Signals, Datadog Continuous Profiler, all support Go pprof natively.
6. God mode
- Assembly: Go’s own asm dialect (Plan 9 derived); per-arch files like
foo_amd64.s. Used inruntime,crypto,math/bigfor SIMD. - Runtime scheduler internals: GMP, run queues (per-P local + global), netpoller (epoll/kqueue/IOCP), sysmon thread, preemption (signal-based since 1.14). Read
runtime/proc.go. - Escape analysis: decides stack vs heap. Inspect with
go build -gcflags='-m -m'. Make values escape (hide intent from compiler) only when necessary. unsafe.Pointerrules: six legal conversion patterns (seeunsafedocs). Anything else is unsafe and may break.- Build tags:
//go:build linux && amd64constraints; file-name suffixes (foo_linux_amd64.go). pluginpackage: Linux/macOS only, ELF/Mach-O dlopen — fragile across Go versions, rarely used in practice.- Generics introspection: limited reflection on type parameters; code-shape-shared instantiation (GCShape) means one binary, runtime dictionary lookup.
- Race detector internals: built on ThreadSanitizer (TSan) with happens-before tracking via vector clocks.
- Compiler directives:
//go:noescape,//go:linkname,//go:noinline,//go:nosplit,//go:nowritebarrier,//go:embed(1.16+),//go:generate. - Profile-guided optimization:
default.pgonext to main package auto-detected (1.21+). Typical 2-7% wins. - Embedding the runtime: Go can be embedded into other languages via
c-shared/c-archivebuild modes (go build -buildmode=c-shared). - TinyGo for microcontrollers / WASM with much smaller runtime; uses LLVM backend.
7. Idioms & style
- Naming:
MixedCaps(PascalCase) for exported,mixedCapsfor unexported. Acronyms uppercase:URL,ID,HTTP. Short receiver names (r *Rect). Package names lowercase, single word, no underscores. - Formatter:
gofmt(andgoimports) — non-negotiable; CI rejects unformatted code. Linter:go vet(built-in),staticcheck(the canonical extra linter),golangci-lint(aggregator). - Idiomatic Go: “accept interfaces, return structs”; small interfaces (often single-method like
io.Reader); errors are values, handle them at every layer; one-letter-ish receiver names; explicitif err != nil; favor composition over inheritance (Go has no inheritance); use channels to share memory by communicating, not memory to share state. Effective Go is the canonical doc. - Reviewers look for: unhandled errors, mutex without
defer Unlock, goroutine leaks (no cancellation), unbuffered channel deadlocks,context.Contextnot first param, package-level mutable state, premature abstraction.
8. Ecosystem
- Web: stdlib
net/http(production-grade; got method+host routing in 1.22). Higher-level: Gin (most popular, fast), Echo, Fiber (fasthttp-based, fastest), Chi (idiomatic, stdlib-style), Gorilla (originally archived 2022, revived as community project), Huma (OpenAPI-first), Encore (full-stack framework with built-in infra), Buffalo, Iris, Beego, Revel. gRPC-Go,connectrpc/connect-go(Bufbuild — schema-first, gRPC/Connect/JSON), Twirp (Twitch), Goa (design-first). - DB:
database/sql+ drivers (pgx— Postgres native, faster than database/sql wrapper for hot paths;go-sql-driver/mysql;mattn/go-sqlite3;microsoft/go-mssqldb). ORMs: GORM (most popular), sqlc (codegen from SQL — type-safe, idiomatic, generates per-query funcs), ent (Facebook, graph schema), bun (ex-go-pg author), Pop (Buffalo), sqlboiler (codegen from DB schema), squirrel (query builder). - Migration: golang-migrate, goose, atlas (HCL/SQL declarative).
- CLI: Cobra (kubectl/hugo standard, GitHub CLI uses it), Viper (config — env, file, flags merged), urfave/cli, kong, mitchellh/cli, kingpin.
- Logging/observability:
log/slog(stdlib structured logging, 1.21+), uber-go/zap (fastest), zerolog, logrus (deprecated). OpenTelemetry-Go, Prometheus client lib, Datadog APM, Honeycomb beeline. - Cloud/infra (Go’s empire): Kubernetes, Terraform, Docker, containerd, etcd, CoreDNS, Prometheus, Grafana Loki/Mimir/Tempo, Cilium (eBPF networking), Istio, Linkerd, HashiCorp Vault, Consul, Nomad, Boundary, Caddy (HTTPS-by-default web server), Traefik, Helm, ArgoCD, Flux, Tekton, Buildkite agent, Drone CI, GoReleaser, Pulumi (Go also a target language), Buf (Protobuf tooling), gRPC Go server, NATS, MinIO, eBPF tooling (Cilium/Tetragon). Roughly 60-70% of CNCF graduated projects are written in Go.
- Testing: stdlib
testing+testing/quick; testify (assertions/mocks, near-ubiquitous), gomock (replaces deprecatedgolang/mock), counterfeiter, gotestsum (nicer output + JUnit XML), gomega/ginkgo (BDD), rapid (property-based), go-cmp (Google, structured diff), sqlmock, httptest (stdlib HTTP fakes), mockery, Testcontainers-Go. - Performance/tracing:
pprof(stdlib + UI ingo tool pprof),go tool trace,delvedebugger (dlv— IDE integration via DAP), objdump/nm/addr2line (go tool), benchstat (statistical bench compare), flamegraph.pl + pprof. - Code generation / interop: buf (Protobuf), stringer (enum String()), wire (Google, compile-time DI), mockgen, swag (Swagger), goa, gqlgen (GraphQL server), rod/chromedp (headless Chrome).
- Embedded / cross-target: TinyGo (LLVM-based, microcontrollers, WASM with much smaller runtime), Gobot (robotics).
- Doc tools:
go doc+ pkg.go.dev (auto-published from Git tags, indexed via the Go module proxy). - Notable users: Google (internal infra, gVisor, Vitess, Bazel), Cloudflare (most internal services), Uber, Twitch, Dropbox (replaced some Python services), Netflix (Spinnaker), MongoDB, Meta, Microsoft (Azure components, GitHub including Actions runner), HashiCorp (entire product line), Snowflake, Stripe, American Express, Riot Games, Twitter, Datadog (agents), New Relic. Used to write 77% of CNCF projects as of 2024.
Concurrency patterns cookbook
// Fan-out, fan-in with errgroup (golang.org/x/sync/errgroup)
g, ctx := errgroup.WithContext(ctx)
results := make([]Result, len(jobs))
for i, job := range jobs {
i, job := i, job // capture; not needed in Go 1.22+ but explicit is fine
g.Go(func() error {
r, err := process(ctx, job)
if err != nil { return err }
results[i] = r
return nil
})
}
if err := g.Wait(); err != nil { return err }
// Concurrency limit with semaphore
sem := semaphore.NewWeighted(8)
for _, job := range jobs {
if err := sem.Acquire(ctx, 1); err != nil { return err }
go func(j Job) {
defer sem.Release(1)
process(j)
}(job)
}
// Pipeline with done-channel cancellation
gen := func(ctx context.Context, nums ...int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for _, n := range nums {
select {
case <-ctx.Done(): return
case out <- n:
}
}
}()
return out
}
// Context propagation
ctx, cancel := context.WithTimeout(parentCtx, 5*time.Second)
defer cancel()
resp, err := http.NewRequestWithContext(ctx, "GET", url, nil)Idiomatic patterns examples
// Constructor returning interface, value-returning struct
type Cache interface { Get(k string) (string, bool); Set(k, v string) }
type memCache struct { mu sync.RWMutex; m map[string]string }
func NewCache() Cache { return &memCache{m: make(map[string]string)} }
// Functional options
type ServerOption func(*Server)
func WithPort(p int) ServerOption { return func(s *Server) { s.port = p } }
func NewServer(opts ...ServerOption) *Server {
s := &Server{port: 8080}
for _, opt := range opts { opt(s) }
return s
}
// HTTP handler with middleware
func logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
slog.Info("req", "method", r.Method, "path", r.URL.Path,
"duration", time.Since(start))
})
}9. Gotchas
nilinterface vs interface holding typed nil:var p *Foo = nil; var i Foo = p; i == nilreturns false. Source of constant bugs.- Loop variable capture (fixed in 1.22): before 1.22,
for i := range xs { go func() { use(i) }() }sharedi. Fixed: each iteration has its own. - Slices share backing arrays:
s2 := s1[2:5]mutatess1. Append may or may not reallocate; rely on returned slice. mapiteration order is randomized; do not depend on order.time.Now()includes monotonic clock; serializing across the wire loses it.- No generics for methods, only top-level functions and type definitions (1.21 still has limits).
deferin a long loop queues N callbacks; can OOM.- Goroutine leaks from forgotten
<-chreceives or missingcontext.Done()selects. init()runs at package load; ordering is per-file alphabetical, then deps; surprising side effects.json.Marshalerrors on cyclic types and unexported fields silently.http.DefaultClienthas no timeout — set one or you’ll wait forever.new(T)vs&T{}: prefer&T{}; same effect, more idiomatic.- Capitalization controls visibility — rename a field from
Nametonameand you’ve broken JSON marshaling and your public API. - Empty
interface{}(nowany) is a runtime escape hatch — once a value entersany, you’ve lost compile-time type info; reflect is the only path back. - Channel direction confusion —
chan T(bidirectional),chan<- T(send-only),<-chan T(receive-only). Functions should take the narrowest channel direction needed. - Slice
lenvscapsurprises —make([]T, 0, 10)gives a length-0, capacity-10 slice;appendwon’t reallocate until you push the 11th element. go buildhonorsGOPROXY/GOSUMDB/GONOPROXY/GOPRIVATEenv vars — setGOPRIVATE=*.internal.corpto skip the public proxy + checksum DB for company-internal modules.time.Afterleaks a goroutine until the timer fires — in select loops, usetime.NewTimer+ manualStop().selectwithdefaultis non-blocking — frequently misused as a busy-loop polling pattern (burns CPU). Use a real timeout case.panicacross goroutines — a panic in goroutine A is not caught byrecoverin goroutine B. Every long-lived goroutine should defer-recover at its top level.
Modern Go (1.21 → 1.26)
| Version | Release | Highlights |
|---|---|---|
| 1.21 (Aug 2023) | LTS-ish | log/slog, slices, maps, cmp packages; PGO GA; min/max built-ins; clear; WASI preview |
| 1.22 (Feb 2024) | Loop variable fix (per-iteration scope); for i := range 10 (int range); net/http enhanced ServeMux (POST /users/{id}); rand/v2 | |
| 1.23 (Aug 2024) | Range-over-func iterators (for v := range myIter); iter pkg with Seq[V] / Seq2[K,V]; unique pkg for interning; telemetry opt-in | |
| 1.24 (Feb 2025) | Generic type aliases; os.Root for FS confinement (no path traversal); weak pointers; tool dependencies in go.mod; FIPS 140-3 module | |
| 1.25 (Aug 2025) | testing/synctest (deterministic concurrency tests); encoding/json/v2 experimental; GOMAXPROCS cgroup-aware default; container-aware GC; new GC experiment (Green Tea) | |
| 1.26 (Feb 2026) | Continued v2 stdlib evolution, PGO improvements, scheduler tweaks |
iter + range-over-func (1.23+) is the most user-visible change since generics — finally lets libraries expose lazy iteration ergonomically:
func Lines(r io.Reader) iter.Seq[string] {
return func(yield func(string) bool) {
sc := bufio.NewScanner(r)
for sc.Scan() {
if !yield(sc.Text()) { return }
}
}
}
for line := range Lines(f) { ... }testing/synctest (1.25+) gives deterministic time control for concurrency tests — no more time.Sleep(50*time.Millisecond) flakiness.
Generics maturity (1.18 → 1.26)
Generics landed in 1.18 (Mar 2022). The 4 years since have smoothed edges:
// Type sets via interfaces
type Ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 |
~string
}
func Max[T Ordered](xs ...T) T {
if len(xs) == 0 { var zero T; return zero }
m := xs[0]
for _, x := range xs[1:] {
if x > m { m = x }
}
return m
}golang.org/x/exp/constraints ships Ordered, Integer, Float, Complex, Signed, Unsigned. The stdlib slices + maps + cmp packages (1.21+) use these throughout.
Known limitations (still in 1.26):
- No generic methods on types (only top-level funcs + type definitions).
- No type-parametric type aliases (until 1.24 — now supported).
comparableconstraint covers most type-set comparison needs but~shows up often.
Testing patterns
Go’s testing package is minimalist. Idiomatic patterns:
// Table-driven tests
func TestParse(t *testing.T) {
cases := []struct {
name string
input string
want int
wantErr bool
}{
{"empty", "", 0, true},
{"int", "42", 42, false},
{"neg", "-7", -7, false},
{"hex", "0x1F", 31, false},
{"junk", "abc", 0, true},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got, err := Parse(c.input)
if (err != nil) != c.wantErr {
t.Fatalf("err = %v, wantErr %v", err, c.wantErr)
}
if got != c.want {
t.Errorf("got %d, want %d", got, c.want)
}
})
}
}
// Parallel subtests
func TestThing(t *testing.T) {
t.Parallel()
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
t.Parallel()
// ...
})
}
}
// Benchmarks
func BenchmarkParse(b *testing.B) {
for b.Loop() { // Go 1.24+: replaces "for i := 0; i < b.N; i++"
_, _ = Parse("12345")
}
}
// Fuzzing (1.18+)
func FuzzParse(f *testing.F) {
f.Add("42"); f.Add("-7"); f.Add("0xFF")
f.Fuzz(func(t *testing.T, s string) {
_, _ = Parse(s) // should never panic
})
}Run: go test ./... -v -race -cover, go test -bench=. -benchmem, go test -fuzz=FuzzParse -fuzztime=30s.
Real code examples
Chi HTTP server with middleware and structured logging
package main
import (
"context"
"encoding/json"
"log/slog"
"net/http"
"os"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
slog.SetDefault(logger)
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Recoverer)
r.Use(middleware.Timeout(15 * time.Second))
r.Get("/users/{id}", getUser)
r.Post("/users", createUser)
srv := &http.Server{
Addr: ":8080",
Handler: r,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
}
slog.Info("listening", "addr", srv.Addr)
if err := srv.ListenAndServe(); err != nil {
slog.Error("server failed", "err", err)
os.Exit(1)
}
}
func getUser(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
user := User{ID: 1, Name: "Alice", Email: "a@x.com"}
_ = id
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(user)
}
func createUser(w http.ResponseWriter, r *http.Request) {
var u User
if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
u.ID = 42
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
_ = json.NewEncoder(w).Encode(u)
}Graceful shutdown with context
func runWithSignal(ctx context.Context, srv *http.Server) error {
ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
defer stop()
errCh := make(chan error, 1)
go func() { errCh <- srv.ListenAndServe() }()
select {
case <-ctx.Done():
slog.Info("shutdown signal received")
sCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
return srv.Shutdown(sCtx)
case err := <-errCh:
return err
}
}Range-over-func iterator (Go 1.23+)
import "iter"
func KVs[K comparable, V any](m map[K]V) iter.Seq2[K, V] {
return func(yield func(K, V) bool) {
for k, v := range m {
if !yield(k, v) { return }
}
}
}
// Caller:
for k, v := range KVs(myMap) {
fmt.Println(k, v)
}Real-world Go service performance (2026)
- A minimal
net/http“hello world” with stdlib mux: ~250k req/s on a single-core box, ~5M on 32 cores. Fiber/fasthttp-based: 1.5-2x faster on hello-world due to avoidingnet/httpallocations.- gRPC-Go: ~100k unary RPC/s/core; streaming much higher.
- Typical containerized microservice: <30 MB resident, <5ms p99 idle, JIT-less so cold-start is instant (~50-100ms incl. TLS).
- Goroutines: spawning 1M is a couple GB of resident memory and feasible; the GMP scheduler stays cool.
- GC pauses: typically <500μs on healthy heaps; with
GOMEMLIMITset close to container limit, allocator backpressure replaces OOM.
Structured logging + error chains (1.21+)
log/slog (Go 1.21) replaced the era of “everyone runs zap/zerolog separately.” The stdlib ships a high-performance structured logger with JSON + text handlers and context.Context propagation.
package main
import (
"context"
"errors"
"fmt"
"log/slog"
"os"
)
type contextKey string
const requestIDKey contextKey = "request_id"
// Custom handler that pulls request_id from ctx into every log line
type ctxHandler struct{ slog.Handler }
func (h ctxHandler) Handle(ctx context.Context, r slog.Record) error {
if v, ok := ctx.Value(requestIDKey).(string); ok {
r.AddAttrs(slog.String("request_id", v))
}
return h.Handler.Handle(ctx, r)
}
var ErrNotFound = errors.New("not found")
type DBError struct { Query string; Err error }
func (e *DBError) Error() string { return fmt.Sprintf("db: %s: %v", e.Query, e.Err) }
func (e *DBError) Unwrap() error { return e.Err }
func loadUser(ctx context.Context, id int) (any, error) {
dbErr := &DBError{Query: "SELECT users", Err: ErrNotFound}
return nil, fmt.Errorf("loadUser(%d): %w", id, dbErr) // %w preserves chain
}
func main() {
h := ctxHandler{slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})}
slog.SetDefault(slog.New(h))
ctx := context.WithValue(context.Background(), requestIDKey, "req-abc-123")
_, err := loadUser(ctx, 42)
if err != nil {
if errors.Is(err, ErrNotFound) { // unwraps chain to find sentinel
slog.WarnContext(ctx, "user not found", "err", err)
}
var dbErr *DBError
if errors.As(err, &dbErr) { // pulls typed error from chain
slog.ErrorContext(ctx, "db failure", "query", dbErr.Query, "err", err)
}
}
combined := errors.Join(ErrNotFound, fmt.Errorf("backup also failed")) // 1.20+
slog.Error("multi-failure", "err", combined)
}slog.Logger.With(...) returns a logger pre-seeded with attrs — per-request or per-component loggers without recomputing fields.
Generics deep-dive — when they help, when they don’t
Generics landed in 1.18; four years in, patterns have converged.
Where generics shine — type-set constraints + parametric helpers:
type SignedInt interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 }
type Numeric interface {
SignedInt | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~float32 | ~float64
}
func Sum[T Numeric](xs []T) T {
var s T
for _, x := range xs { s += x }
return s
}
func Map[T, U any](xs []T, f func(T) U) []U {
out := make([]U, len(xs))
for i, x := range xs { out[i] = f(x) }
return out
}
func GroupBy[K comparable, V any](xs []V, keyFn func(V) K) map[K][]V {
out := map[K][]V{}
for _, x := range xs { out[keyFn(x)] = append(out[keyFn(x)], x) }
return out
}
// Type-parameter inference (1.21+) — call site doesn't need explicit T
doubled := Map([]int{1, 2, 3}, func(n int) int { return n * 2 }) // T, U inferredWhere generics DON’T help — interface satisfaction is cleaner:
// Bad: forcing generics where an interface is obvious
func Print[T fmt.Stringer](x T) { fmt.Println(x.String()) }
// Better: just take the interface
func Print(x fmt.Stringer) { fmt.Println(x.String()) }Rule of thumb: if it would compile with any + a type assertion, use an interface. If the function needs the static return type to be T, use generics.
Limitations as of 1.26:
- No generic methods on types — only top-level funcs + type definitions take type parameters.
- GCShape instantiation — compiler shares one instantiation per “GC shape” (size class + pointer layout). Small runtime dictionary lookup in generic code; usually invisible.
- No higher-kinded types — can’t express
Functor[F[_]]. - Generic type aliases — finally added in 1.24 (
type Set[T comparable] = map[T]struct{}).
Fuzz + property + golden-file testing
// Fuzz testing (1.18+)
func FuzzMarshal(f *testing.F) {
f.Add([]byte(`{"x":1}`))
f.Add([]byte(`{"x":1,"y":2}`))
f.Fuzz(func(t *testing.T, data []byte) {
var v map[string]any
if err := json.Unmarshal(data, &v); err != nil { return }
out, err := json.Marshal(v)
if err != nil { t.Fatalf("round-trip: %v", err) }
var back map[string]any
if err := json.Unmarshal(out, &back); err != nil { t.Fatalf("re-unmarshal: %v", err) }
})
}
// Run: go test -fuzz=FuzzMarshal -fuzztime=60s
// Property-based with pgregory.net/rapid
import "pgregory.net/rapid"
func TestReverseProp(t *testing.T) {
rapid.Check(t, func(t *rapid.T) {
xs := rapid.SliceOf(rapid.Int()).Draw(t, "xs")
if !slices.Equal(xs, reverse(reverse(xs))) {
t.Fatalf("double-reverse changed slice")
}
})
}
// Golden file with go-cmp
import "github.com/google/go-cmp/cmp"
func TestRender(t *testing.T) {
got := Render(input)
goldenPath := "testdata/expected.json"
if *update { os.WriteFile(goldenPath, got, 0644); return }
want, _ := os.ReadFile(goldenPath)
if diff := cmp.Diff(string(want), string(got)); diff != "" {
t.Errorf("output mismatch (-want +got):\n%s", diff)
}
}Combine with testcontainers-go for ephemeral Postgres / Redis / Kafka, net/http/httptest for HTTP fakes, sqlmock for DB mocks, uber-go/mock (maintained gomock fork) for interfaces. gotestsum produces JUnit XML for CI.
Production observability (OpenTelemetry-Go)
CNCF-standard tracing + metrics + logging recipe for Go services in 2026:
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
func initTracing(ctx context.Context) (func(context.Context) error, error) {
exporter, err := otlptracehttp.New(ctx, otlptracehttp.WithEndpoint("otel-collector:4318"))
if err != nil { return nil, err }
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resource.NewWithAttributes(semconv.SchemaURL,
semconv.ServiceName("api"), semconv.ServiceVersion("1.2.3"))),
)
otel.SetTracerProvider(tp)
return tp.Shutdown, nil
}
// Auto-instrument HTTP with otelhttp
mux := http.NewServeMux()
mux.Handle("/api/", otelhttp.NewHandler(apiHandler, "api"))
// Manual span (propagates trace context via ctx)
tracer := otel.Tracer("github.com/me/api")
ctx, span := tracer.Start(ctx, "loadUser")
defer span.End()
span.SetAttributes(attribute.Int("user.id", id))W3C headers (traceparent, tracestate) auto-propagate across services. Vendor exporters: Datadog Go SDK, Honeycomb beeline-go, New Relic Go agent, Grafana Tempo + Pyroscope, Jaeger native, OTLP for any CNCF backend.
Distributed Go workloads (gRPC + ConnectRPC + middleware)
ConnectRPC (Buf) has overtaken vanilla gRPC for new projects — speaks gRPC, gRPC-Web, and JSON over HTTP/1.1+2 with the same generated code.
// service.proto compiled with `buf generate`
type userServer struct{}
func (s *userServer) GetUser(ctx context.Context, req *connect.Request[userv1.GetUserRequest]) (*connect.Response[userv1.GetUserResponse], error) {
return connect.NewResponse(&userv1.GetUserResponse{
Id: req.Msg.Id, Name: "Alice",
}), nil
}
func main() {
mux := http.NewServeMux()
mux.Handle(userv1connect.NewUserServiceHandler(&userServer{}))
srv := &http.Server{
Addr: ":8080",
Handler: h2c.NewHandler(mux, &http2.Server{}), // HTTP/2 over plaintext
ReadTimeout: 10 * time.Second,
WriteTimeout: 30 * time.Second,
}
runWithGracefulShutdown(srv)
}
// Graceful shutdown — refuse new conns, drain in-flight
func runWithGracefulShutdown(srv *http.Server) {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
errCh := make(chan error, 1)
go func() { errCh <- srv.ListenAndServe() }()
select {
case <-ctx.Done():
slog.Info("draining connections")
sCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
srv.Shutdown(sCtx)
case err := <-errCh:
slog.Error("server died", "err", err)
}
}Alternatives: Twirp (Twitch, JSON-friendly), go-kit (microservices toolkit), go-zero (Chinese ecosystem, batteries-included). For canary deploys: systemd socket activation lets a new process inherit the listener FD via coreos/go-systemd/activation, old process drains and exits — zero-downtime without a load balancer.
CNCF Go ecosystem (Go’s empire)
Go is the dominant CNCF language. As of 2026, ~70% of CNCF graduated + incubating projects are Go-based:
| Layer | Projects |
|---|---|
| Orchestration | Kubernetes, OpenShift, k3s, k0s, RKE2 |
| Container runtime | containerd, CRI-O, runc, youki (Rust exception) |
| Service mesh | Istio control plane, Consul Connect, Cilium service mesh |
| Networking | Cilium (eBPF), Calico, Flannel, MetalLB, Submariner |
| CI/CD | ArgoCD, Flux, Tekton, Jenkins X, KubeVela |
| Observability | Prometheus, Grafana core, Loki, Mimir, Tempo, Pyroscope, Jaeger, OpenTelemetry Collector |
| Storage | etcd, MinIO, Rook, Longhorn, OpenEBS, Velero |
| Service discovery | Consul, CoreDNS |
| API gateway | Traefik, Emissary, Envoy Gateway control plane |
| Messaging | NATS, CloudEvents SDK |
| Policy | OPA (Open Policy Agent), Kyverno, Falco |
| Workflow | Argo Workflows, Temporal (Go SDK first-class), Cadence |
| Security | Trivy, Notary, sigstore/cosign, Falco, Tetragon |
| Web servers | Caddy (HTTPS by default), Traefik |
| IaC | Terraform, Pulumi (Go is a target lang), Crossplane |
The combination of single-binary deploys, fast cross-compilation, small static binaries, lightweight goroutines for fan-out, and a stdlib handling HTTP/TLS/JSON without deps — is exactly what infra code needs.
10. Citations
- Official docs: https://go.dev/doc/
- Language specification: https://go.dev/ref/spec
- Release history (Go 1.26 = Feb 2026; 1.26.2 = April 2026): https://go.dev/doc/devel/release
- Effective Go (style/idioms): https://go.dev/doc/effective_go
- Go Code Review Comments: https://go.dev/wiki/CodeReviewComments
- GMP scheduler reference (runtime/proc.go and design doc): https://go.googlesource.com/proposal/+/master/design/17503-eliminate-rescheduling.md
- PGO docs: https://go.dev/doc/pgo
- Race detector internals: https://go.dev/doc/articles/race_detector
- pkg.go.dev (stdlib + ecosystem): https://pkg.go.dev
- Iter / range-over-func proposal (1.23): https://go.dev/blog/range-functions