Simpler Backoff
Published on: 2025-06-11 10:43:14
Exponential backoff with jitter is de rigeur for making service calls. This code, or something like it, probably looks really familiar:
func do(ctx context.Context) error { const ( maxAttempts = 10 baseDelay = 1 * time.Second maxDelay = 60 * time.Second ) delay := baseDelay for attempt := range maxAttempts { err := request(ctx) if err == nil { return nil } delay *= 2 delay = min(delay, maxDelay) jitter := multiplyDuration(delay, rand.Float64()*0.5-0.25) // ±25% sleepTime := delay + jitter select { case <-ctx.Done(): return ctx.Err() case <-time.After(sleepTime): } } return fmt.Errorf("failed after %d attempts", maxAttempts) } func multiplyDuration(d time.Duration, mul float64) time.Duration { return time.Duration(float64(d) * mul) }
But we can make this much nicer with a simple lookup table.
func do(ctx context.Context) error { delays := []time.Duration{ 1 * time.Second, 2 * time.Second, 4 * time.Second, 8 * time.Second, 16 * time.Second, 32 * time.Second, 60 * time.Second, 60 * tim
... Read full article.