Ramblings of an aging IT geek
← Ramblings of an aging IT geek
golang

context.Context, and learning to thread it through

I finally stopped resenting Go's habit of passing context.Context as the first argument everywhere once a timeout actually started doing its job.

A code editor showing a Go source file

For ages I treated ctx context.Context as boilerplate I copied into the first parameter slot and otherwise ignored. It looked like ceremony: pass it down, pass it down again, never quite use it. I resented threading the thing through every function signature for what felt like no payoff.

The penny dropped when a downstream HTTP call hung and took a request handler with it. I had a context with a deadline sitting right there, unused, because I had been passing it along but not actually handing it to the call that could block. The fix was one argument:

req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
resp, err := http.DefaultClient.Do(req)

Now the timeout on the inbound request propagates the whole way down. When the client gives up, the outbound call gives up too, and the goroutine unwinds instead of leaking. That is the entire point I had been missing: context is only useful if you actually pass it to the thing that does the waiting.

The first-argument convention stopped looking like ceremony after that. It is a discipline that makes cancellation and deadlines a property of the call graph rather than an afterthought. Thread it all the way through, hand it to every blocking call, and the resentment goes away. I came around eventually. Go usually wins these arguments.