Context Control in Go
Go developers weigh how to use `context` and goroutines responsibly, focusing on cancellation, resource lifetimes, and avoiding hidden concurrency in libraries. Many favor synchronous-looking APIs where callers control whether work runs concurrently, while acknowledging legitimate cases for internal goroutines and long‑running workers. The conversation also critiques `context` as a mixed bag of cancellation and request-scoped data, compares it to patterns in Rust and C#, and touches on structured concurrency and the limits of Go’s “no async coloring” ideal.
Goroutines in Libraries & API Design
- Strong theme: library APIs should appear synchronous; callers decide if they want concurrency by spawning goroutines themselves.
- Internal goroutines are acceptable when they are:
- Clearly part of the abstraction (e.g., crawler, parallel map, batching pub/sub, metrics flusher).
- Bounded, well-documented, and tunable (limits, rate, batching, in‑flight caps).
- Properly cleaned up before the public function returns (structured concurrency style, often via
errgrouporWaitGroup).
- Critics of “never start goroutines in libraries” call that overly rigid; some workloads are simpler and safer if the library owns scheduling.
- Panics in background goroutines are a concern: callers can’t wrap those in
recoverunless they own the goroutine boundary.
Context: Purpose, Pattern, and Pain Points
- Two main roles:
- Cancellation / deadlines / “you can stop now”.
- Request-scoped data (logger, tracing, auth, etc.).
- Many dislike the “bag of stuff” aspect; it’s hard to know why a function needs a context or what’s inside.
- Others defend explicit
ctxparameters:- Make interruptibility and logging requirements visible.
- Easier to review and reason about than implicit thread-local storage.
- Guidance debated:
- Common rule: don’t store contexts; pass them down the stack.
- Nuance: ok in structs that are effectively parameters, and in stdlib for backward compatibility (e.g.,
net/http), sometimes usingNewRequestWithContext. context.WithoutCancelis seen as a useful compromise when storing “values but not deadlines”.
- Friction points:
- No way to statically know if callees honor context.
- Pressure to thread
ctxthrough “almost every function”, especially for logging.
Context vs Function Coloring / Async Models
- Some see
ctxas a mild “coloring” (functions with vs without context), partially undermining Go’s “no async keyword” story. - Others argue it’s far less constraining than async/await:
- You can always use
context.Background()or wrap it with timeouts. - It doesn’t create the hard sync/async separation seen in other languages.
- You can always use
Error Handling & Zero Values (Side Thread)
- Debate over the
(T, error)pattern:- One camp: zero/nil values should be “useful” even when
erroris set. - Another: in most real APIs, zero values are meaningless or dangerous; lack of sum types forces awkward conventions.
- One camp: zero/nil values should be “useful” even when