Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Mapping External Service Integration: Failure Modes, Anti-Corruption Layer and Choosing a Client (Hand-Written, Refit, Kiota)
The first job in an external integration is to write down the expected behavior for every failure mode and translate the provider's model and errors into our own types with a port and adapter. Using Bookstore's fake payment provider and carrier API, we show the error model, the ProblemDetails mapping, and how a hand-written client, Refit and Kiota really behave when the schema changes.
IHttpClientFactory in Depth: Named, Typed and Keyed Clients, Handler Lifetime and DelegatingHandlers
IHttpClientFactory returns a new HttpClient on every CreateClient call but shares the handler chain for two minutes by default, and since .NET 9 keyed clients are recommended over typed clients. We show handler lifetime, the client captured by a singleton, the handler's separate scope, a DelegatingHandler that caches an OAuth token, log redaction, and HttpClient.Timeout versus CancellationToken, all with real output.
The Standard Pipeline with Microsoft.Extensions.Http.Resilience: Defaults, Settings and Pitfalls
AddStandardResilienceHandler adds, outside in, a rate limiter, a 30 s total timeout, 3 retries, a circuit breaker and a 10 s attempt timeout; by default it also retries POST and sets HttpClient.Timeout to infinite. With real output we show separate pipelines for Bookstore's payment, carrier and exchange-rate clients, configuration from appsettings, the validation rules, how two handlers multiply retries, and hedging.
Polly v8 Strategies in Depth: Retry, Timeout, Circuit Breaker, Rate Limiter and Fallback
In Polly v8 the order of strategies decides behavior: total timeout outside, retry in the middle, attempt timeout inside; MaxDelay doesn't cap Retry-After, and a timeout can't stop code that ignores the token. In Bookstore we build, with real output, a named pipeline for SMTP, a retry that honors Retry-After, a circuit breaker with manual control and a health check, a concurrency limiter, and a flat-tariff fallback for carrier prices.
Testing and Observing Resilience: WireMock.Net, Polly Chaos Strategies, FakeTimeProvider and Metrics
Resilience behavior is verified with integration tests against the real API using WireMock.Net, backoff is tested without waiting using FakeTimeProvider, Polly's chaos strategies inject failures in staging, and the resilience.polly.strategy.events metric makes retries and the breaker visible. In Bookstore we test that the payment POST is never retried, checkout under 20% failures, and the metrics.
Idempotent Outbound Calls: Sending an Idempotency-Key, Unknown Outcomes and Reconciliation
After a timeout you cannot know whether the card was charged; the fix is to create one Idempotency-Key per payment intent, store it before the first request, resend the same key on every retry, and let a reconciliation job ask the provider about open attempts. In Bookstore we watch timeouts, 409s and replayed responses in real runs.
Sending Webhooks: Standard Webhooks, Signing, Outbox-Based Delivery and Retry Schedules
Send webhooks from an outbox in the background, not inside the request; sign every request with the Standard Webhooks headers (webhook-id, webhook-timestamp, webhook-signature) and HMAC-SHA256, follow a retry schedule spanning days, and disable endpoints that keep failing. This is how Bookstore delivers order.shipped to its institutional customers.
Receiving Webhooks: Signature Verification, Replay Protection, Fast 2xx and Idempotent Processing
Verify an incoming webhook's signature over the raw body before model binding with a constant-time comparison, block replays with a timestamp tolerance and webhook-id, store the event in an inbox and return 200 right away, and process it idempotently in the background. In Bookstore, payment.succeeded closes unknown payment outcomes.
What Changed in HttpClient and Resilience: From Microsoft.Extensions.Http.Polly to Http.Resilience, Polly v7 to v8 and .NET 11
Microsoft.Extensions.Http.Resilience replaced Microsoft.Extensions.Http.Polly, and in Polly v8 policies became a single ResiliencePipeline; old code can be migrated step by step with the AsAsyncPolicy bridge. .NET 11 adds request compression, an experimental connection eviction callback and typed DNS records.