The Go context package coordinates cancellation, deadlines, and request-scoped values across function and network-call boundaries. Create one application-root Context with context.Background() in main, then pass it explicitly to dependencies. section 10.6.2 section 10.8
Use contexts consistently:
- Accept context.Context as the first function parameter and commonly name the parameter ctx; functions receiving a context should not replace it with Background() or TODO(). section 10.6.2
- Derive children with context.WithTimeout for a duration, WithDeadline for a timestamp, or WithCancel when callers need explicit cancellation. section 10.6.2 section 10.8
- Always bound remote calls with a deadline or timeout so an unavailable dependency cannot cause the application to wait forever. section 10.6.2 section 10.8
- Select on ctx.Done() to detect expiration and inspect ctx.Err(), or context.Cause(ctx), to determine why the context ended. section 10.8
- Treat WithValue as a last resort for request metadata rather than using Context as general application key-value storage. section 10.6.3 section 10.8
Bound a database call
func Create(ctx context.Context, db habitCreator, h Habit) (Habit, error) { dbCtx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) defer cancel() if err := db.Add(dbCtx, h); err != nil { return Habit{}, fmt.Errorf("cannot save habit: %w", err) } return h, nil } A backend can return promptly when the deadline fires: select { case response := <-responseChan: return response, nil case <-ctx.Done(): return nil, ctx.Err() }
Test the timeout path
A mock dependency can wait longer than the configured database timeout, then select on ctx.Done(). The expected result is context.DeadlineExceeded, making the cancellation behavior executable and testable.
