Compiler warnings are easy to ignore. They don't break the build. They don't cause immediate errors. They just sit there, accumulating quietly, until your codebase is full of them and nobody feels responsible for any individual one.

When I took a hard look at our codebase on the SKG team, we had 1,152 of them.

Why Warnings Are Actually a Problem

The majority of our warnings were one of two types:

The null reference ones were the dangerous category. Each was a latent production failure waiting for the right runtime conditions to surface. And because there were 1,152 of them mixed in with benign ones, the signal was completely lost in noise — a new critical warning would just disappear into the list.

The real cost A warning-heavy codebase doesn't just carry technical debt — it trains engineers to ignore warnings entirely. When a genuinely dangerous warning appears, nobody notices.

The Fix: Three Parts

Part 1 — Eliminate All 1,152 Warnings

I went through the codebase methodically, project by project. The null-reference warnings required proper null-handling — null checks, null-coalescing operators, or restructuring the logic to avoid nullable paths. The unnecessary async keywords were straightforward removals.

Not glamorous work. But methodical, and with a clear finish line.

Part 2 — Make New Warnings Impossible to Merge

Cleaning up 1,152 warnings means nothing if the next PR adds 10 more. I configured all projects in the solution to treat warnings as errors. From that point forward, any PR that introduced a new warning would fail to compile and fail the build — it simply couldn't be merged.

This was the important part. Fixing warnings once is a one-time activity. Making it structurally impossible to introduce them is a permanent property of the codebase.

Part 3 — Enforce Test Coverage in CI

While I was improving code quality, I also added a code coverage gate to the Azure DevOps pipeline: any PR that drops L0 (unit test) coverage below 70% automatically fails.

This meant every line of code merged after the gate was introduced had meaningful test coverage. It changed the culture around testing — not through persuasion, but through the pipeline. If your code doesn't have tests, it doesn't merge. Simple.

Part 4 — Standardise Exception Handling

I also standardised HTTP exception handling across the entire codebase — all exceptions now resolve to a common format with consistent fields. This sounds minor but had real impact: debugging production issues became faster because every error response looked the same, and client teams could rely on a consistent error schema.

Results

1,152
Warnings eliminated
70%
L0 test coverage gate enforced in CI
0
Net-new warnings since the change
"Your contributions are commendable, and your work has significantly improved the product quality and efficiency." — Manager feedback

What I Learned

C#.NETCI/CDAzure DevOpsCode QualityTesting