Context propagation
How Go code should pass, derive, and cancel context.
Canonical guidance
- Pass
context.Contextexplicitly as the first parameter. - Derive child contexts near the operation boundary.
- Cancel work you start when the work no longer matters.
- Do not hide context in globals or long-lived structs.
Use when
- request-scoped work
- cancellation
- deadlines
- tracing / request metadata
Avoid
- storing context on a service struct
- passing
nilcontext - using context for optional function arguments
Preferred pattern
func (s *Service) Fetch(ctx context.Context, id string) error {
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
return s.repo.Fetch(ctx, id)
}
Anti-pattern
type Service struct {
ctx context.Context
}
Explanation: This anti-pattern is common because storing context looks convenient, but it obscures request lifetime, cancellation ownership, and testability.
Why
- explicit context propagation makes cancellation and deadlines composable
- hidden context makes lifetime and ownership unclear
Related pages
Sources
- Go Concurrency Patterns: Context - Sameer Ajmani
- Go Concurrency Patterns: Pipelines and cancellation - Sameer Ajmani