Context should go away for Go 2 (2017)
Go 2, Versioning, and Comparisons to Other Languages
- Many note the article is from 2017; consensus now is “there won’t be a Go 2” in the breaking-change sense.
- Go has evolved by adding features while keeping backwards compatibility; “Go 1.x forever” is seen as a deliberate response to Python 2→3 pain.
- Rust’s “editions” are compared with Go’s
goversion directive:- Both gate syntax/behavior based on a per-module setting.
- Rust explicitly bundles breaking changes into editions while keeping old editions supported.
- Go’s directive mainly gates new features; the official stance is “no breaking changes” with rare exceptions.
- Some argue Rust’s approach proves “never break anything” is the wrong lesson from Python; others say Rust/Go are effectively doing the same thing.
Role of context in Go
contextis widely accepted as essential infrastructure for:- Cancellation and timeouts.
- Passing request-scoped metadata (user/tenant IDs, locales, tracing IDs, logging data).
- It’s praised for being explicit, value-based, and composable with non-context-aware code (e.g., by wrapping
io.Reader).
Critiques of Context Design (“Context Virus”)
- Main complaints:
- Verbosity and “infection”: once added, context parameters propagate through many function signatures.
- Boilerplate similar to explicit error handling.
- Guidance against storing contexts in structs is seen as constraining and fragile.
- Some propose language-level support or implicit/goroutine-local context to avoid threading it manually.
- Others strongly prefer explicit passing over implicit thread-local/dynamic scope, citing debugging and refactoring difficulties.
ctx.Value and Passing Metadata
- Strong split:
- Critics see
ctx.Valueas an untyped key–value bag that hides dependencies and can become an undocumented API. - Supporters find it invaluable for cross-cutting concerns (logging, tracing, sharding decisions) that must pass through third-party libraries.
- Critics see
- Recommended practice (from docs, echoed in thread): wrap
ctx.Valuewith typed helper functions and private key types.
Alternatives and Broader Reflections
- Alternatives mentioned: per-call CancellationToken-style args, explicit structs, DI, goroutine-local storage, dynamic scope, monads/effects.
- Many conclude every approach pushes complexity somewhere: either into function signatures (explicit) or into hidden coupling (implicit).
- Several comments generalize this to cross-cutting concerns and argue that tooling and language design, not just APIs, are the real bottleneck.