backoff

Exponential backoff and retry for Go, with a generic Retry function, typed errors, and RetryAfter support for rate-limited APIs.

Library
Go
vv7.0.0
4,055stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
57/100Fair
Development Activity44
Maintenance24
Community60
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
73/100Good
Architecture82
Code Quality88
Innovation55
Learning Curve65

backoff is a Go library implementing the exponential backoff algorithm ported from Google’s HTTP Client Library for Java. It exposes a generic Retry[T any] function that wraps any fallible operation, retrying it with a randomized exponential delay until it succeeds, returns a permanent error, or a configurable limit is reached.

Version 7 layers on typed error introspection via *RetryError (carrying both the last operation error and the reason retrying stopped), a RetryAfter mechanism for honoring server-specified retry delays (e.g. HTTP 429 Retry-After headers), and independent limits for context cancellation, max tries, and max elapsed time — all implemented with zero external dependencies.

What You Get

  • Generic Retry[T any] function that retries any typed operation with exponential backoff, context support, max-tries and max-elapsed-time limits
  • *RetryError with LastErr and Cause fields, inspectable via errors.Is/errors.As or the AsRetryError helper
  • Permanent(err) to stop retrying immediately on non-transient errors
  • RetryAfter(duration, cause) to honor server-specified delays (e.g. HTTP 429 Retry-After) and reset the backoff schedule
  • Pluggable BackOff interface with ExponentialBackOff, ConstantBackOff, ZeroBackOff, and StopBackOff implementations
  • Ticker type for channel-based retry loops outside of Retry

Common Use Cases

  • Resilient HTTP clients - wrap outbound API calls in Retry so transient 5xx errors and network blips are retried automatically while 4xx client errors stop immediately via Permanent
  • Rate-limit-aware API integrations - return RetryAfter from an operation when a downstream service responds with a Retry-After header, so the client waits the server-requested duration before retrying
  • Bounded background job retries - combine WithMaxTries and WithMaxElapsedTime to cap how long a background task keeps retrying before giving up and surfacing a typed ErrExhausted or ErrMaxElapsedTime cause
  • Custom retry loops - use Ticker directly when you need a channel of retry ticks instead of a blocking Retry call, for integrating backoff timing into existing select-based control flow

Under The Hood

Architecture The package is a flat, single-directory Go module with each file owning one concern: backoff.go defines the BackOff interface and the trivial ZeroBackOff/StopBackOff/ConstantBackOff policies, exponential.go implements ExponentialBackOff’s randomized-interval formula, retry.go layers the generic Retry[T any] orchestration (context handling, MaxTries, MaxElapsedTime, RetryAfter, Permanent) on top of any BackOff, error.go defines the RetryError/RetryAfterError error types and sentinel causes, and ticker.go/timer.go provide a channel-based alternative to Retry plus an internal timer abstraction that keeps the package testable without real sleeps. Nothing in Retry depends on ExponentialBackOff directly — it only requires the BackOff interface — so swapping in a custom policy changes no other code.

Tech Stack The module (github.com/cenkalti/backoff/v7, go.mod targets Go 1.23) has zero third-party dependencies, relying entirely on the standard library: context for cancellation, errors for Is/As-based error chains, math/rand/v2 for jittered intervals, sync for the Ticker’s stop-once semantics, and time throughout. Retry’s use of Go generics (Operation[T any]) lets callers get back a strongly typed result instead of an any that needs a type assertion, a change introduced in the v5-v7 rewrites documented in CHANGELOG.md.

Code Quality Testing is extensive and table-driven: retry_test.go alone runs to nearly 600 lines covering permanent errors, RetryAfter resets, context cancellation races, and max-tries/max-elapsed-time interactions, with additional dedicated suites for the ticker, exponential formula, and tries counter. Error handling is explicit and typed throughout — RetryError implements Unwrap() []error so both Cause and LastErr participate in errors.Is/errors.As chains — and naming is consistent with Go conventions. A GitHub Actions workflow (.github/workflows/go.yaml) runs the test suite in CI, and doc comments are dense enough to double as the package’s primary documentation.

What Makes It Unique The library doesn’t reinvent exponential backoff — the algorithm is the well-known jittered-multiplicative one ported from Google’s Java HTTP client — but its v6/v7 API design is notably deliberate about failure introspection: every Retry failure surfaces through one RetryError type that separates “what failed” (LastErr) from “why retrying stopped” (Cause), and RetryAfter lets an operation hand the scheduler a server-dictated delay without abandoning the surrounding backoff state. The CHANGELOG shows this API was iterated through several breaking major versions specifically to make error handling less ambiguous, rather than adding new backoff algorithms.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search