# Azure SDK Best Practices and Patterns

> Chapter 88 of the Courseiva AZ-204 curriculum — https://courseiva.com/learn/az-204/azure-sdk-patterns-204

**Official objective:** 1.1 — Compute

## Introduction

This chapter covers the essential best practices and design patterns for using the Azure SDK effectively when building cloud-native applications for the AZ-204 exam. Understanding these patterns is critical because approximately 15-20% of exam questions directly test your knowledge of SDK usage, including retry policies, transient fault handling, logging, and authentication. Mastering these practices will not only prepare you for the exam but also enable you to build production-grade, resilient Azure solutions.

## The Blueprint Blueprint: Azure SDK Patterns as Construction Standards

Imagine a large construction company that builds skyscrapers. Over time, they've learned that every successful building project follows a set of standard blueprints and processes: they always use the same foundation design (retry logic), the same safety netting (logging), and the same testing protocols (unit tests). These standards are documented in a 'Blueprint Blueprint' — a meta-standard that guides how blueprints are created. In Azure development, the Azure SDK is like a set of pre-built, tested components (like pre-stressed concrete beams) that you assemble according to best practices. The best practices themselves (retry policies, circuit breakers, cancellation tokens) are the 'Blueprint Blueprint' — the proven patterns that ensure your cloud application is resilient, secure, and maintainable. Just as a construction crew must follow the blueprint to avoid collapse, a developer must follow SDK best practices to avoid failures, cost overruns, and security breaches. The SDK provides the materials; the best practices provide the instructions for using them correctly.

## Core explanation

### What Are Azure SDK Best Practices and Patterns?

The Azure SDK is a collection of libraries for multiple programming languages (C#, Python, JavaScript, Java, Go, C++) that provide a consistent interface to interact with Azure services. Best practices are the proven guidelines for using these libraries to build robust, scalable, and secure applications. Patterns are reusable solutions to common problems, such as handling transient faults or managing configuration.

### Why They Exist

Cloud applications face unique challenges: network latency, transient failures, rate limiting, and security threats. Without structured patterns, applications become fragile, hard to maintain, and insecure. The Azure SDK itself incorporates many of these patterns (e.g., built-in retry logic), but developers must still apply higher-level patterns like circuit breakers and distributed tracing.

### How It Works Internally

#### Retry Policies

- **Exponential Backoff**: The SDK automatically retries failed requests after increasing delays. Default: initial delay 0.8 seconds, max delay 60 seconds, retry count up to 3.
- **Jitter**: Randomizes the delay to avoid thundering herd problem. The SDK adds ±20% random variation to the computed delay.
- **Retry Modes**: `Exponential` (default) and `Fixed`. Exponential grows delay as 2^n * initial_delay. Fixed uses constant delay.
- **Retry Filters**: Only certain HTTP status codes trigger retries: 408 (Timeout), 429 (Too Many Requests), 500+ (Server Errors), and transport-level errors.

#### Circuit Breaker Pattern

- Monitors failure rate over a sliding window (e.g., last 60 seconds).
- When failure rate exceeds threshold (e.g., 50%), circuit opens and requests fail immediately for a cooldown period (e.g., 30 seconds).
- After cooldown, circuit enters half-open state, allowing a probe request. If successful, circuit closes; if fails, it opens again.
- The Azure SDK does not implement circuit breakers natively; you must use libraries like Polly.

#### Cancellation Tokens

- Every asynchronous SDK method accepts a `CancellationToken` parameter.
- When cancellation is requested, the SDK stops further processing and throws `OperationCanceledException`.
- Default timeout for SDK operations is 100 seconds for most services, but you can set it per request.

#### Logging and Telemetry

- The SDK integrates with `ILogger` (ASP.NET Core) or `OpenTelemetry`.
- Log levels: Debug, Information, Warning, Error, Critical.
- SDK emits events for: request start/end, retry attempts, authentication, and throttling.
- You can configure logging via `ServiceClientOptions.Diagnostics`.

#### Authentication and Authorization

- DefaultAzureCredential: tries multiple credential sources in order: Environment, Managed Identity, Visual Studio, Azure CLI, Interactive Browser.
- Token caching: tokens are cached for their lifetime (typically 1 hour) to reduce authentication calls.
- Scope: each service requires specific scopes (e.g., `https://storage.azure.com/.default`).

### Key Components, Values, Defaults, and Timers

- **RetryPolicy**: `MaxRetries` (default 3), `Delay` (default 0.8s), `Mode` (Exponential).
- **CircuitBreakerPolicy**: `FailureThreshold` (e.g., 0.5), `SamplingDuration` (e.g., 60s), `DurationOfBreak` (e.g., 30s).
- **CancellationToken**: `Timeout` (default 100s for most services).
- **HttpClient**: Max connections per server = 10 (default). Use `IHttpClientFactory` to manage.
- **Logging**: `SetLogLevel` for each service.

### Configuration and Verification Commands

#### .NET (C#) Example

```csharp
var client = new BlobServiceClient(
    new Uri("https://mystorage.blob.core.windows.net"),
    new DefaultAzureCredential(),
    new BlobClientOptions
    {
        Retry = {
            Mode = RetryMode.Exponential,
            MaxRetries = 5,
            Delay = TimeSpan.FromSeconds(1),
            MaxDelay = TimeSpan.FromSeconds(10)
        },
        Diagnostics = {
            IsLoggingEnabled = true,
            LoggedContentSizeLimit = 4096
        }
    });
```

#### Python Example

```python
from azure.storage.blob import BlobServiceClient
from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential()
client = BlobServiceClient(
    account_url="https://mystorage.blob.core.windows.net",
    credential=credential,
    retry_total=5,
    retry_mode='exponential',
    retry_backoff_factor=1
)
```

#### Verification: Check Logs

```csharp
// Configure logging in Startup.cs
builder.Logging.AddAzureWebAppDiagnostics();
// Then check Application Insights or Azure Monitor for SDK logs
```

### How It Interacts with Related Technologies

- **Azure Functions**: Use `ILogger` injection; SDK logging integrates automatically.
- **App Service**: Logging and metrics are forwarded to Azure Monitor.
- **Application Insights**: Distributed tracing correlates SDK calls across services.
- **Azure Policy**: Can enforce certain SDK configurations (e.g., minimum TLS version).

### Common Pitfalls

- Not using `IHttpClientFactory` leads to socket exhaustion.
- Ignoring cancellation tokens causes zombie requests.
- Overriding retry policy to zero retries makes apps brittle.
- Not configuring logging makes debugging impossible.
- Using connection strings instead of managed identities reduces security.

### Advanced Patterns

- **Exponential Backoff with Jitter**: Prevents thundering herd.
- **Retry with Circuit Breaker**: Protects downstream services.
- **Bulkhead Isolation**: Limits concurrent calls to a service.
- **Cache-Aside**: Store results of expensive SDK calls in a cache (e.g., Redis).
- **Saga Pattern**: For distributed transactions across multiple SDK calls (e.g., Azure Cosmos DB + Service Bus).

### Code Example: Retry with Polly and Azure SDK

```csharp
using Polly;
using Polly.Extensions.Http;

var retryPolicy = HttpPolicyExtensions
    .HandleTransientHttpError()
    .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
    .WaitAndRetryAsync(3, retryAttempt => 
        TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));

var circuitBreakerPolicy = HttpPolicyExtensions
    .HandleTransientHttpError()
    .CircuitBreakerAsync(2, TimeSpan.FromSeconds(30));

var policyWrap = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy);

var httpClient = new HttpClient();
var response = await policyWrap.ExecuteAsync(() => httpClient.GetAsync(url));
```

## Real-world context

### Enterprise Scenario 1: E-Commerce Checkout Microservice

A large retailer built a checkout microservice that calls Azure SQL Database, Azure Cosmos DB, and Azure Service Bus. Initially, they used default SDK retry policies (3 retries, exponential backoff). During Black Friday, a sudden spike in traffic caused Cosmos DB to throttle requests (HTTP 429). The default retries were insufficient; many requests failed after exhausting retries. They implemented a circuit breaker with Polly: after 10 failures in 30 seconds, the circuit opened for 60 seconds. This prevented cascading failures and allowed Cosmos DB to recover. They also added jitter to retries to avoid thundering herd. The circuit breaker reduced error rates from 15% to under 1%.

### Enterprise Scenario 2: IoT Device Telemetry Ingestion

A manufacturing company ingests telemetry from thousands of IoT devices into Azure Event Hubs. They used the Azure SDK for .NET to send events. Initially, they did not use cancellation tokens, causing memory leaks due to abandoned tasks. After implementing `CancellationTokenSource` with a 10-second timeout per batch, memory usage dropped by 40%. They also configured `DefaultAzureCredential` with managed identity for the IoT hub. For logging, they enabled SDK diagnostics and forwarded logs to Azure Monitor, which helped identify network latency issues.

### Common Misconfigurations and Failures

- **No circuit breaker**: A downstream service failure causes all SDK calls to fail slowly, leading to thread pool starvation.
- **Too many retries**: Setting `MaxRetries` to 10 with long delays can cause requests to take minutes, timing out the client.
- **Ignoring logging**: Without logging, transient failures are invisible, making debugging nearly impossible.
- **Using connection strings**: Hardcoded secrets in code lead to security breaches and rotation headaches.
- **Overriding retry policy to zero**: Some developers disable retries thinking they will handle failures themselves, but they often forget, making the app brittle.

## Exam focus

### What AZ-204 Tests on This Topic

The AZ-204 exam covers SDK best practices under objective 'Develop Azure compute solutions' (20-25%) and 'Develop for Azure storage' (15-20%). Specific sub-objectives include:
- Implement IAsyncDisposable and cancellation tokens.
- Configure retry policies for transient faults.
- Implement circuit breaker pattern.
- Use DefaultAzureCredential for authentication.
- Configure logging and telemetry.

### Common Wrong Answers and Why Candidates Choose Them

1. **'Use a fixed retry interval of 5 seconds with 10 retries.'** Candidates think more retries are better, but this causes thundering herd and long delays. The correct pattern is exponential backoff with jitter and a limited number of retries (3-5).
2. **'Disable retries to improve performance.'** Candidates think retries waste time, but transient failures are common in cloud. Without retries, the app fails on first network blip. The correct approach is to use retries with circuit breaker.
3. **'Use connection strings for simplicity.'** Candidates choose this because it's easier to code. However, connection strings expose secrets and don't support managed identities. The correct approach is DefaultAzureCredential.
4. **'Skip cancellation tokens to simplify code.'** Candidates think they add complexity. Without them, requests hang and resources leak. Always use cancellation tokens.

### Specific Numbers and Terms That Appear on the Exam

- Default retry count: 3
- Default exponential backoff delay: 0.8 seconds
- Default max delay: 60 seconds
- Circuit breaker failure threshold: often 50% or a count like 5
- Circuit breaker break duration: typically 30-60 seconds
- CancellationToken default timeout: 100 seconds (for many SDKs)
- DefaultAzureCredential credential chain order: Environment > Managed Identity > Visual Studio > Azure CLI > Interactive

### Edge Cases and Exceptions

- **Rate limiting**: Some services (e.g., Azure Key Vault) have strict rate limits. The SDK retries on 429 but may still fail if limit is low. Use circuit breaker to back off.
- **Non-retryable errors**: 400 (Bad Request) and 401 (Unauthorized) are never retried. Don't waste retries on them.
- **Idempotency**: Retries can cause duplicate operations. Ensure your SDK calls are idempotent (e.g., using ETags or idempotency keys).
- **Managed Identity availability**: Managed Identity is only available for Azure-hosted resources (App Service, VMs, Functions). For local dev, you need other credentials.

### How to Eliminate Wrong Answers Using the Underlying Mechanism

- If an answer suggests disabling retries, think: 'Transient faults are inevitable; retries are essential.' Eliminate.
- If an answer uses a fixed delay without jitter, think: 'Thundering herd will occur.' Eliminate.
- If an answer proposes storing secrets in code, think: 'Security best practice is to use managed identities or Key Vault.' Eliminate.
- If an answer omits cancellation tokens, think: 'Resource leaks and hung requests.' Eliminate.

## Step by step

1. **Configure Retry Policy** — Start by setting the retry policy on the service client options. Use exponential backoff with jitter for most scenarios. The default retry count is 3, but for critical operations you may increase to 5. Set the initial delay to at least 1 second to avoid immediate retries on transient failures. For Azure Storage, the SDK automatically retries on 429 and 5xx errors. You can also customize which errors trigger retries via `RetryPolicy` delegates.
2. **Implement Circuit Breaker** — Wrap your SDK calls with a circuit breaker using a library like Polly. Define a threshold (e.g., 5 failures within 60 seconds) and a break duration (e.g., 30 seconds). When the circuit opens, subsequent calls fail immediately without hitting the SDK, protecting the downstream service from overload. After the break, allow a probe request. If it succeeds, close the circuit. This pattern is essential for dependencies like Azure SQL Database or Cosmos DB.
3. **Use Cancellation Tokens** — Always pass a `CancellationToken` to SDK methods. Create a token with a timeout using `CancellationTokenSource(TimeSpan.FromSeconds(30))`. This prevents requests from hanging indefinitely. In ASP.NET Core, the framework provides `HttpContext.RequestAborted` for client disconnections. If you don't use cancellation tokens, requests may continue processing after the client has disconnected, wasting resources.
4. **Enable Logging and Telemetry** — Configure the SDK's `Diagnostics` settings to enable logging. In .NET, set `IsLoggingEnabled = true` and optionally restrict log content size. Integrate with `ILogger` to forward logs to Application Insights or Azure Monitor. Use `OpenTelemetry` for distributed tracing. This allows you to monitor SDK behavior, detect throttling, and debug issues. Without logging, you are blind to transient failures.
5. **Authenticate with DefaultAzureCredential** — Use `DefaultAzureCredential` for authentication in production. It tries multiple sources in order: environment variables (AZURE_CLIENT_ID, etc.), managed identity (if running on Azure), Visual Studio, Azure CLI, and interactive browser. This works for both local development and cloud deployment. Avoid hardcoding connection strings. For specific scenarios, use `ClientSecretCredential` or `ManagedIdentityCredential` explicitly.

## Comparisons

### Exponential Backoff Retry vs Fixed Interval Retry

**Exponential Backoff Retry:**
- Delay increases exponentially: 2^n * initial_delay
- Reduces thundering herd effect
- More resilient to transient faults
- Default in Azure SDK
- Requires jitter for optimal performance

**Fixed Interval Retry:**
- Delay is constant (e.g., 5 seconds)
- Can cause thundering herd if many clients retry simultaneously
- Less effective for bursty failures
- Not recommended for cloud services
- Simpler to implement but less robust

### DefaultAzureCredential vs Connection String

**DefaultAzureCredential:**
- No secrets in code
- Supports managed identity in Azure
- Works in local development without changes
- Automatically rotates credentials
- Requires appropriate RBAC roles

**Connection String:**
- Contains secrets that must be protected
- Does not support managed identity
- Hard to rotate without redeployment
- Exposes connection details
- Simpler for quick prototypes but insecure

## Common misconceptions

- **Misconception:** More retries always make the application more resilient. **Reality:** Excessive retries can cause thundering herd, increase latency, and exhaust resources. Best practice is 3-5 retries with exponential backoff and jitter.
- **Misconception:** Circuit breaker and retry are the same thing. **Reality:** Retry repeats a failed operation; circuit breaker stops all calls to a failing service for a period. They are complementary: retry handles transient faults, circuit breaker prevents cascading failures.
- **Misconception:** Cancellation tokens are optional and can be omitted. **Reality:** Omitting cancellation tokens can lead to resource leaks, hung requests, and unresponsive applications. They are essential for graceful shutdown and timeout handling.
- **Misconception:** DefaultAzureCredential is only for local development. **Reality:** DefaultAzureCredential is designed for both local development and production. It tries managed identity in Azure and falls back to other credentials locally.
- **Misconception:** Logging is only for debugging and should be disabled in production. **Reality:** Production logging is critical for monitoring, auditing, and troubleshooting. Azure SDK logging can be configured to emit only warnings/errors to minimize overhead.

## Key takeaways

- Always use exponential backoff retry with jitter; default retry count is 3, initial delay 0.8s.
- Implement circuit breaker to protect downstream services; typical break duration is 30-60s.
- Pass CancellationToken to all async SDK methods; set timeouts to prevent hanging.
- Use DefaultAzureCredential for authentication; never hardcode connection strings.
- Enable SDK logging and integrate with Application Insights for observability.
- Use IHttpClientFactory to manage HttpClient instances and avoid socket exhaustion.
- Ensure idempotency of SDK operations to safely retry without side effects.

## FAQ

**What is the default retry policy in the Azure SDK for .NET?**

The default retry policy uses exponential backoff with a maximum of 3 retries, an initial delay of 0.8 seconds, and a maximum delay of 60 seconds. It retries on HTTP 408, 429, 5xx, and transport-level errors. You can customize it via the `Retry` property in client options.

**How do I implement a circuit breaker with the Azure SDK?**

The Azure SDK does not include a built-in circuit breaker. You must use a library like Polly. Wrap your SDK calls in a Polly policy that monitors failures and opens the circuit when a threshold is exceeded. For example, use `HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(2, TimeSpan.FromSeconds(30))`.

**What is the purpose of CancellationToken in Azure SDK methods?**

CancellationToken allows you to cancel an ongoing SDK operation. This is important for implementing timeouts, responding to user cancellations, or shutting down gracefully. If not provided, the operation may run indefinitely, causing resource leaks. Always create a CancellationTokenSource with a timeout and pass its token.

**How does DefaultAzureCredential work?**

DefaultAzureCredential tries multiple authentication sources in order: environment variables (AZURE_TENANT_ID, etc.), managed identity (if running on Azure), Visual Studio account, Azure CLI, and interactive browser. It returns the first successfully obtained token. This allows code to work both locally and in Azure without changes.

**Should I disable retries to improve performance?**

No. Transient faults are common in cloud environments. Disabling retries makes your application brittle. Instead, use a well-configured retry policy with exponential backoff and a circuit breaker to handle persistent failures. This balances performance and resilience.

**How do I configure logging for the Azure SDK?**

In .NET, set `clientOptions.Diagnostics.IsLoggingEnabled = true` and optionally `LoggedContentSizeLimit`. Integrate with `ILogger` by calling `builder.Logging.AddAzureWebAppDiagnostics()` in ASP.NET Core. For Python, use the `logging` module to set the level for `azure` loggers.

**What is the difference between retry and circuit breaker?**

Retry repeats a failed operation a limited number of times, hoping it will succeed. Circuit breaker stops all calls to a failing service for a period to give it time to recover. Retry handles transient faults; circuit breaker prevents cascading failures and resource exhaustion.

---

Interactive version with quiz and diagrams: https://courseiva.com/learn/az-204/azure-sdk-patterns-204
