go-retry
A dependency-free Go library for retrying flakey operations with pluggable backoff and jitter middleware.
Repository Health
Technical Analysis
go-retry gives Go developers a small, composable toolkit for retrying operations that may fail transiently, such as database connections or network calls. Instead of a single opinionated retry loop, it exposes a Backoff interface and a handful of constructors (NewConstant, NewExponential, NewFibonacci) that callers wrap with middleware to add jitter, cap individual delays, limit total retries, or bound total elapsed time.
Callers explicitly mark which errors should trigger a retry with RetryableError, so a function’s own logic decides what is transient versus fatal, and everything respects Go’s context.Context for cancellation. The library has zero dependencies beyond the standard library, so it adds no bloat and no supply-chain surface to a project that just needs a retry loop.
What You Get
- Three built-in backoff algorithms — constant, exponential, and Fibonacci — each created with a single constructor call
- Composable middleware (
WithJitter,WithJitterPercent,WithFullJitter,WithMaxRetries,WithCappedDuration,WithMaxDuration) that wraps anyBackoffto change its behavior - Generic
DoValue[T]andDofunctions that run a retryable function against aBackoffand acontext.Context, stopping on cancellation or a non-retryable error - Explicit opt-in retry semantics via
RetryableError, so only errors a caller marks are retried and everything else fails fast - Safe-for-concurrent-use backoff implementations built on
sync/atomic, so a singleBackoffvalue can be shared across goroutines
Common Use Cases
- Retrying a database connection or ping until it succeeds or the context is canceled
- Wrapping outbound HTTP calls to a flakey upstream API with exponential backoff and jitter to avoid thundering-herd retries
- Bounding the total time spent retrying a startup health check with
WithMaxDurationbefore failing the process - Building a custom backoff policy by writing your own
BackoffFuncand layering the existing middleware on top of it
Under The Hood
Architecture
The whole module is a single flat retry package with no subdirectories. Its core abstraction is the Backoff interface (Next() (time.Duration, bool)), implemented by small concrete types (exponentialBackoff, fibonacciBackoff) and by a BackoffFunc adapter modeled directly on Go’s http.HandlerFunc pattern. Retry execution lives in retry.go’s generic DoValue[T]/Do, which loop until the context is canceled, a non-retryable error surfaces, or the backoff itself signals stop, waiting via a time.Timer selected against ctx.Done(). Middleware in backoff.go (WithJitter, WithMaxRetries, WithCappedDuration, and friends) wraps any Backoff and returns a new closure-based BackoffFunc, composing behavior through decoration rather than inheritance. Because every concrete backoff and every middleware function depends on nothing but the single-method Backoff interface, changing that interface’s shape would break every implementation and every middleware wrapper simultaneously.
Tech Stack
Built for Go 1.25 with zero external dependencies — go.mod has no require lines, and the code imports only stdlib packages (context, errors, math, math/rand/v2, sync, sync/atomic, time). Tests run via the standard testing package using t.Parallel() and godoc-verified Example functions rather than a third-party assertion library. CI is a single GitHub Actions workflow (.github/workflows/test.yml) that runs go test -race -short -timeout=5m ./... on every push and pull request to main, pinning actions to ratcheted commit SHAs. Distribution is the standard Go module proxy under github.com/sethvargo/go-retry; there is no separate build step beyond go build.
Code Quality
Every source file has a matching test file (backoff_constant_test.go, backoff_exponential_test.go, backoff_fibonacci_test.go, backoff_test.go, retry_test.go), and CI runs the full suite with the race detector enabled. Errors are typed via an unexported retryableError struct implementing Unwrap/Error, checked with errors.As rather than sentinel comparisons, so only errors explicitly wrapped by a caller are retried. Naming is idiomatic Go throughout (exported NewX constructors, unexported state), and the concurrency-sensitive backoffs use atomic.Uint64/atomic.Pointer with compare-and-swap loops instead of locks wherever that pattern applies, falling back to a sync.Mutex only in WithMaxRetries where a CAS loop would be awkward. No linter configuration file is present in the repo, but the style is consistent and comment coverage on exported identifiers is thorough.
What Makes It Unique
The retry/backoff pattern itself is well-established — the project’s own README benchmarks itself directly against two other popular Go backoff libraries. What distinguishes this implementation is the http.Handler-style BackoffFunc adapter, which lets any function satisfy Backoff and lets callers layer arbitrary custom middleware on top of the built-in ones without a plugin system, plus a documented notes-and-caveats section calling out non-obvious behavior like modifier ordering effects. It is a clean, minimal-dependency, benchmark-conscious take on a standard pattern rather than a conceptually new one.
Used by 3 apps in this directory
Digger
Devops · Automation · Developer Tools
Run Terraform and OpenTofu natively inside your existing CI pipeline — no separate runners, no third-party secrets, no extra compute costs.
Hatchet
AI Development · Developer Tools · Automation
A Postgres-backed orchestration engine for background tasks, AI agents, and durable workflows that replaces Redis queues and multi-datastore durable execution platforms with a single self-hostable service.
OSV.dev
Security
Google's open-source vulnerability database that maps CVEs to exact package versions across 50+ ecosystems with a public API and data dumps.