Skip to main content
The Backoff namespace provides composable retry delay strategies for use in Workflow.step retry configuration. Import it directly from @durable-effect/workflow.
import { Backoff } from "@durable-effect/workflow";

Backoff.exponential

Delay grows exponentially: base * factor^attempt.

Signature

Backoff.exponential(options: {
  base: string | number;
  factor?: number;
  max?: string | number;
}): BackoffStrategy

Parameters

options.base
string | number
required
Starting delay for the first retry. Accepts a duration string ("1 second") or milliseconds (1000).
options.factor
number
default:"2"
Multiplier applied at each attempt. A factor of 2 doubles the delay each time. Must be greater than 1 for exponential growth.
options.max
string | number
Maximum delay cap. Once the calculated delay exceeds this value, it is clamped. Accepts a duration string or milliseconds.

Example

Backoff.exponential({ base: "1 second", factor: 2, max: "30 seconds" })
// Attempt delays: 1s → 2s → 4s → 8s → 16s (capped at 30s)

yield* Workflow.step({
  name: "API call",
  execute: callAPI(),
  retry: {
    maxAttempts: 5,
    delay: Backoff.exponential({
      base: "1 second",
      factor: 2,
      max: "30 seconds",
    }),
  },
});

Backoff.linear

Delay grows linearly: initial + (attempt * increment).

Signature

Backoff.linear(options: {
  initial: string | number;
  increment: string | number;
  max?: string | number;
}): BackoffStrategy

Parameters

options.initial
string | number
required
Delay for the first retry. Accepts a duration string or milliseconds.
options.increment
string | number
required
Amount added to the delay on each subsequent retry. Accepts a duration string or milliseconds.
options.max
string | number
Maximum delay cap. Accepts a duration string or milliseconds.

Example

Backoff.linear({ initial: "1 second", increment: "2 seconds", max: "10 seconds" })
// Attempt delays: 1s → 3s → 5s → 7s → 9s (capped at 10s)

yield* Workflow.step({
  name: "API call",
  execute: callAPI(),
  retry: {
    maxAttempts: 5,
    delay: Backoff.linear({
      initial: "1 second",
      increment: "2 seconds",
      max: "10 seconds",
    }),
  },
});

Backoff.constant

Fixed delay between every retry attempt.

Signature

Backoff.constant(delay: string | number): BackoffStrategy

Parameters

delay
string | number
required
Fixed delay between retries. Accepts a duration string ("5 seconds") or milliseconds (5000).

Example

Backoff.constant("5 seconds")
// Attempt delays: 5s → 5s → 5s

yield* Workflow.step({
  name: "API call",
  execute: callAPI(),
  retry: {
    maxAttempts: 3,
    delay: Backoff.constant("5 seconds"),
  },
});

Backoff.presets

Pre-configured strategies for common scenarios.

Presets reference

Backoff.presets.standard()
BackoffStrategy
Exponential backoff starting at 1 second, capped at 30 seconds. Delays: 1s → 2s → 4s → 8s → 16s → 30s. Use for most external API calls.
Backoff.presets.aggressive()
BackoffStrategy
Exponential backoff starting at 100ms, capped at 5 seconds. Delays: 100ms → 200ms → 400ms → 800ms → 1.6s → 5s. Use for internal services with low latency targets.
Backoff.presets.patient()
BackoffStrategy
Exponential backoff starting at 5 seconds, capped at 2 minutes. Delays: 5s → 10s → 20s → 40s → 80s → 120s. Use for rate-limited external APIs.
Backoff.presets.simple()
BackoffStrategy
Constant 1-second delay between every retry. Use for polling scenarios or when predictable timing matters.

Example

import { Backoff, Workflow } from "@durable-effect/workflow";

// Rate-limited API with patient backoff
yield* Workflow.step({
  name: "Call rate-limited API",
  execute: callRateLimitedAPI(),
  retry: {
    maxAttempts: 10,
    delay: Backoff.presets.patient(),
  },
});

// Internal service with aggressive backoff
yield* Workflow.step({
  name: "Internal cache fetch",
  execute: fetchFromCache(key),
  retry: {
    maxAttempts: 5,
    delay: Backoff.presets.aggressive(),
  },
});

BackoffStrategy type

The BackoffStrategy type is returned by all Backoff.* functions and accepted by RetryConfig.delay.
type BackoffStrategy =
  | { type: "constant"; delayMs: number }
  | { type: "linear"; initialDelayMs: number; incrementMs: number; maxDelayMs?: number }
  | { type: "exponential"; initialDelayMs: number; multiplier: number; maxDelayMs?: number }

addJitter

Add ±10% randomness to a delay in milliseconds to prevent the thundering herd problem.

Signature

function addJitter(delayMs: number, jitterFactor?: number): number

Parameters

delayMs
number
required
Base delay in milliseconds to apply jitter to.
jitterFactor
number
default:"0.1"
Fraction of the delay used as the jitter range. Default 0.1 applies ±10% randomness. For example, a 1000ms delay with factor 0.1 produces a value between 900ms and 1100ms.
Jitter is applied automatically by Workflow.step when retry.jitter is true (the default). Use addJitter directly only when building custom retry logic outside of the step primitive.

parseDuration

Parse a human-readable duration string or number to milliseconds.

Signature

function parseDuration(duration: string | number): number

Parameters

duration
string | number
required
Duration to parse. If a number, it is returned as-is (treated as milliseconds). If a string, it is parsed using the supported unit suffixes.

Supported formats

parseDuration(5000)            // 5000 (number passthrough)
parseDuration("500ms")         // 500
parseDuration("30 seconds")    // 30_000
parseDuration("30s")           // 30_000
parseDuration("5 minutes")     // 300_000
parseDuration("5m")            // 300_000
parseDuration("2 hours")       // 7_200_000
parseDuration("2h")            // 7_200_000
parseDuration("7 days")        // 604_800_000
parseDuration("7d")            // 604_800_000
Throws an Error if the string format is not recognized.

Build docs developers (and LLMs) love