Skip to content

Migration

Use this page when you are evaluating the cost of moving from retry-only libraries to redress.

What changes when you move to redress

The main difference is abstraction level.

Most retry libraries focus on:

  • retry predicates
  • decorator ergonomics
  • backoff behavior

redress adds:

  • explicit semantic classification (ErrorClass)
  • a canonical policy container
  • structured stop reasons and outcomes
  • shared retry budgets
  • circuit breakers
  • low-cardinality observability hooks

That means migration usually makes a few things more explicit, but gives you a stronger model once the code stops being “just a decorator.”

From Tenacity-style decorators

Follow Migrating from Tenacity for exception selection, wait schedules, result classifiers, async usage, and terminal behavior. It includes runnable Redress examples and a migration verification checklist.

From Backoff-style decorators

Follow Migrating from Backoff for exception filters, give-up rules, result predicates, hooks, async usage, and fallback behavior. The guide calls out intentional timing changes and differences in attempt limits.

When to stop using decorator-only migration

Move from the decorator to Policy when you need:

  • circuit breakers
  • shared retry budgets
  • explicit outcome handling for result-based retries
  • execute() / RetryOutcome
  • multiple operations sharing one configuration

Retry-only object migration

If you currently think in terms of a retry object rather than a decorator, the closest shape is:

from redress import RetryPolicy, default_classifier
from redress.strategies import decorrelated_jitter

policy = RetryPolicy(
    classifier=default_classifier,
    strategy=decorrelated_jitter(max_s=5.0),
    max_attempts=5,
    deadline_s=30.0,
)

Then, when you need the wider execution model:

from redress import Policy, Retry, default_classifier
from redress.strategies import decorrelated_jitter

policy = Policy(
    retry=Retry(
        classifier=default_classifier,
        strategy=decorrelated_jitter(max_s=5.0),
        max_attempts=5,
        deadline_s=30.0,
    ),
)

HTTP 429 / Retry-After migration

This is a common place where redress gets more valuable than a generic retry decorator.

from redress import RetryPolicy
from redress.extras import http_retry_after_classifier
from redress.strategies import decorrelated_jitter, retry_after_or

policy = RetryPolicy(
    classifier=http_retry_after_classifier,
    strategy=retry_after_or(decorrelated_jitter(max_s=10.0)),
    max_attempts=5,
)

This keeps rate limiting separate from generic transient failure handling.

What redress makes more explicit on purpose

  • failures should map to coarse semantic classes
  • retries should be bounded
  • observability should use stable, low-cardinality fields
  • callers should be able to ask why retries stopped

If that explicitness feels heavy, the decorator may be enough. If the code is already carrying ad-hoc retry rules, redress usually makes the behavior easier to reason about.