Reduce Review Fatigue in the AI Coding Era: My .NET Guardrails
In the AI coding era, we type less and let the machine do the heavy-lifting. However, unlike the productivity boost from compilers, assemblers, linkers, high-level languages and IDEs, LLM output is not deterministic, yet.
In just a few years, our productivity has shifted from trying to type as fast as we can read and think, to having coding agents generate more code than we can realistically review. If the output quality is poor, it slows us down rather than speeding us up.
In order to have the LLM to produce production quality code while minimize our mental energy to review the code it produces, setting up good guardrails is essential.
Since LLMs donβt get tired, the strictest compiler/analyser rules can detect when the model starts drifting off spec.
Here I am sharing my .NET project configuration I use in my projects:
- The rules set in
Directory.Build.propsapplies to all projects under that directory tree (and can be overridden lower down).
<!-- MSBuild settings that will auto import to each project. -->
<!-- Strict Mode -->
<Project>
<PropertyGroup>
<!-- Enable nullable reference type analysis and annotations. -->
<Nullable>enable</Nullable>
<!-- Auto-import common framework namespaces so fewer explicit using directives are needed. -->
<ImplicitUsings>enable</ImplicitUsings>
<!-- Fail the build when any warning is produced. -->
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- Turn on built-in .NET analyzers. -->
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<!-- Use the latest analyzer rule set available in the current SDK. -->
<AnalysisLevel>latest</AnalysisLevel>
<!-- Run all analyzer categories. -->
<AnalysisMode>Recommended</AnalysisMode>
<!-- Enforce code-style rules during command-line build, not only in the IDE. -->
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<!-- Produce deterministic binaries for reproducible builds. -->
<Deterministic>true</Deterministic>
<!-- Apply CI-focused build behaviors (for example, reproducibility/source-link conventions). -->
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<!-- Compile integer arithmetic in checked mode to detect overflow/underflow at runtime. -->
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<!-- Disallow unsafe code blocks unless explicitly overridden in a project. -->
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<!-- Continue restore if some configured NuGet sources are temporarily unreachable. -->
<RestoreIgnoreFailedSources>true</RestoreIgnoreFailedSources>
<!-- Emit XML documentation output during build. -->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
- We can then fine-tune the rules for specific file/project when needed in
.editorconfig
root = true
# Base whitespace/newline defaults for all text files.
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
# C# style and analyzer policy.
[*.cs]
indent_style = space
indent_size = 4
csharp_new_line_before_open_brace = all
# Prefer explicit type when it improves readability; allow var when obvious.
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = false:suggestion
# Fail for unnecessary usings and unused parameter.
# IDE0005: Using directive is unnecessary
dotnet_diagnostic.IDE0005.severity = warning
# IDE0060: Remove unused parameter
dotnet_diagnostic.IDE0060.severity = warning
# Keep XML docs generated, but do not fail builds for missing public comments.
# Team can raise/lower this severity later without touching MSBuild.
dotnet_diagnostic.CS1591.severity = suggestion
# Non-C# indentation defaults.
[*.{json,yml,yaml,md,astro,mjs}]
indent_style = space
indent_size = 2
- Make use of
pre-commitandpre-pushgit hook:.git/hooks/pre-commit:
#!/usr/bin/env bash
# exit on error
set -e
dotnet restore
dotnet format --verify-no-changes
dotnet build
.git/hooks/pre-push:
#!/usr/bin/env bash
# exit on error
set -e
dotnet test --no-build
By doing this, you force a coding agent to iterate until the output satisfies the compiler and analyzers β catching drift early.
You might be surprised: this doesnβt only guard AI-generated code, it also maintains good human development hygiene for engineering excellence.
Guardrails are only a precautionary measure, though. Security-minded prompting, solid test coverage, and good engineering practices are equally important.
And you still need human review to ensure no secrets (API keys, tokens, connection strings) slip into the repository.
See you next time and happy coding! π β I still genuinely enjoy writing code by hand.
This blog post is also available on Medium.