A modern, intelligent command-line tool for practicing patience with adaptive backoff strategies. Built with Go and designed to be your patient companion when dealing with flaky commands, network requests, or any process that might need a second (or third, or fourth) chance.
Author: Shane Isley
Repository: github.com/shaneisley/patience
License: MIT
We've all been there – a deployment script fails because of a temporary network hiccup, a test flakes out randomly, or an API call times out just when you need it most. Instead of manually running the same command over and over, let patience handle the tedious work for you with grace and wisdom.
- Strategy-based interface – Choose the right backoff strategy for your use case
- HTTP-aware patience – Respects
Retry-Afterheaders and server timing hints - 10 backoff strategies – From simple fixed delays to mathematical proactive rate limiting
- Diophantine strategy – Mathematical proactive rate limiting with multi-instance coordination
- Intelligent pattern matching – Define success/failure based on output patterns, not just exit codes
- Timeout protection – Prevent commands from hanging indefinitely
- Preserves behavior – Your command's output and exit codes work exactly as expected
- Minimal dependencies – Single binary with vendored dependencies
- Unix Socket Daemon – Real daemon server with JSON protocol for multi-instance coordination
- Metrics Daemon (Optional) – Collect and visualize patience metrics with the
patienceddaemon
📚 Complete Documentation Suite:
- Quick Start - Get running in 2 minutes
- Migration Guide - Switch from other retry tools
- examples.md - Real-world usage scenarios and patterns
- Architecture.md - System design and technical decisions
- DAEMON.md - Optional metrics collection and monitoring
- Development-Guidelines.md - TDD process and contribution standards
- DOCUMENTATION.md - Documentation maintenance and standards
Get up and running with patience in under 2 minutes:
git clone https://github.com/shaneisley/patience.git
cd patience
go build -o patience ./cmd/patience# Basic retry with exponential backoff
./patience exponential -- curl https://httpbin.org/status/500
# HTTP-aware retry (respects server timing)
./patience http-aware -- curl -i https://httpbin.org/delay/2
# Mathematical proactive rate limiting (prevents rate limit violations)
./patience diophantine --rate-limit 100 --window 1h -- curl https://httpbin.org/status/429
# Success! Your command now has patienceAPI Calls with Rate Limiting:
patience http-aware -- curl -H "Authorization: Bearer $TOKEN" https://api.github.com/userDatabase Connections:
patience linear --increment 2s --max-delay 30s -- psql -h db.example.com -c "SELECT 1"Deployment Scripts:
patience exponential --success-pattern "deployment successful" -- kubectl apply -f app.yamlFlaky Tests:
patience fixed --attempts 5 --delay 1s -- npm testMulti-Instance Rate Limiting:
# Coordinate across multiple instances to prevent rate limit violations
patience diophantine --daemon --resource-id "shared-api" --rate-limit 50 --window 1h -- curl https://httpbin.org/status/429- See Strategy Details for choosing the right backoff strategy
- Check Pattern Matching for advanced success/failure detection
- Explore Configuration for persistent settings
git clone https://github.com/shaneisley/patience.git
cd patience
go build -o patience ./cmd/patience# Test with a command that always succeeds
./patience fixed -- echo "Hello, World!"
# Test with a command that fails (will have patience 3 times by default)
./patience exponential -- false
# Test HTTP-aware strategy
./patience http-aware -- curl -i https://httpbin.org/status/200The basic syntax is: patience STRATEGY [OPTIONS] -- COMMAND [ARGS...]
# HTTP-aware patience for API calls (respects Retry-After headers)
patience http-aware -- curl -i https://api.github.com/user
# Exponential backoff with custom parameters
patience exponential --base-delay 1s --multiplier 2.0 -- curl https://httpbin.org/delay/2
# Linear backoff for database connections
patience linear --increment 2s --max-delay 30s -- psql -h db.example.com
# Using abbreviations for brevity
patience ha -f exp -- curl -i https://api.github.com
patience exp -b 1s -x 2.0 -- curl https://httpbin.org/status/503| Strategy | Alias | Description | Best For |
|---|---|---|---|
http-aware |
ha |
Respects HTTP Retry-After headers |
API calls, HTTP requests |
exponential |
exp |
Exponentially increasing delays | Network operations, external services |
linear |
lin |
Linearly increasing delays | Rate-limited APIs, predictable timing |
fixed |
fix |
Fixed delay between patience | Simple patience, testing |
jitter |
jit |
Random jitter around exponential | Distributed systems, load balancing |
decorrelated-jitter |
dj |
AWS-style decorrelated jitter | High-scale distributed systems |
fibonacci |
fib |
Fibonacci sequence delays | Moderate growth, gradual recovery |
polynomial |
poly |
Polynomial growth with configurable exponent | Customizable growth patterns |
adaptive |
adapt |
Machine learning adaptive strategy | Commands with changing patterns |
diophantine |
dio |
Mathematical proactive rate limiting | Multi-instance coordination, enterprise APIs |
# Set maximum patience attempts
patience exponential --attempts 5 -- command
# Add timeout per attempt
patience linear --timeout 30s -- command
# Pattern matching - succeed when output contains pattern
patience fixed --success-pattern "deployment successful" -- deploy.sh
# Pattern matching - fail when output contains pattern
patience exponential --failure-pattern "(?i)error|failed" -- health-check.sh
# Case-insensitive pattern matching
patience http-aware --success-pattern "SUCCESS" --case-insensitive -- deployment-scriptMany real-world commands don't use exit codes properly. A deployment script might print "deployment successful" but exit with code 1, or a health check might exit with code 0 but print "Error: service unavailable". Pattern matching solves this by letting you define success and failure based on the command's output.
Use --success-pattern to define when a command should be considered successful, regardless of exit code:
# Deployment tools that don't use exit codes properly
patience --success-pattern "deployment successful" -- kubectl apply -f deployment.yaml
# API responses that indicate success
patience --success-pattern "\"status\":\"ok\"" -- curl -s https://httpbin.org/json
# Multiple success indicators (regex OR)
patience --success-pattern "(success|completed|ready)" -- health-check.shUse --failure-pattern to define when a command should be considered failed, even with exit code 0:
# Catch error messages in output
patience --failure-pattern "(?i)error|failed|timeout" -- flaky-script.sh
# Specific failure conditions
patience --failure-pattern "connection refused|network unreachable" -- network-test.sh
# JSON error responses
patience --failure-pattern "\"error\":" -- api-call.shPatterns are evaluated in this order:
- Failure pattern match → Command fails (exit code 1)
- Success pattern match → Command succeeds (exit code 0)
- Exit code → Standard behavior (0 = success, non-zero = failure)
Add --case-insensitive to make pattern matching case-insensitive:
# Matches "SUCCESS", "success", "Success", etc.
patience --success-pattern "success" --case-insensitive -- deployment.shBoth success and failure patterns support full regex syntax:
# Match specific formats
patience --success-pattern "build #\d+ completed" -- build-script.sh
# Word boundaries
patience --failure-pattern "\berror\b" -- log-parser.sh
# Capture groups and alternatives
patience --success-pattern "(deployed|updated) successfully" -- deploy.shThe HTTP-aware strategy is patience's flagship feature - it intelligently parses HTTP responses to determine optimal patience timing.
# Basic HTTP-aware patience
patience http-aware -- curl -i https://api.github.com/user
# With fallback strategy when no HTTP info available
patience http-aware --fallback exponential -- curl https://httpbin.org/delay/2
# Set maximum delay cap
patience http-aware --max-delay 5m -- curl https://api.slow-service.comHow it works:
- Parses
Retry-Afterheaders from HTTP responses - Extracts patience timing from JSON responses (
retry_after,retryAfterfields) - Falls back to specified strategy when no HTTP timing information is available
- Validated with 7 major APIs: GitHub, Twitter, AWS, Stripe, Discord, Reddit, Slack
Doubles the delay after each failed attempt - industry standard for network operations.
# Basic exponential backoff (1s, 2s, 4s, 8s...)
patience exponential --base-delay 1s -- api-call
# Custom multiplier (1s, 1.5s, 2.25s, 3.375s...)
patience exponential --base-delay 1s --multiplier 1.5 -- api-call
# With maximum delay cap
patience exponential --base-delay 1s --max-delay 10s -- api-callIncreases delay by a fixed increment each attempt - predictable timing.
# Linear progression (2s, 4s, 6s, 8s...)
patience linear --increment 2s -- gradual-patience
# With maximum delay cap
patience linear --increment 1s --max-delay 30s -- rate-limited-apiWaits the same amount of time between each attempt - simple and predictable.
# Wait 3 seconds between each attempt
patience fixed --delay 3s -- flaky-commandAdds randomness to exponential backoff to prevent thundering herd problems.
# Random delays between 0 and exponential backoff time
patience jitter --base-delay 1s --multiplier 2.0 -- distributed-api-callAWS-recommended strategy that uses the previous delay to calculate the next delay.
# Smart jitter based on previous delay
patience decorrelated-jitter --base-delay 1s --multiplier 3.0 -- aws-api-callUses the Fibonacci sequence for delays - moderate growth between linear and exponential.
# Fibonacci sequence delays (1s, 1s, 2s, 3s, 5s, 8s...)
patience fibonacci --base-delay 1s -- moderate-growth-patienceUses polynomial growth with configurable exponent for fine-tuned delay patterns.
# Quadratic backoff (1s, 4s, 9s, 16s...)
patience polynomial --base-delay 1s --exponent 2.0 -- database-connection
# Moderate growth (1s, 2.8s, 5.2s, 8s...)
patience polynomial --base-delay 1s --exponent 1.5 -- api-call
# Gentle sublinear growth
patience polynomial --base-delay 1s --exponent 0.8 -- frequent-operationMachine learning-inspired strategy that learns from success/failure patterns to optimize timing.
# Basic adaptive with exponential fallback
patience adaptive --learning-rate 0.1 --memory-window 50 -- flaky-service
# Fast learning for rapidly changing conditions
patience adaptive --learning-rate 0.5 --fallback fixed -- dynamic-api
# Conservative learning with large memory
patience adaptive --learning-rate 0.05 --memory-window 200 -- database-operationMathematical proactive rate limiting using Diophantine inequalities to prevent rate limit violations before they occur.
# Basic proactive rate limiting
patience diophantine --rate-limit 100 --window 1h -- curl https://httpbin.org/status/429
# Multi-instance coordination via daemon
patience diophantine --daemon --resource-id "shared-api" --rate-limit 50 --window 1h -- curl https://httpbin.org/status/429
# Custom retry patterns within mathematical constraints
patience diophantine --rate-limit 1000 --window 1h --retry-offsets "1s,5s,15s" -- api-call.sh
# Enterprise deployment with daemon coordination
patience diophantine --daemon --daemon-address "/var/run/patience/daemon.sock" --resource-id "production-api" --rate-limit 500 --window 1h -- production-script.sh| Strategy | Growth Pattern | Use Case | Example Delays (1s base) |
|---|---|---|---|
http-aware |
Server-directed | HTTP APIs, web services | Varies based on server response |
exponential |
Exponential | Network calls, APIs | 1s, 2s, 4s, 8s |
linear |
Linear | Rate-limited APIs | 1s, 2s, 3s, 4s |
fixed |
Constant | Simple patience, testing | 1s, 1s, 1s, 1s |
jitter |
Random exponential | Distributed systems | 0.3s, 1.8s, 0.9s, 5.2s |
decorrelated-jitter |
Smart random | AWS services, high-scale | 1.2s, 2.8s, 1.9s, 4.1s |
fibonacci |
Fibonacci | Moderate growth | 1s, 1s, 2s, 3s, 5s, 8s |
polynomial |
Polynomial | Customizable growth | 1s, 4s, 9s, 16s (exponent=2.0) |
adaptive |
Learning-based | Changing patterns | Adapts based on success/failure |
diophantine |
Mathematical | Proactive rate limiting | Calculated to prevent violations |
patience supports configuration files for setting default values. Configuration files use TOML format and follow this precedence order:
- CLI flags (highest priority)
- Environment variables
- Configuration file
- Default values (lowest priority)
patience automatically looks for configuration files in the current directory:
.patience.tomlpatience.toml
Use the --config flag to specify a configuration file:
patience exponential --config /path/to/config.toml -- command# .patience.toml
attempts = 5
timeout = "30s"
success_pattern = "deployment successful|build completed"
failure_pattern = "(?i)error|failed|timeout"
case_insensitive = true
# Strategy-specific settings (used when no CLI flags provided)
base_delay = "1s"
multiplier = 2.0
max_delay = "10s"All configuration options can be set via environment variables with the PATIENCE_ prefix:
export PATIENCE_ATTEMPTS=5
export PATIENCE_TIMEOUT=30s
export PATIENCE_SUCCESS_PATTERN="deployment successful"
export PATIENCE_FAILURE_PATTERN="(?i)error|failed"
export PATIENCE_CASE_INSENSITIVE=true
# Strategy-specific variables
export PATIENCE_BASE_DELAY=1s
export PATIENCE_MULTIPLIER=2.0
export PATIENCE_MAX_DELAY=10s
patience exponential -- commandUse --debug-config to see how configuration values are resolved:
patience exponential --debug-config -- commandThis shows the source of each configuration value (CLI flag, environment variable, config file, or default).
| Flag | Short | Default | Description |
|---|---|---|---|
--attempts |
-a |
3 |
Maximum number of attempts (1-1000) |
--timeout |
-t |
0 |
Timeout per attempt (e.g., 30s, 5m). Note: ~10-20ms overhead |
--success-pattern |
Regex pattern indicating success in stdout/stderr | ||
--failure-pattern |
Regex pattern indicating failure in stdout/stderr | ||
--case-insensitive |
false |
Make pattern matching case-insensitive | |
--config |
Configuration file path | ||
--debug-config |
false |
Show configuration debug information | |
--help |
-h |
Show help information |
| Flag | Short | Default | Description |
|---|---|---|---|
--fallback |
-f |
exponential |
Fallback strategy when no HTTP info available |
--max-delay |
-m |
30m |
Maximum delay cap |
| Flag | Short | Default | Description |
|---|---|---|---|
--base-delay |
-b |
1s |
Base delay for first patience |
--multiplier |
-x |
2.0 |
Multiplier for exponential growth |
--max-delay |
-m |
60s |
Maximum delay cap |
| Flag | Short | Default | Description |
|---|---|---|---|
--increment |
-i |
1s |
Delay increment per attempt |
--max-delay |
-m |
60s |
Maximum delay cap |
| Flag | Short | Default | Description |
|---|---|---|---|
--delay |
-d |
1s |
Fixed delay between attempts |
| Flag | Short | Default | Description |
|---|---|---|---|
--base-delay |
-b |
1s |
Base delay for calculations |
--multiplier |
-x |
2.0 |
Multiplier for jitter range |
--max-delay |
-m |
60s |
Maximum delay cap |
| Flag | Short | Default | Description |
|---|---|---|---|
--base-delay |
-b |
1s |
Base delay for calculations |
--multiplier |
-x |
2.0 |
Multiplier for jitter calculations |
--max-delay |
-m |
60s |
Maximum delay cap |
| Flag | Short | Default | Description |
|---|---|---|---|
--base-delay |
-b |
1s |
Base delay for Fibonacci sequence |
--max-delay |
-m |
60s |
Maximum delay cap |
| Flag | Short | Default | Description |
|---|---|---|---|
--base-delay |
-b |
1s |
Base delay for polynomial calculation |
--exponent |
-e |
2.0 |
Polynomial exponent (controls growth rate) |
--max-delay |
-m |
60s |
Maximum delay cap |
| Flag | Short | Default | Description |
|---|---|---|---|
--learning-rate |
-r |
0.1 |
Learning rate for adaptation (0.01-1.0) |
--memory-window |
-w |
50 |
Number of recent outcomes to remember (5-10000) |
--fallback |
-f |
exponential |
Fallback strategy when learning data insufficient |
| Flag | Short | Default | Description |
|---|---|---|---|
--rate-limit |
-l |
100 |
Maximum requests allowed in the time window |
--window |
-w |
1h |
Time window for rate limiting (e.g., 1h, 30m, 60s) |
--retry-offsets |
-o |
1s,5s,15s |
Comma-separated retry timing offsets |
--daemon |
-d |
false |
Enable daemon coordination for multi-instance rate limiting |
--daemon-address |
-a |
/var/run/patience/daemon.sock |
Unix socket path for daemon communication |
--resource-id |
-r |
Resource identifier for shared rate limiting (derived from command if not specified) |
- Run your command –
patienceexecutes your command exactly as you would - Check the result – Determine success using pattern matching (if configured) or exit code
- Pattern precedence – Failure patterns override success patterns, which override exit codes
- Exit on success – If the command succeeds,
patienceexits immediately (remaining attempts are skipped) - Calculate delay – Use the configured backoff strategy (fixed, exponential, jitter, linear, decorrelated-jitter, or fibonacci) based on attempt number
- Wait patiently – If it fails, wait for the calculated delay and try again with grace
- Respect limits – Stop after the maximum number of attempts or max delay reached
- Preserve exit codes – The final exit code matches your command's result
- 0 – Command succeeded on any attempt (remaining attempts skipped)
- 1 – Command failed due to failure pattern match
- Non-zero – Command failed after all patience attempts (matches the command's final exit code)
Note: patience exits with the result of the first successful attempt, not the last attempt.
Important: patience stops immediately when a command succeeds - it does not execute remaining attempts.
- ✅ Exits on first success - If attempt 1 succeeds, attempts 2-N are never executed
- 🔄 Only has patience on failure - Success means the job is complete
- 📊 Preserves exit codes - Your command's original behavior is maintained
- ⏱️ Efficient execution - No wasted time on unnecessary attempts
# If API is up on attempt 1, attempts 2-5 are skipped
patience exponential --attempts 5 -- curl https://httpbin.org/status/200
# Only has patience while the service is starting up
patience linear --attempts 10 --increment 1s -- nc -z localhost 8080
# This stops immediately if the first curl succeeds
patience http-aware --attempts 5 -- curl https://httpbin.org/status/200
# Output: "✅ Command succeeded after 1 attempt" (attempts 2-5 never run)Switching from other retry tools? Here's how to migrate common patterns to patience:
Old:
retry -t 5 -d 2 curl https://httpbin.org/status/503New:
patience fixed --attempts 5 --delay 2s -- curl https://httpbin.org/status/503Old:
@retries(max_attempts=3, delay=1, backoff=2)
def api_call():
return requests.get('https://api.example.com')New:
patience exponential --attempts 3 --base-delay 1s --multiplier 2 -- curl https://httpbin.org/status/503Old:
exponential-backoff --initial-delay 1000 --max-delay 30000 -- curl api.comNew:
patience exponential --base-delay 1s --max-delay 30s -- curl api.comOld:
while ! curl https://httpbin.org/status/503; do
echo "Retrying in 5 seconds..."
sleep 5
doneNew:
patience fixed --delay 5s -- curl https://httpbin.org/status/503Old:
aws s3 cp file.txt s3://bucket/ --cli-read-timeout 0 --cli-connect-timeout 60New:
patience exponential --timeout 60s -- aws s3 cp file.txt s3://bucket/Old:
for i in {1..3}; do
timeout 30 command && break
sleep $((i * 2))
doneNew:
patience exponential --attempts 3 --base-delay 2s --timeout 30s -- command- HTTP Intelligence: Automatically respects
Retry-Afterheaders - Pattern Matching: Success/failure based on output, not just exit codes
- Strategy Variety: 10 different backoff strategies for different use cases
- Real-time Feedback: Clear progress reporting during retries
- Configuration: Persistent settings via config files
- Metrics: Optional daemon for long-term retry analytics
Check out examples.md for real-world usage scenarios and common patterns.
This project follows Test-Driven Development (TDD) principles and is built incrementally. The codebase includes:
- Comprehensive test coverage – Unit tests for all core functionality
- Integration tests – End-to-end CLI testing
- Clean architecture – Modular design with clear separation of concerns
See Development-Guidelines.md for details on our TDD process and code style.
# Run all tests
go test ./...
# Run tests with race detection
go test -race ./...
# Run CLI integration tests
go test ./cmd/patience -v
# Run HTTP-aware strategy tests
go test -v ./pkg/backoff -run TestHTTPAware# Build for current platform
go build -o patience ./cmd/patience
# Build for multiple platforms
GOOS=linux GOARCH=amd64 go build -o patience-linux ./cmd/patience
GOOS=darwin GOARCH=amd64 go build -o patience-darwin ./cmd/patience
GOOS=windows GOARCH=amd64 go build -o patience.exe ./cmd/patienceThe project is organized into clean, testable packages:
cmd/patience– CLI interface with subcommand architecture using Cobrapkg/executor– Core patience logic and command executionpkg/backoff– Backoff strategies including HTTP-aware intelligencepkg/conditions– Pattern matching for success/failure detectionpkg/metrics– Metrics collection and daemon communicationpkg/ui– Terminal output and status reportingpkg/config– Configuration loading and validation
See Architecture.md for a detailed breakdown of the components.
We welcome contributions! The project follows conventional commit messages and maintains high test coverage. Feel free to:
- Report bugs or suggest features via GitHub Issues
- Submit pull requests with tests
- Improve documentation
- Share your use cases
Documentation:
- Development Guidelines - TDD process, code style, and contribution standards
- Architecture - System design and technical decisions
- Documentation Maintenance - How to maintain and improve documentation
- Daemon Setup - Optional metrics collection and monitoring
Getting Started:
- See Development section for setup instructions
- Check examples.md for real-world usage patterns
- Review existing tests for contribution examples
- Author: Shane Isley
- GitHub: @shaneisley
- Repository: github.com/shaneisley/patience
MIT License – see LICENSE file for details.
Built with:
- Cobra for CLI framework
- Testify for testing utilities
- The Go standard library for robust, concurrent execution
Practice patience! 🧘