Context should go away for Go 2 (2017)

Go’s `context` package, originally introduced for cancellation and timeouts in concurrent servers, is criticized for its verbosity, “viral” need to be threaded through every call, and untyped key–value storage via `ctx.Value`, which can hide important dependencies and break static guarantees. Defenders argue it strikes a pragmatic balance: explicit over implicit state (vs thread‑local storage or dynamic scope), composable across libraries, and flexible enough to carry request metadata like user or tenant IDs, logging fields, and tracing information. Alongside this, commenters note that there will likely never be a “Go 2,” contrasting Go’s strong backward-compatibility stance with Rust’s edition system and Python’s painful 2→3 break, and suggesting any future improvements should be language-level refinements rather than a clean break.

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.