---
title: "Polly in .NET: Complete Beginner's Guide to Resilience"  
description: "Learn Polly in .NET from scratch. Understand Retry, Circuit Breaker, Timeout, Fallback, Rate Limiting, Hedging, resilience pipelines, and HttpClient integration"  
author: "Yogendra  Mohan"  
published: 2026-08-13  
updated: 2026-08-13  
canonical: https://answers.mindstick.com/blog/542/polly-in-dot-net-complete-beginner-s-guide-to-resilience  
category: "technology"  
tags: [".net programming", "c#", "asp.net"]  
reading_time: 20 minutes  

---

# Polly in .NET: Complete Beginner's Guide to Resilience

Modern .NET applications rarely work alone. An ASP.NET Core application may communicate with payment gateways, third-party APIs, databases, cloud services, email providers, or other microservices.

Most of the time, these services work perfectly. But occasionally, something goes wrong.

An external API may temporarily return `503 Service Unavailable`. A network connection may fail. A server may respond too slowly. Or a service may become overloaded.

Should your application immediately show an error to the user?

Not always.

This is where **Polly** comes in.

[Polly is a .NET resilience](https://www.pollydocs.org/) library that provides strategies such as [**Retry, Circuit Breaker, Timeout, Fallback, Rate Limiting, and Hedging**](https://www.pollydocs.org/strategies/index.html). Modern Polly uses a **Resilience Pipeline** to combine these strategies and apply them around your application operations.

![Polly in .NET: Complete Beginner's Guide to Resilience](https://answers.mindstick.com/blogs/b6d0d0a9-0994-4bd7-8fdb-548c495689a1/images/92d020c1-61d7-4ae3-af3a-942c829f1a12.png)

## What Is Polly?

In simple terms:

> **Polly helps your .NET application handle temporary failures gracefully.**

Imagine your application calls an external API:

```plaintext
ASP.NET Core Application
          |
          v
     External API
```

If the API temporarily fails:

```plaintext
Application
     |
     v
External API
     |
     X
   Failure
     |
     v
Application Error
```

With Polly, you can define what should happen:

```plaintext
Application
     |
     v
Polly Resilience Pipeline
     |
     v
External API
     |
     X
Temporary Failure
     |
     v
Retry
     |
     v
External API
     |
     v
Success
```

Instead of immediately failing, your application gets an opportunity to recover.

## Why Do We Need Polly?

Consider a simple HTTP request:

```cs
// Create an HttpClient instance for making HTTP requests.
using var client = new HttpClient();

// Send a GET request to the external API.
var response = await client.GetAsync(
    "https://example.com/api/products");

// Throw an exception if the response indicates failure.
response.EnsureSuccessStatusCode();
```

- This code is perfectly valid.

But what happens if the server temporarily returns:

```plaintext
503 Service Unavailable
```

- Your application may immediately fail.
- However, the service could recover one second later.
- This is called a **transient failure**.

Instead of:

```plaintext
Failure → Error
```

you may want:

```plaintext
Failure
   ↓
Wait
   ↓
Retry
   ↓
Success
```

Polly helps you implement this kind of behavior without writing all the resilience logic yourself.

## What Is Resilience?

**Resilience** means an application can handle failures and recover when possible.

For example:

```plaintext
External API temporarily fails
          ↓
Application detects the failure
          ↓
Applies a resilience strategy
          ↓
Operation succeeds
```

- Resilience does not mean your application will never fail.
- It means your application knows **how to react when something fails**.

## What Is a Transient Failure?

A transient failure is usually temporary.

Examples include:

- Temporary network failure
- HTTP `503 Service Unavailable`
- Temporary connection failure
- Temporary server overload
- Temporary timeout
- Short-lived cloud service failure

For example:

```plaintext
10:00:00 → API fails
10:00:01 → API fails
10:00:02 → API works
```

- Retrying may make sense here.

But some errors should not be retried:

```plaintext
400 Bad Request
401 Unauthorized
403 Forbidden
Invalid data
Invalid payment information
Business validation error
```

A good resilience strategy distinguishes between **temporary failures** and **permanent failures**.

## Polly Versions: What Should Beginners Learn?

If you search for Polly examples online, you may see older code such as:

```plaintext
// Older Polly APIs commonly used Policy objects.
Policy
```

Modern Polly introduced **Resilience Pipelines**.

The newer approach uses:

```plaintext
// Represents a reusable resilience pipeline.
ResiliencePipeline
```

and:

```plaintext
// Builds a resilience pipeline by adding strategies.
ResiliencePipelineBuilder
```

[Polly's current documentation](https://www.pollydocs.org/getting-started.html) focuses on the modern pipeline model.

So, if you're learning Polly today, focus on **Polly v8-style resilience pipelines**.

## Install Polly

For the core Polly API, install `Polly.Core`:

```plaintext
# Add the Polly core package to your .NET project.
dotnet add package Polly.Core
```

You can then use Polly from your C# application.

## Your First Polly Example

Let's start with a simple retry pipeline.

```cs
// Import Polly's resilience pipeline APIs.
using Polly;

// Create a new resilience pipeline.
var pipeline = new ResiliencePipelineBuilder()

    // Add a retry strategy to retry failed operations.
    .AddRetry(new RetryStrategyOptions
    {
        // Allow the operation to be retried three times.
        MaxRetryAttempts = 3
    })

    // Build the configured pipeline.
    .Build();

// Execute an operation through the resilience pipeline.
await pipeline.ExecuteAsync(async cancellationToken =>
{
    // Call the operation that may temporarily fail.
    await CallExternalApiAsync(cancellationToken);
});
```

The basic idea is:

```plaintext
Create Pipeline
      ↓
Add Strategy
      ↓
Build Pipeline
      ↓
Execute Operation
```

## Polly Resilience Strategies

The main strategies you should understand are:

| Strategy | What it does |
| --- | --- |
| Retry | Tries the operation again |
| Circuit Breaker | Stops calling an unhealthy service |
| Timeout | Prevents waiting too long |
| Fallback | Provides an alternative result |
| Rate Limiter | Controls the number of operations |
| Hedging | Uses an alternative operation when needed |

[Polly categorizes](https://www.pollydocs.org/strategies/index.html) these into reactive and proactive resilience strategies.

Let's understand each one.

![Resiliency Patterns - Let's Code KnownSense](https://www.codingknownsense.com/wp-content/uploads/2023/10/R-pattern-1.gif)

## 1. Retry

Retry is the easiest Polly concept.

It means:

> **If the operation fails temporarily, try it again.**

Without retry:

```plaintext
Request
   ↓
Failure
   ↓
Error
```

With retry:

```plaintext
Request
   ↓
Failure
   ↓
Retry
   ↓
Failure
   ↓
Retry
   ↓
Success
```

Example:

```cs
// Import Polly APIs.
using Polly;

// Create a resilience pipeline.
var pipeline = new ResiliencePipelineBuilder()

    // Add a retry strategy.
    .AddRetry(new RetryStrategyOptions
    {
        // Allow up to three retry attempts.
        MaxRetryAttempts = 3,

        // Wait two seconds before retrying.
        Delay = TimeSpan.FromSeconds(2)
    })

    // Build the pipeline.
    .Build();

// Execute the external operation.
await pipeline.ExecuteAsync(async cancellationToken =>
{
    // Execute an operation that may temporarily fail.
    await CallExternalApiAsync(cancellationToken);
});
```

## When Should You Use Retry?

Retry is useful when the failure is likely to disappear.

Good examples:

```plaintext
Temporary network failure
Temporary HTTP 503
Temporary connection failure
Temporary cloud-service failure
```

Don't blindly retry:

```plaintext
Invalid username/password
Invalid request
Permission denied
Invalid business data
```

Retrying a permanent error only wastes resources.

## Retry With Exponential Backoff

Imagine an API is overloaded.

If thousands of clients immediately retry, they can create even more traffic.

Instead of:

```plaintext
2 sec
2 sec
2 sec
```

you can gradually increase the delay:

```plaintext
1 sec
2 sec
4 sec
8 sec
```

This is called **exponential backoff**.

```cs
// Import Polly APIs.
using Polly;

// Create a resilience pipeline.
var pipeline = new ResiliencePipelineBuilder()

    // Configure the retry strategy.
    .AddRetry(new RetryStrategyOptions
    {
        // Allow four retry attempts.
        MaxRetryAttempts = 4,

        // Increase the delay after each failed attempt.
        BackoffType = DelayBackoffType.Exponential,

        // Add randomness to the retry delay.
        UseJitter = true
    })

    // Build the pipeline.
    .Build();
```

## What Is Jitter?

Imagine 1,000 application servers all fail at the same time.

If all of them retry exactly two seconds later:

```plaintext
Server 1 → retry at 2.0 sec
Server 2 → retry at 2.0 sec
Server 3 → retry at 2.0 sec
...
Server 1000 → retry at 2.0 sec
```

The external service suddenly receives another huge traffic spike.

> **Jitter** adds some randomness to retry timing.

For example:

```plaintext
Server 1 → 2.1 sec
Server 2 → 2.7 sec
Server 3 → 2.3 sec
Server 4 → 3.0 sec
```

This spreads requests over time.

## 2. Circuit Breaker

- Retry is useful when you believe the service may recover.
- But what if the service is completely down?
- Continuously retrying isn't a good idea.
- This is where [**Circuit Breaker** helps](https://www.pollydocs.org/strategies/circuit-breaker.html).
- Think about an electrical circuit breaker.
- When too much current flows, the breaker opens and stops the flow.
- Polly works similarly.

```plaintext
Normal
  ↓
Failures increase
  ↓
Circuit opens
  ↓
Requests stop
  ↓
Wait
  ↓
Test service
  ↓
Service recovered?
```

Polly's circuit breaker has three important states:

```plaintext
Closed
   ↓
Open
   ↓
Half-Open
```

- **Closed**

   - Everything is working normally.

- **Open**

   - The dependency is considered unhealthy, so calls are blocked.

- **Half-Open**

   - After the break duration, Polly allows an operation to test whether the dependency has recovered.

## Circuit Breaker Example

```cs
// Import Polly APIs.
using Polly;

// Create a resilience pipeline.
var pipeline = new ResiliencePipelineBuilder()

    // Add a circuit breaker strategy.
    .AddCircuitBreaker(new CircuitBreakerStrategyOptions
    {
        // Open the circuit when 50% of sampled operations fail.
        FailureRatio = 0.5,

        // Evaluate failures over a ten-second period.
        SamplingDuration = TimeSpan.FromSeconds(10),

        // Require at least eight operations before evaluating failures.
        MinimumThroughput = 8,

        // Keep the circuit open for 30 seconds.
        BreakDuration = TimeSpan.FromSeconds(30)
    })

    // Build the pipeline.
    .Build();
```

Circuit Breaker and Retry solve different problems.

## Retry:

> Try again because the problem might be temporary.

## Circuit Breaker:

> Stop calling because the dependency appears unhealthy.

They can be combined when appropriate.

## 3. Timeout

- What if the external service doesn't fail?
- What if it simply takes too long?

For example:

```plaintext
Request
   ↓
Waiting...
   ↓
Waiting...
   ↓
Waiting...
   ↓
Waiting...
```

Your application should not wait forever.

A timeout means:

> **Stop waiting after a specified amount of time.**

Example:

```cs
// Import Polly APIs.
using Polly;

// Create a resilience pipeline.
var pipeline = new ResiliencePipelineBuilder()

    // Stop an operation if it exceeds five seconds.
    .AddTimeout(TimeSpan.FromSeconds(5))

    // Build the pipeline.
    .Build();

// Execute the operation.
await pipeline.ExecuteAsync(async cancellationToken =>
{
    // Pass the cancellation token to the operation.
    await CallExternalApiAsync(cancellationToken);
});
```

Always pass the cancellation token to operations that support cancellation.

Polly's timeout strategy cancels execution after the configured timeout and can produce a `TimeoutRejectedException`.

## 4. Fallback

- Sometimes retrying isn't enough.
- Suppose your product API is unavailable, but you have cached product information.

Instead of:

```plaintext
Product API
    ↓
Failure
    ↓
Error
```

you could use:

```plaintext
Product API
    ↓
Failure
    ↓
Cache
    ↓
Return cached products
```

That's a **Fallback**.

Fallback means:

> **If the primary operation cannot succeed, provide a safe alternative.**

Example:

```cs
// Import Polly APIs.
using Polly;

// Create a typed pipeline because the operation returns a value.
var pipeline = new ResiliencePipelineBuilder<string>()

    // Add a fallback strategy.
    .AddFallback(new FallbackStrategyOptions<string>
    {
        // Handle exceptions from the primary operation.
        ShouldHandle = new PredicateBuilder<string>()
            .Handle<Exception>(),

        // Return this value when the primary operation fails.
        FallbackAction = static args =>
            Outcome.FromResultAsValueTask(
                "Service temporarily unavailable.")
    })

    // Build the pipeline.
    .Build();

// Execute the primary operation.
var result = await pipeline.ExecuteAsync(
    async cancellationToken =>
    {
        // Call the primary service.
        return await GetProductsAsync(cancellationToken);
    });
```

- Fallback is useful when you have a genuine alternative.
- Never use fallback to hide serious failures.

## 5. Rate Limiting

Suppose your application sends 10,000 requests per second, but the external API only accepts 100 requests per second.

Without protection:

```plaintext
Application
    ↓
10,000 requests
    ↓
External API
    ↓
429 Too Many Requests
```

A rate limiter controls how much traffic is allowed through the pipeline.

```plaintext
Application
    ↓
Rate Limiter
    ↓
Controlled requests
    ↓
External API
```

Rate limiting can protect both your application and downstream services.

## 6. Hedging

Hedging is a more advanced strategy.

Imagine you have two equivalent servers:

```plaintext
             Request
              /   \
             /     \
        Server A   Server B
             \     /
              \   /
             Response
```

- If one endpoint becomes unusually slow, an alternative execution can be used.
- Hedging can be useful in distributed systems where multiple equivalent endpoints exist.
- However, be very careful with operations that create side effects.
- For example, you should not casually send multiple payment requests.

## Combining Polly Strategies

The real power of Polly comes from combining strategies.

For example:

```plaintext
Application
    ↓
Timeout
    ↓
Retry
    ↓
Circuit Breaker
    ↓
External API
```

A pipeline can look like this:

```cs
// Import Polly APIs.
using Polly;

// Create a resilience pipeline.
var pipeline = new ResiliencePipelineBuilder()

    // Retry temporary failures.
    .AddRetry(new RetryStrategyOptions
    {
        // Allow three retry attempts.
        MaxRetryAttempts = 3,

        // Use exponential backoff.
        BackoffType = DelayBackoffType.Exponential,

        // Add random delay to avoid synchronized retries.
        UseJitter = true
    })

    // Limit the duration of an execution.
    .AddTimeout(TimeSpan.FromSeconds(10))

    // Stop sending requests when the dependency is unhealthy.
    .AddCircuitBreaker(new CircuitBreakerStrategyOptions
    {
        // Break when the failure ratio reaches 50%.
        FailureRatio = 0.5,

        // Analyze failures over ten seconds.
        SamplingDuration = TimeSpan.FromSeconds(10),

        // Require eight operations before evaluating the ratio.
        MinimumThroughput = 8,

        // Keep the circuit open for 30 seconds.
        BreakDuration = TimeSpan.FromSeconds(30)
    })

    // Build the final pipeline.
    .Build();
```

- Don't add every strategy just because Polly supports it.
- Each strategy should solve a real problem.

## Why Does Strategy Order Matter?

- Polly strategies are executed as a pipeline.
- The order can affect the behavior of the application.

For example, there is an important difference between:

```plaintext
5-second timeout per attempt
```

and:

```plaintext
5-second timeout for the complete operation
```

- when retries are involved.
- Therefore, don't randomly change the order of strategies.
- Define what you want the pipeline to accomplish first, then configure it accordingly.

## Polly With HttpClient

One of the most common uses of resilience is HTTP communication.

For example:

```plaintext
ASP.NET Core
      ↓
HttpClient
      ↓
Payment API
```

Modern .NET provides `Microsoft.Extensions.Http.Resilience`, which integrates HTTP clients with the .NET resilience infrastructure built around Polly. [Microsoft recommends this approach for resilient](https://learn.microsoft.com/en-us/dotnet/core/resilience/) `HttpClient` applications.

Install the package:

```plaintext
# Install Microsoft's HTTP resilience integration.
dotnet add package Microsoft.Extensions.Http.Resilience
```

Then you can configure a resilient HTTP client:

```cs
// Register ProductClient with dependency injection.
builder.Services
    .AddHttpClient<ProductClient>(client =>
    {
        // Set the base address of the external API.
        client.BaseAddress =
            new Uri("https://api.example.com");
    })

    // Add Microsoft's standard HTTP resilience handler.
    .AddStandardResilienceHandler();
```

This is a very convenient option for typical HTTP scenarios.

## Custom HttpClient Resilience

Sometimes you need custom behavior.

```cs
// Register the HTTP client.
builder.Services
    .AddHttpClient<ProductClient>(client =>
    {
        // Configure the external API address.
        client.BaseAddress =
            new Uri("https://api.example.com");
    })

    // Add a custom resilience pipeline to this HTTP client.
    .AddResilienceHandler("ProductApiResilience", pipeline =>
    {
        // Configure retry behavior.
        pipeline.AddRetry(new HttpRetryStrategyOptions
        {
            // Retry the request three times.
            MaxRetryAttempts = 3,

            // Use exponential backoff.
            BackoffType = DelayBackoffType.Exponential,

            // Add randomness to retry delays.
            UseJitter = true
        });

        // Stop an individual operation after ten seconds.
        pipeline.AddTimeout(TimeSpan.FromSeconds(10));
    });
```

This gives you more control when the default HTTP resilience configuration isn't appropriate.

## Polly and Dependency Injection

In a real ASP.NET Core application, you generally want reusable resilience pipelines instead of creating them repeatedly.

For example:

```plaintext
// Register a named resilience pipeline.
builder.Services.AddResiliencePipeline(
    "external-api",
    pipeline =>
    {
        // Add retry behavior.
        pipeline.AddRetry(new RetryStrategyOptions
        {
            // Retry three times.
            MaxRetryAttempts = 3,

            // Use exponential backoff.
            BackoffType = DelayBackoffType.Exponential,

            // Add jitter to retry delays.
            UseJitter = true
        });

        // Add a five-second timeout.
        pipeline.AddTimeout(TimeSpan.FromSeconds(5));
    });
```

This allows your application to define resilience behavior centrally.

## Different APIs May Need Different Resilience

Suppose your application communicates with:

```plaintext
Payment API
Product API
Shipping API
Email API
Search API
```

They don't necessarily need the same configuration.

For example:

```plaintext
Payment API
 → Conservative retry
 → Strict timeout
 → Circuit breaker

Product API
 → Retry
 → Cache fallback

Search API
 → Timeout
 → Possibly hedging

Email API
 → Retry
 → Rate limiting
```

Resilience should be designed according to the dependency.

## The Most Important Topic: Retry and Idempotency

This is something every developer should understand before adding retries.

Imagine:

```plaintext
POST /api/payment
```

- Your application sends a payment request.
- The payment server processes it successfully.
- But the network connection fails before your application receives the response.

Your application sees:

```plaintext
Request failed
```

If Polly retries:

```plaintext
POST /api/payment
```

the payment could potentially be processed twice.

Therefore:

> **Not every operation is safe to retry.**

Be especially careful with:

- Payments
- Orders
- Account creation
- Database writes
- Emails
- Other operations with side effects

For these operations, consider mechanisms such as:

- Idempotency keys
- Unique transaction IDs
- Server-side deduplication
- Database constraints

Polly can control the retry behavior, but **your application must determine whether retrying is safe**.

## Common Polly Mistakes

### 1. Retrying Everything

- Don't retry every exception.
- A `400 Bad Request` usually won't become valid simply because you send it again.

### 2. Too Many Retries

- More retries do not automatically mean more reliability.
- Too many retries can cause:

   - Increased latency
   - Higher server load
   - More resource consumption
   - Traffic spikes

### 3. No Timeout

A retry without a sensible timeout can still make users wait too long.

For example:

```plaintext
30-second request
+
3 retries
=
Potentially very long wait
```

Think about the **total time** your user is willing to wait.

### 4. Retrying Non-Idempotent Operations

- Be particularly careful with payment and order operations.
- A retry can create duplicate side effects if the underlying operation isn't idempotent.

### 5. Using Fallback to Hide Errors

Bad:

```plaintext
Payment failed
    ↓
Return "Payment successful"
```

Good:

```plaintext
Recommendation API failed
    ↓
Return cached recommendations
```

A fallback must represent a valid alternative.

### 6. Using Polly for Everything

You don't need Polly for a simple calculation:

```plaintext
// Calculate a value locally without external dependencies.
var total = price * quantity;
```

There is normally no transient external failure to handle.

Always start with:

> ## What can fail, and what should happen when it fails?

## Polly Is Not a Magic Error Handler

Polly doesn't make an unavailable service magically available.

For example:

```plaintext
External API permanently down
        ↓
Retry
        ↓
Retry
        ↓
Retry
        ↓
Still down
```

Eventually the operation needs to fail.

Resilience means:

```plaintext
Fail safely
Recover when possible
Protect dependencies
Avoid unnecessary load
Provide a useful response
```

## Monitoring Resilience

Adding Polly without monitoring isn't enough.

You should know:

```plaintext
How many retries occurred?
How many timeouts occurred?
How often did the circuit open?
How many fallbacks occurred?
How many requests were rate limited?
```

For example:

```plaintext
Product API
-----------------
Requests:       100,000
Retries:          2,500
Timeouts:           300
Circuit Opens:       12
Fallbacks:           150
```

If retries suddenly increase from:

```plaintext
2%
```

to:

```plaintext
30%
```

- there may be a serious problem with the external service.
- Polly should help you handle failures, not hide them.

## When Should You Use Polly?

Polly is particularly useful when your application communicates with:

- REST APIs
- Payment services
- Cloud services
- Microservices
- Email providers
- Authentication services
- Shipping services
- Third-party APIs
- Other distributed systems

Use Polly when temporary failures are possible and automatic recovery is safe.

## When Should You Not Use Polly?

Don't add resilience strategies just because they are available.

For example:

```plaintext
Simple calculation
Local string processing
Simple object mapping
Pure business logic
```

usually doesn't need retry or circuit breaking.

Polly is most valuable around operations that depend on resources that can temporarily fail or become unavailable.

## Polly vs Writing Your Own Retry Loop

You could write your own retry loop:

```cs
// Try the operation up to three times.
for (int attempt = 1; attempt <= 3; attempt++)
{
    try
    {
        // Execute the operation.
        await CallApiAsync();

        // Exit the loop when the operation succeeds.
        break;
    }
    catch
    {
        // Wait before trying again.
        await Task.Delay(1000);
    }
}
```

This looks simple.

But real applications quickly need:

```plaintext
Retry conditions
Exponential backoff
Jitter
Timeout
Cancellation
Circuit breaker
Fallback
Rate limiting
Exception handling
Telemetry
```

That's where a dedicated resilience library becomes valuable.

## A Practical ASP.NET Core Example

Imagine an ASP.NET Core application that calls an inventory service.

Architecture:

```plaintext
Client
  ↓
ASP.NET Core API
  ↓
InventoryClient
  ↓
Inventory API
```

Let's add retry and timeout.

```cs
// Register the inventory HTTP client.
builder.Services
    .AddHttpClient<InventoryClient>(client =>
    {
        // Configure the inventory API base address.
        client.BaseAddress =
            new Uri("https://inventory.example.com");
    })

    // Add a custom resilience pipeline for the inventory API.
    .AddResilienceHandler("InventoryResilience", pipeline =>
    {
        // Retry transient HTTP failures.
        pipeline.AddRetry(new HttpRetryStrategyOptions
        {
            // Allow three retry attempts.
            MaxRetryAttempts = 3,

            // Use exponential backoff.
            BackoffType = DelayBackoffType.Exponential,

            // Add randomness to retry timing.
            UseJitter = true
        });

        // Limit each operation to five seconds.
        pipeline.AddTimeout(TimeSpan.FromSeconds(5));
    });
```

Now the actual client can remain focused on its business responsibility:

```cs
// Define a client for communicating with the inventory service.
public class InventoryClient
{
    // Store the configured HttpClient.
    private readonly HttpClient _httpClient;

    // Receive HttpClient through dependency injection.
    public InventoryClient(HttpClient httpClient)
    {
        // Save the injected HttpClient.
        _httpClient = httpClient;
    }

    // Retrieve inventory information asynchronously.
    public async Task<string> GetInventoryAsync(
        int productId,
        CancellationToken cancellationToken)
    {
        // Build the inventory endpoint URL.
        var url = $"/api/inventory/{productId}";

        // Send the HTTP GET request.
        var response = await _httpClient.GetAsync(
            url,
            cancellationToken);

        // Throw an exception for an unsuccessful response.
        response.EnsureSuccessStatusCode();

        // Read and return the response content.
        return await response.Content.ReadAsStringAsync(
            cancellationToken);
    }
}
```

- Notice that the client doesn't contain retry loops.
- The resilience behavior is configured separately.
- This is one of the biggest advantages of using resilience pipelines.

## How Should a Beginner Think About Polly?

Don't try to memorize every Polly configuration property.

Ask these questions.

### Is this failure temporary?

Use:

- **Retry**

### Is the dependency continuously failing?

Consider:

- **Circuit Breaker**

### Can the operation take too long?

Use:

- **Timeout**

### Do I have a safe alternative?

Consider:

- **Fallback**

### Can too much traffic overload the dependency?

Consider:

- **Rate Limiter**

### Do I have multiple equivalent endpoints and unpredictable latency?

Consider:

- **Hedging**

This mental model is more useful than memorizing APIs.

## Simple Polly Decision Table

| Problem | Recommended Strategy |
| --- | --- |
| Temporary network error | Retry |
| Temporary HTTP 503 | Retry |
| Repeated dependency failures | Circuit Breaker |
| API responds too slowly | Timeout |
| Primary service unavailable | Fallback |
| Too many requests | Rate Limiter |
| One endpoint is unusually slow | Hedging |

## Polly and Modern .NET

For modern .NET applications, you will encounter both Polly and Microsoft's resilience integration.

The ecosystem can be thought of like this:

```plaintext
Polly
  |
  +-- Polly.Core
  |
  +-- Microsoft.Extensions.Resilience
          |
          +-- Microsoft.Extensions.Http.Resilience
```

`Microsoft.Extensions.Http.Resilience` is especially useful when applying resilience to `HttpClient`.

[Microsoft's current .NET resilience documentation](https://learn.microsoft.com/en-us/dotnet/core/resilience/) recommends these newer resilience packages and notes that the older `Microsoft.Extensions.Http.Polly` package is deprecated.

## Final Mental Model

If you're completely new to Polly, remember this:

```plaintext
                  Polly
                    |
        +-----------+-----------+
        |           |           |
      Retry      Timeout    Circuit Breaker
        |           |           |
     Try again   Don't wait   Stop calling
        |
        +------ Fallback
        |
        +------ Rate Limiter
        |
        +------ Hedging
```

And remember the main pipeline:

```plaintext
Your Application
       ↓
Resilience Pipeline
       ↓
External Dependency
```

The pipeline determines how your application should behave when that dependency becomes unreliable.

## Conclusion

Polly is much more than a retry library.

It provides a structured way to build **resilient .NET applications** that can handle temporary failures, slow services, overloaded dependencies, and unavailable APIs.

The most important strategies to understand are:

- **Retry** — try again when a temporary failure may recover.
- **Circuit Breaker** — stop calling an unhealthy dependency.
- **Timeout** — don't wait indefinitely.
- **Fallback** — provide a safe alternative.
- **Rate Limiter** — control traffic.
- **Hedging** — use alternative executions when latency is a problem.
- **Resilience Pipeline** — combine strategies into a reusable pipeline.

The most important lesson for a beginner isn't remembering every Polly API.

It's asking:

> ## What can go wrong with this operation, and what should my application do when it happens?

Once you start thinking this way, Polly becomes much easier to understand.

For modern ASP.NET Core applications, you can use Polly directly through resilience pipelines or use Microsoft's `Microsoft.Extensions.Resilience` and `Microsoft.Extensions.Http.Resilience` integrations when they fit your architecture.

Good resilience isn't about hiding failures.

**It's about handling failures intelligently.**

---

Original Source: https://answers.mindstick.com/blog/542/polly-in-dot-net-complete-beginner-s-guide-to-resilience

Copyright © MindStick Software Pvt. Ltd. This Markdown version is provided for developers, AI systems, and offline reading.
