Go

Why is storing a context.Context in a struct considered an anti-pattern? Are there exceptions?

Question 271MediumGo 1.22 to 1.25

The context package docs say: "Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it." The reasons:

  • Lifetime mismatch: a context belongs to one operation, while a struct usually lives across many. A Client holding the context of the request that created it will fail every later call once that first request finishes.
  • Hidden control flow: callers cannot give different deadlines to different method calls.
  • Unclear API: a signature like func (c *Client) Get(url string) hides the fact that the call can block or be canceled.
// Anti-pattern
type Worker struct {
	ctx context.Context
	db  *sql.DB
}
func (w *Worker) Process(id int) error { /* uses w.ctx */ return nil }

// Idiomatic
type Worker struct{ db *sql.DB }
func (w *Worker) Process(ctx context.Context, id int) error { /* ... */ return nil }

Accepted exceptions:

  • Structs that are a single operation, such as http.Request (its context field is set through WithContext) or an iterator/stream object used only inside one call.
  • A long-running component that owns its own lifecycle, which keeps a root context and a cancel function for Close()/Stop().
  • Backward compatibility, when a method signature cannot change.

Convention: ctx is the first parameter and is named ctx. To add context support without breaking an API, add a second method (Query alongside QueryContext).

More on Context

All 35 Context questions