Options Pattern & Config Binding
On this page
Why the Options Pattern Matters in Production
Configuration is production behavior. A single incorrect value can: - disable authentication - point to the wrong database - remove timeouts and cause cascading failures - enable debug logging and flood disks The Options pattern is how you bind configuration safely and predictably. If you misuse it, you ship silent misconfigurations that only explode after deployment.Real Production Incident
Symptoms: - After deployment, outbound requests begin timing out. - Latency gradually increases. - Error rate spikes under moderate load. Root cause: - Timeout configuration existed but was not correctly bound. - Code fell back to a default (effectively infinite timeout). - Requests accumulated, threads blocked, and the service degraded. This was not a networking problem. It was a configuration binding failure.Symptom → Cause → Diagnosis → Fix
Symptom: - Behavior differs between local and production. - Config appears defined but defaults are still used. Cause: - Wrong configuration section name - Incorrect class shape for binding - Missing validation - Using raw IConfiguration lookups - Injecting wrong Options interface type Diagnosis: - Log effective non-sensitive config values at startup. - Enable ValidateOnStart(). - Verify environment variable override behavior in the target environment. Fix: - Use strongly typed options. - Add validation rules. - Fail startup on invalid config.Anti-Pattern: IConfiguration String Access
This approach is fragile and unvalidated:var timeoutMs = int.Parse(builder.Configuration["Http:TimeoutMs"]); services.AddSingleton(new SomeClient(timeoutMs));Problems: - Missing keys cause runtime failures. - Typos silently break configuration. - No range enforcement. - No discoverability.
Correct Pattern: Strongly Typed Options
Define the options class:
public sealed class HttpClientOptions
{
public int TimeoutMs { get; init; } = 5000;
public int RetryCount { get; init; } = 2;
}
Bind and validate:
builder.Services
.AddOptions()
.Bind(builder.Configuration.GetSection("HttpClient"))
.Validate(o => o.TimeoutMs >= 100 && o.TimeoutMs <= 60000, "TimeoutMs out of range")
.Validate(o => o.RetryCount >= 0 && o.RetryCount <= 10, "RetryCount out of range")
.ValidateOnStart();
Production rule:
If the service cannot run safely, it should not start.