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 go version 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

  • context is 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.Value as 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.
  • Recommended practice (from docs, echoed in thread): wrap ctx.Value with 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.