Software warnings кажутся безобидными до того дня, когда один из них превращается в late-night outage.
В этой статье разберем, как превратить желтые squiggles в early-failing, production-saving errors с помощью family settings WarningsAsErrors.
Вы увидите, как включить switch, когда его fine-tune и что делать, если project уже drowning in warnings.
Почему один warning может утопить release
Обычная C# solution компилируется даже когда пролетают десятки diagnostics, потому что default severity для большинства compiler diagnostics и analyzer rules - warning. Teams привыкают к noise, и реальные defects прячутся на виду:
-
Nullability warnings вроде
CS8602, которые позже crash application. -
Unobserved task exceptions, masked by ignored async warnings.
-
API misuse, пойманный analyzers, но так и не исправленный.
Прямо говоря, warning - это bug, который вы запланировали на production.
Code Example
public static class Program
{
static void Main(string[] args)
{
// CS0168: Variable declared but never used
int unusedVariable;
// CS8600: Converting null literal or possible
// null value to non-nullable type
string nullableStr = GetNullableString();
// CS8602: Dereference of a possibly null reference
Console.WriteLine($"Length: {nullableStr.Length}");
}
// Method that can return null
private static string? GetNullableString()
{
return DateTime.Now.Second % 2 == 0 ? "Test string" : null;
}
}
Error List показывает CS0168, CS8600 и CS8602, но build все равно успешно завершается с zero errors и three warnings.

TreatWarningsAsErrors
Одна строка в .csproj превращает every compiler warning в error:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
С этого момента build fails fast.

Pros
-
Нулевой шанс отправить fresh compiler warnings в production.
-
Clear signal для каждого developer.
Cons
- Это может быть brutal, если в legacy codebase уже сотни diagnostics.
WarningsAsErrors
Если нужен больший control, перечислите только warning codes, которые должны treated as errors:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<WarningsAsErrors>CS8600</WarningsAsErrors>
</PropertyGroup>
</Project>
Все остальные warnings остаются warnings и не ломают build. Чтобы включить несколько codes, разделяйте их semicolons:
<WarningsAsErrors>CS8600;CS8602</WarningsAsErrors>

WarningsNotAsErrors
Иногда нужен обратный подход: держать most warnings as errors, кроме нескольких noisy ones.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsNotAsErrors>CS0168</WarningsNotAsErrors>
</PropertyGroup>
</Project>

Если у вас уже сотни warnings, этого approach может быть недостаточно. В одном из следующих постов я поделюсь tactics, как быстро их clean up.
Key Takeaways
-
Warning, проигнорированный сегодня, может стать завтрашним post-mortem.
-
TreatWarningsAsErrors- самый простой и надежный safety net. -
Selective lists с
WarningsAsErrorsиWarningsNotAsErrorsпозволяют внедрять rule постепенно. -
Автоматизируйте compiler switches, analyzers и formatting, чтобы signal оставался clean.
Закройте warnings сейчас, и production environment позже будет спать спокойнее.