python 41 lines · 6 steps

A retry decorator with exponential backoff

A configurable decorator that re-runs a flaky function, waiting longer between each failed attempt.

Explained by highlit
1import random
2import time
3import logging
4from functools import wraps
5 
6logger = logging.getLogger(__name__)
7 
8 
9def retry_with_backoff(
10 max_attempts=5,
11 base_delay=0.5,
12 max_delay=30.0,
13 backoff_factor=2.0,
14 jitter=True,
15 exceptions=(Exception,),
16):
17 def decorator(func):
18 @wraps(func)
19 def wrapper(*args, **kwargs):
20 delay = base_delay
21 for attempt in range(1, max_attempts + 1):
22 try:
23 return func(*args, **kwargs)
24 except exceptions as exc:
25 if attempt == max_attempts:
26 logger.error(
27 "%s failed after %d attempts: %s",
28 func.__name__, attempt, exc,
29 )
30 raise
31 sleep_for = min(delay, max_delay)
32 if jitter:
33 sleep_for = random.uniform(0, sleep_for)
34 logger.warning(
35 "%s attempt %d/%d failed (%s); retrying in %.2fs",
36 func.__name__, attempt, max_attempts, exc, sleep_for,
37 )
38 time.sleep(sleep_for)
39 delay *= backoff_factor
40 return wrapper
41 return decorator
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A three-layer nested function lets a decorator accept configuration arguments while still wrapping the target.
  2. 2Exponential backoff with a cap and jitter spreads out retries so a failing dependency isn't hammered in lockstep.
  3. 3Re-raising on the final attempt preserves the original exception so callers still see real failures.

Related explainers

typescript
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { ValidationPipe } from '@nestjs/common';
import { ApiProperty } from '@nestjs/swagger';

Wiring validation and Swagger docs in NestJS

validation openapi decorators
Intermediate 8 steps
python
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status
from pydantic import BaseModel, EmailStr
from sqlalchemy.orm import Session
 

Building a signup endpoint in FastAPI

dependency-injection request-validation background-tasks
Intermediate 8 steps
javascript
const ROLE_PERMISSIONS = {
  admin: ['users:read', 'users:write', 'billing:read', 'billing:write'],
  manager: ['users:read', 'billing:read'],
  member: ['users:read'],

Role-based permissions middleware in Express

authorization middleware rbac
Intermediate 9 steps
ruby
class TemplateInterpolator
  PLACEHOLDER = /\{\{\s*([\w.]+)\s*\}\}/
 
  def initialize(strict: false)

Interpolating templates with dotted keys in Ruby

regex string-interpolation hash-traversal
Intermediate 6 steps
python
from django import forms
from django.utils import timezone
 
from .models import Reservation

Multi-field validation in a Django ModelForm

form validation cross-field validation modelform
Intermediate 7 steps
python
from django import template
from django.urls import reverse, NoReverseMatch
from django.utils.html import format_html
 

Active nav-link template tags in Django

template tags url routing active state
Intermediate 7 steps

Share this explainer

Here's the card — post it anywhere.

A retry decorator with exponential backoff — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code