Cheatsheet: Intermediate Go: Build Concurrent Backend Services
Scope: concurrency foundations, HTTP service + data access, and shipping with gRPC, profiling, and containers.
Module 1: Concurrency as the Spine
1.1 Types, Interfaces, Errors
| Concept | Rule of thumb |
|---|---|
| Value semantics | Struct passed by value = copy. Mutate via pointer *T receiver only when needed |
| Interface size | Small (1-3 methods). Define at the consumer, not the producer |
| Nil interface trap | var e error = (*MyErr)(nil) is non-nil; return explicit nil |
| Error wrapping | fmt.Errorf("...: %w", err) keeps the chain |
| Error inspection | errors.Is for sentinels, errors.As for typed errors |
| Panic | Only for unrecoverable programmer bugs, never for control flow |
var ErrNotFound = errors.New("not found")
if errors.Is(err, ErrNotFound) { /* sentinel match */ }
var pe *PathError
if errors.As(err, &pe) { /* typed match */ }
[!TIP] Wrap with
%wand add context at each layer. Unwrap down to the sentinel only at the boundary that decides behavior.
1.2 Goroutines, Channels, Context
| Need | Use |
|---|---|
| Hand off data / ownership | Channel |
| Guard state that stays put | sync.Mutex / sync.RWMutex |
| Request deadline + cancellation | context.Context |
| Wait for N goroutines | sync.WaitGroup |
| One-time init | sync.Once |
Rules:
- Every goroutine needs an exit path. No fire-and-forget.
- Pass
ctxas the first parameter; never store it in a struct. selectonctx.Done()to respect cancellation.- Close a channel from the sender only. Receivers never close.
- Buffered channel = bounded queue; size it deliberately.
select {
case <-ctx.Done():
return ctx.Err()
case v := <-ch:
handle(v)
}
1.3 Bounded Fan-Out with errgroup
This cheatsheet is part of the paid section. Sign in and purchase the course to view the full content. Sign in