Turning on nullable reference types in an existing codebase
Nullable reference types catch null reference exceptions at compile time. Switching them on in a large, older project produces thousands of warnings. Here's how to adopt them one step at a time.
NullReferenceException is still the most common runtime error in .NET code. Nullable reference types (NRTs) move that check to compile time: the compiler tracks which references might be null and warns when you use one without checking.
New projects have them on by default. Older projects often don't, and turning them on all at once in a project with years of history buries the team in warnings.
What changes when they're on
#nullable enable
string name = null; // warning: assigning null to a non-nullable type
string? nickname = null; // fine: the ? says null is allowed
Console.WriteLine(nickname.Length); // warning: possible null dereference
if (nickname is not null)
Console.WriteLine(nickname.Length); // fine: the compiler knows it's not null here
string now means "never null" and string? means "might be null". Nothing changes at runtime; it's all compile-time analysis.
Adopt it gradually
Option 1: file by file. Leave the project setting off and add #nullable enable to the top of files as you work on them. New files get it from day one, and the covered area grows naturally.
Option 2: warnings only. Set the project to annotate nothing but still warn:
<Nullable>warnings</Nullable>
Option 3: project by project. In a multi-project solution, turn it on fully in one project at a time, starting with the ones with the fewest dependencies, such as domain or contracts projects.
Whichever you choose, fix warnings in the code you're already changing rather than in one giant pull request.
Make the intent clear
A few tools help the compiler understand your code:
requiredproperties for values that must be set when an object is created:
public class Customer
{
public required string Id { get; init; }
public required string Name { get; init; }
public string? Email { get; init; }
}
- Attributes such as
[NotNullWhen(true)]onTryGet-style methods, so the compiler knows the out value is safe after atrueresult. - The
!operator tells the compiler "trust me, this isn't null". Use it sparingly. Every!is a place a null can still sneak through.
Lock it in
Once a project is clean, make new nullable warnings fail the build so it stays that way:
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable</WarningsAsErrors>
Takeaway
Nullable reference types turn null bugs into compiler warnings. In an existing codebase, enable them gradually by file or project, fix warnings in code you're already touching, and once a project is clean, make nullable warnings errors so it stays clean.