Getting Started
Install
dotnet add package Kevlar
Optional satellites:
dotnet add package Kevlar.Chaos # controlled fault injection
dotnet add package Kevlar.Extensions.DependencyInjection # named shields + IKevlarRegistry
dotnet add package Kevlar.Extensions.Http # HttpClientFactory integration
The core targets netstandard2.0 (so .NET Framework 4.6.2+ works) and net10.0.
Your first shield
using Kevlar;
var shield = Shield
.Timeout(TimeSpan.FromSeconds(30)) // total budget for the whole operation
.Retry(3) // exponential backoff + jitter, out of the box
.CircuitBreaker(5, breakDuration: TimeSpan.FromSeconds(30));
var user = await shield.ExecuteAsync(ct => LoadUserAsync(id, ct), cancellationToken);
Three things to notice:
- Reading order is execution order. The first strategy in the chain is the outermost — the timeout wraps the retries, which wrap the circuit breaker. Same rule as ASP.NET middleware.
- The defaults are the ones you'd have picked.
Retry(3)means exponential backoff with jitter, starting at 250ms and capped at 30s. - Your delegate gets a cancellation token. Always use the token you're handed — it's how timeouts and hedging cancel abandoned work.
Reuse it everywhere
Shields are immutable and thread-safe. Build one, store it in a static readonly field or register it in DI, and use it for every call to that dependency:
private static readonly Shield GitHubShield = Shield
.Timeout(TimeSpan.FromSeconds(10))
.Retry(3);
// Any result type, sync or async, through the same instance:
var repos = await GitHubShield.ExecuteAsync(ct => GetReposAsync(ct), ct);
var user = await GitHubShield.ExecuteAsync(ct => GetUserAsync(ct), ct);
This matters for stateful strategies: a circuit breaker's state lives with the shield instance that created it. Reuse the instance and every call site shares one circuit; build a new instance and you get fresh state. See Composition.
Deciding what counts as a failure
Reactive strategies (retry, circuit breaker, hedging, fallback) act on failures. By default that's any exception except OperationCanceledException. Narrow it with a handling clause:
var shield = Shield
.When<HttpRequestException>()
.Or<TimeoutExceededException>()
.Retry(5);
Want to treat certain results as failures too (HTTP 500s, say)? Lift into a typed shield with For<T>:
var http = Shield.For<HttpResponseMessage>()
.When<HttpRequestException>()
.WhenResult(r => (int)r.StatusCode >= 500)
.Retry(3);
Full details in Handling failures.
Next steps
- Browse the strategy reference — each strategy's options, defaults and semantics.
- Wire shields into dependency injection or HttpClient.
- Test your shields without real waiting, using
TimeProvider.