context.Background() vs context.TODO(): when do you use each, and why not pass nil?
Question 272MediumGo 1.22 to 1.25
Both return an empty context that is never canceled, has no deadline, and holds no values. They behave the same; the difference is intent, which static analysis tools and reviewers read:
Background(): the true root. Use it inmain,init, tests, and top-level background daemons.TODO(): "a context should be threaded here but isn't yet". It marks unfinished work during refactors, and you can grep for it.
Never pass nil, even if a function "doesn't use it". Calling any method on a nil interface panics, and WithCancel(nil) and friends panic with "cannot create context from nil parent". staticcheck SA1012 flags this.
// Handler deep inside a request: never use Background here
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() // correct root for this request
// ctx := context.Background() // BUG: loses cancellation, deadline, and trace IDs
h.svc.Load(ctx, r.PathValue("id"))
}
Gotcha interviewers probe: calling context.Background() in the middle of a request path breaks the chain. Client disconnects are no longer noticed, timeouts no longer apply, and tracing loses its parent span.
More on Context
- Q270What belongs in context values and what doesn't?
- Q271Why is storing a context.Context in a struct considered an anti-pattern? Are there exceptions?
- Q273What is context.AfterFunc and what are the semantics of its stop function?
- Q274How do you make a blocking call that doesn't accept a context (e.g. net.Conn.Read) cancelable?
- Q275What does context.WithoutCancel do and when would you use it?
- Q276What is wrong with this handler? What error will the goroutine see?