๐Ÿ”—context.ContextLESSON

context.Context

The context package provides a way to carry deadlines, cancellation signals, and request-scoped values across API boundaries and goroutine trees.

Why context exists

Without context, cancelling a deep chain of goroutines requires custom done-channels threaded through every function. The context.Context interface standardises this so any library or handler can participate in cancellation using the same mechanism.

The Root Contexts

Always start a context chain from one of the two root constructors:

context.Background() is used in main, top-level handlers, and tests. context.TODO() signals that code needs to be updated to accept a real context.

Cancellation with WithCancel

context.WithCancel returns a child context and a cancel function. Calling cancel() closes the ctx.Done() channel, signalling every goroutine that holds the context:

ctx.Err() returns context.Canceled when cancelled manually, and context.DeadlineExceeded when a deadline expires.

Deadlines and Timeouts

WithTimeout and WithDeadline automatically cancel the context after a duration or at a fixed point in time:

Always defer cancel() even with timeouts โ€” the parent context may be cancelled before the timeout fires, and calling cancel releases resources immediately.

Passing Values with WithValue

context.WithValue stores a key/value pair in the context chain. Use it for request-scoped data like trace IDs and user IDs โ€” not for optional parameters:

Always use an unexported custom key type (not a plain string). Using string as a key type risks collisions if another package stores a value under the same string key.

The ctx-first convention

By convention, ctx is always the first parameter of any function that accepts a context:

Contexts flow top-down through the call chain. Never store a context in a struct โ€” pass it explicitly so the call graph is always clear.

Propagation through call chains

Child contexts inherit the parent's deadline. If the parent times out first, all children are cancelled automatically.

Knowledge Check

What does `defer cancel()` do when used with `context.WithCancel`?

Why should you use an unexported custom type as a context key instead of a plain string?

What does `ctx.Err()` return when a context times out?