Goroutine lifecycle
Every goroutine should have a clear owner, exit condition, and cancellation path.
Canonical guidance
- know who starts a goroutine
- know what event makes it stop
- know who waits for it or otherwise observes completion
- tie long-lived goroutines to process or component lifecycle explicitly
Use when
- launching background work
- designing worker pools
- reviewing shutdown behavior
Avoid
- fire-and-forget goroutines with hidden side effects
- goroutines that only terminate on process exit
- no ownership model for background loops
Preferred pattern
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
return worker(ctx)
})
return g.Wait()
Anti-pattern
go syncAllCaches()
Explanation: This anti-pattern is common because fire-and-forget feels simple, but unowned goroutines become leak, shutdown, and observability problems.
Why
- unmanaged goroutines are a common source of leaks, races, and shutdown bugs
Related pages
Sources
- Go Concurrency Patterns: Context - Sameer Ajmani
- Go Concurrency Patterns: Pipelines and cancellation - Sameer Ajmani
- Concurrency is not parallelism - Rob Pike