Where I was going with all of this was that the linked style guide mentions cases where people create buffered channels presumably because their channel writes start blocking. No buffer will make up for the case where the rate of work requested is higher than the rate at which work can be completed. What people really want in that case is the ability to get out of the channel write and return an error to the downstream system; you don't want to buffer, you want to cancel. There are many ways to accomplish the act of cancelling work, but you do have to watch your channel writes explicitly.
select {
default:
case <-ctx.Done():
return ctx.Err()
}
When there's no other channels to select with the context, just use that code block between lengthy operations to check context cancellation and return early.(If you are curious about how many extra time is wasted, it's easy to write a benchmark test for that. Last time I checked it was ~20ns when you are going the default/not-cancelled route)
so in your example it should be done like this: ch <- workFor(ctx, in)
and the 'workFor' function should be the one that gets canceled with the context.
Deep down at the very end, you would have select statement with '<-ctx.Done()' and something else that doesn't need canceling (if it needs canceling it should have taken context too).
Of course the world isn't perfect and not everything takes context right now (even in standard library), so you do have to do some workarounds every once in a while.