George Wall ®

Compile-time safety · configuration

Catching broken appsettings before deployment.

Configuration is code with none of code’s usual protection. A one-letter section typo can compile cleanly, pass review and wait until startup to introduce itself.

Strongly typed Options make configuration pleasant to consume. They do not make the configuration itself strongly typed. The C# model and the JSON file can drift in separate pull requests, with separate reviewers, and the compiler is perfectly happy with both.

That gap explains a familiar class of production failure: the application builds, the deployment succeeds, and the process then discovers that a section was renamed, a required value is absent or the string "eighty" will not bind to an integer port.

Binding is not validation

This registration creates an Options object, but by itself it does not prove the section exists or force every validation rule to run when the application starts.

services.AddOptions<PaymentsOptions>()
    .BindConfiguration("Paymnets"); // typo compiles cleanly
The string and the JSON section have no compile-time relationship.

Data annotations and startup validation close part of the gap. They turn missing required values into an early process failure instead of a later null reference or bad request.

services.AddOptions<PaymentsOptions>()
    .BindConfiguration("Payments")
    .ValidateDataAnnotations()
    .ValidateOnStart();
Useful protection, provided the correct section is bound and validation is actually enabled.

The failures that remain

Even a careful registration leaves questions the compiler normally cannot answer:

  • Does Payments exist in any visible appsettings*.json file?
  • Does every required Options property have a matching key?
  • Is a JSON key merely unknown, or will strict binding make it fatal?
  • Can the configured scalar convert to the target CLR type?
  • Are nested Options objects being validated recursively?

Integration tests can answer those questions, but only for the configuration files and environment combinations the test loads. Production overrides, renamed properties and small spelling mistakes still have room to hide.

Treat JSON as part of the program

An analyser can inspect the Options registrations and the JSON additional files together. When both sides are statically visible, it can join the section literal to the document, walk the Options shape and report the mismatch on the line the developer is editing.

The important boundary is certainty. A dynamic section name, a custom configuration provider or a value assembled at runtime should not produce a confident warning. High-signal tooling reports what it can prove and stays quiet where the runtime owns the answer.

Generate the contract in the other direction

The same Options model can also produce a JSON Schema. Point the editor at that schema and appsettings.json gains autocomplete, type checks, required-key hints and unknown-property warnings before the application is built.

dotnet tool install --global ConfigContraband.Tool
configcontraband schema --project src/MyApp/MyApp.csproj
One model becomes both the runtime Options type and the editor contract for its JSON.

ConfigContraband applies both sides of that approach: Roslyn diagnostics for provable configuration drift and a companion schema tool for live JSON guidance. The deployment should still validate its real environment. It simply should not be the first place a spelling mistake gets a fair hearing.

If configuration can stop the application, it deserves feedback before the application starts.

← Back to writing