Retries and Serverless: Essential AWS SDK Practices (2025 Edition)

Reliable serverless applications require thoughtful handling of retries. Proper retry mechanisms help gracefully manage transient failures, such as network interruptions, throttling, or temporary AWS service issues.

6 days ago   •   3 min read

By Maik Wiesmüller
Photo by Tim Mossholder / Unsplash

Why Retries Matter in Serverless Applications

Serverless architectures often interact with AWS services that may face intermittent outages, or experience latency spikes. Without proper retry strategies, these transient failures will significantly impact your application's availability and performance.

On top of that: Traditionally, servers under heavy load would gradually slow down until becoming unresponsive. However, in cloud-based, especially serverless environments, managed services also enforce rate limits.

And if that's not enough: throttling exceptions can occur when services need time to autoscale in response to sudden increases in demand. For instance, DynamoDB's on-demand scaling can experience delays, sometimes several minutes, when adjusting read/write capacities significantly.

Retrying becomes are a powerful strategy for managing these scenarios effectively.

Once you reach the maximum retry count, consider further measures such as implementing dead letter queues (DLQs) to handle persistent failures gracefully.

AWS SDK Default Retry Behavior

It's crucial to be proactive about retries. Understand your SDKs default settings and what happens if you exceed the maximum retry count. If you don't like the defaults you can change those to your needs.

AWS SDK comes with preset retry behaviors enabled by default for all service calls. This value is specific to your SDK (Javascript, Java, Go, etc.). Check your specific SDK guide for its default. This built-in behavior ensures a reasonable balance between robustness and efficiency, making your application resilient without additional configuration.

AWS SDK Retry Modes

AWS SDK provides built-in retry mechanisms with different configurable modes:

  • Standard (Recommended): For general use, includes exponential backoff with jitter to handle typical scenarios effectively.
  • Adaptive: (Experimental) Dynamically adjusts retry timing based on observed latency and errors.
  • Legacy: Older, simpler retry logic with limited capabilities, typically avoided for new implementations.

To set the retry mode explicitly in AWS SDK for Javascript v3:

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";

const client = new DynamoDBClient({
  retryMode: "adaptive", // options: 'standard', 'adaptive', 'legacy'
});

Customizing Retry Behavior (AWS SDK JavaScript V3)

While AWS SDK has sensible defaults, it's beneficial to customize retries to match your use cases. See your SDKs documentation for other examples than Javascript here.

💡
Even if there is no custom retry mentioned for your SDK on a first glance, take a look at the developer documentation. There are options!

Set Maximum Retry Attempts

Control the maximum number of retry attempts explicitly:

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";

const client = new DynamoDBClient({
  maxAttempts: 5
});

Custom Retry Backoff Logic

You can implement your own retry strategy by customizing the backoff delay:

...
import {ConfiguredRetryStrategy} from '@smithy/util-retry';

const maxAttempts = 4;
const backoffFunction = (attempt: number) => 100 + attempt * 1000;

const retryStrategy = new ConfiguredRetryStrategy(
  maxAttempts,
  backoffFunction
);

const client = new DynamoDBClient({
  retryStrategy
});
...
💡
retryStragety is part of the RetryInputConfig interface. You won't find it directly on the Service Client Documentation page.

Important Considerations

When implementing retries, always consider:

  • Idempotency: Ensure operations can safely execute multiple times without unintended side-effects.
  • Monitoring & Logging: Track retries for better observability, debugging, and tuning.
  • Dead Letter Queues (DLQs): Capture events that exhaust retries for later manual intervention or analysis.

Example of idempotent DynamoDB write operation:

import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";

const client = new DynamoDBClient({});
...
const putCommand = new PutItemCommand({
  TableName: "ExampleTable",
  Item: {
    id: { S: generatedId },
    data: { S: databody },
  },
  ConditionExpression: "attribute_not_exists(id)", // ensures idempotency
});

try {
  await client.send(putCommand);
} catch (error) {
  ...
}

Conclusion

Considering AWS SDK retry behaviors is crucial for building resilient and reliable serverless applications. Utilize provided retry modes, customize strategies as needed, and always design your application logic with idempotency and monitoring in mind.

Spread the word

Keep reading