migrate-nullable-references
Enable nullable reference types in a C# project and systematically resolve all warnings. USE FOR: adopting NRTs in existing codebases, file-by-file or project-wide migration, fixing CS8602/CS8618/CS86xx warnings, annotating APIs for nullability, cleaning up null-forgiving operators, upgrading depend…
Install / Use
npx skills add dotnet/skills --skill migrate-nullable-referencesInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
Development & EngineeringSupported Platforms
Our assessment of migrate-nullable-references
migrate-nullable-references scores 87/100 on our quality scale, 897th of 2,398 Development & Engineering skills we index (top 38%).
Its SKILL.md is 35 KB long, well organised into 22 sections and no code examples: a thorough specification that gives an agent plenty to work with.
With 5,471 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated 2 days ago, so migrate-nullable-references is actively maintained.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
migrate-nullable-references compared with similar skills
All 4 of these similar skills score higher than migrate-nullable-references; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| migrate-nullable-references (this skill)by dotnet | 87 | 5.5k | 2d ago | SKILL.md |
| Agent-Reachby Panniantong | 100 | 85.6k | 11d ago | CLAUDE.md |
| headroomby headroomlabs-ai | 100 | 73.9k | today | CLAUDE.md |
| ai-job-searchby MadsLorentzen | 100 | 44.0k | 5d ago | CLAUDE.md |
| claude-howtoby luongnv89 | 100 | 41.7k | today | CLAUDE.md |
Frequently asked questions
- How do I install migrate-nullable-references?
- Run
npx skills add dotnet/skills --skill migrate-nullable-references. The install tabs above show the steps for each supported agent. - Which AI agents does migrate-nullable-references work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is migrate-nullable-references safe to use?
- It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is migrate-nullable-references still maintained?
- The repository was last updated 2 days ago, so migrate-nullable-references is actively maintained.
Skill content
View source on GitHubname: migrate-nullable-references description: > Enable nullable reference types in a C# project and systematically resolve all warnings. USE FOR: adopting NRTs in existing codebases, file-by-file or project-wide migration, fixing CS8602/CS8618/CS86xx warnings, annotating APIs for nullability, cleaning up null-forgiving operators, upgrading dependencies with new nullable annotations. DO NOT USE FOR: projects already fully migrated with zero warnings (unless auditing suppressions), fixing a handful of nullable warnings in code that already has NRTs enabled, suppressing warnings without fixing them, C# 7.3 or earlier projects. INVOKES: Get-NullableReadiness.ps1 scanner script. license: MIT
Nullable Reference Migration
Enable C# nullable reference types (NRTs) in an existing codebase and systematically resolve all warnings. The outcome is a project (or solution) with <Nullable>enable</Nullable>, zero nullable warnings, and accurately annotated public API surfaces — giving both the compiler and consumers reliable nullability information.
When to Use
- Enabling nullable reference types in an existing C# project or solution
- Systematically resolving CS86xx nullable warnings after enabling the feature
- Annotating a library's public API surface so consumers get accurate nullability information
- Upgrading a dependency that has added nullable annotations and new warnings appear
- Analyzing suppressions in a code base that has already enabled NRTs to determine whether they can be removed
When Not to Use
- The project already has
<Nullable>enable</Nullable>and zero warnings — the migration is done unless the user wants to re-examine suppressions with a view to removing unnecessary ones (see Step 6) - The user only wants to suppress warnings without fixing them (recommend against this)
- The code targets C# 7.3 or earlier, which does not support nullable reference types
Inputs
| Input | Required | Description |
|-------|----------|-------------|
| Project or solution path | Yes | The .csproj, .sln, or build entry point to migrate |
| Migration scope | No | project-wide (default) or file-by-file — controls the rollout strategy |
| Build command | No | How to build the project (e.g., dotnet build, msbuild, or a repo-specific build script). Detect from the repo if not provided |
| Test command | No | How to run tests (e.g., dotnet test, or a repo-specific test script). Detect from the repo if not provided |
Workflow
🛑 Zero runtime behavior changes. NRT migration is strictly a metadata and annotation exercise. The generated IL must not change — no new branches, no new null checks, no changed control flow, no added or removed method calls. The only acceptable changes are nullable annotations (
?), nullable attributes ([NotNullWhen], etc.),!operators (metadata-only), and#nullabledirectives. If you discover a missing runtime null guard or a latent bug during migration, do not fix it inline. Instead, offer to insert a// TODO: Consider adding ArgumentNullException.ThrowIfNull(param)comment at the site so the user can address it as a separate change. Never mix behavioral fixes into an annotation commit.
Commit strategy: Commit at each logical boundary — after enabling
<Nullable>(Step 2), after fixing dereference warnings (Step 3), after annotating declarations (Step 4), after applying nullable attributes (Step 5), and after cleaning up suppressions (Step 6). This keeps each commit focused and reviewable, and prevents losing work if a later step reveals a design issue that requires rethinking. For file-by-file migrations, commit each file or batch of related files individually.
Step 1: Evaluate readiness
Optional: Run
scripts/Get-NullableReadiness.ps1 -Path <project-or-solution>to automate the checks below. The script reports<Nullable>,<LangVersion>,<TargetFramework>,<WarningsAsErrors>settings and counts#nullable disabledirectives,!operators, and#pragma warning disable CS86xxsuppressions. Use-Jsonfor machine-readable output.
- Identify how the project is built and tested. Look for build scripts (e.g.,
build.cmd,build.sh,Makefile), a.slnfile, or individual.csprojfiles. If the repo uses a custom build script, use it instead ofdotnet buildthroughout this workflow. - Run
dotnet --versionto confirm the SDK is installed. Nullable reference types (NRTs) require C# 8.0+ (.NET Core 3.0/.NET Standard 2.1or later). - Open the
.csproj(orDirectory.Build.propsif properties are set at the repo level) and check the<LangVersion>and<TargetFramework>. If the project multi-targets, note all TFMs.
Stop if the language version or target framework is insufficient. If
<LangVersion>is below 8.0, or the project targets a framework that defaults to C# 7.x (e.g.,.NET Framework 4.xwithout an explicit<LangVersion>), NRTs cannot be enabled as-is. Inform the user explicitly: explain what needs to change (set<LangVersion>8.0</LangVersion>or higher, or retarget to.NET Core 3.0+/.NET 5+), and ask whether they want to make that update and continue, or abort the migration. Do not silently proceed or assume the update is acceptable.
- Check whether
<Nullable>is already set. If it is set toenable, skip to Step 5 to audit remaining warnings. - Determine the project type — this shapes annotation priorities throughout the migration:
- Library: Focus on public API contracts first. Every
?on a public parameter or return type is a contract change that consumers depend on. Be precise and conservative. - Application (web, console, desktop): Focus on null safety at boundaries — deserialization, database queries, user input, external API responses. Internal plumbing can be annotated more liberally.
- Test project: Lower priority for annotation precision. Use
!more freely on test setup and assertions where null is never expected. Focus on ensuring test code compiles cleanly.
- Library: Focus on public API contracts first. Every
Step 2: Choose a rollout strategy
Pick one of the following strategies based on codebase size and activity level. Recommend the strategy to the user and confirm before proceeding.
Multi-project solutions: Migrate in dependency order — shared libraries and core projects first, then projects that consume them. Annotating a dependency first eliminates cascading warnings in its consumers and prevents doing work twice.
Regardless of strategy, start at the center and work outward:begin with core domain models, DTOs, and shared utility types that have few dependencies but are used widely. Annotating these first eliminates cascading warnings across the codebase and gives the biggest return on effort. Then move on to higher-level services, controllers, and UI code that depend on the core types. This approach minimizes the number of warnings at each step and prevents getting overwhelmed by a flood of warnings from a large project-wide enable. Prefer to create at least one PR per project, or per layer, to keep changesets reviewable and focused. If there are relatively few annotations needed, a single project-wide enable and single PR may be appropriate.
Strategy A — Project-wide enable (small to medium projects)
Best when the project has fewer than roughly 50 source files or the team wants to finish in one pass.
- Add
<Nullable>enable</Nullable>to the<PropertyGroup>in the.csproj. - Build and address all warnings at once.
Strategy B — Warnings-first, then annotations (large or active projects)
Best when the codebase is large or under active development by multiple contributors.
- Add
<Nullable>warnings</Nullable>to the.csproj. This enables warnings without changing type semantics. - Build, fix all warnings from Step 3 onward.
- Change to
<Nullable>enable</Nullable>to activate annotations — this triggers a second wave of warnings. - Resolve the annotation-phase warnings from Step 4 onward.
Strategy C — File-by-file (very large projects)
Best for large legacy codebases where enabling project-wide would produce an unmanageable number of warnings.
- Set
<Nullable>disable</Nullable>(or omit it) at the project level. - Add
#nullable enableat the top of each file as it is migrated. - Prioritize files in dependency order: shared utilities and models first, then higher-level consumers.
Build checkpoint: After enabling
<Nullable>(or adding#nullable enableto the first batch of files), do a clean build (e.g.,dotnet build --no-incremental, or deletebin/objfirst). Incremental builds only recompile changed files and will hide warnings in untouched files. Record the initial warning count — this is the baseline to work down from. Do not proceed to fixing warnings without first confirming the project still compiles. Use clean builds for all subsequent build checkpoints in this workflow.
Step 3: Fix dereference warnings
Prioritization: Work through files in dependency order — start with core models and shared utilities that other code depends on, then move to higher-level consumers. Within each file, fix public and protected members first (these define the contract), then internal and private members. This order minimizes cascading warnings: fixing a core type's annotations often resolves warnings in its consumers automatically.
Build the project and work through dereference warnings. These are the most common:
| Warning | Meaning | Typical fix |
|---------|---------|-------------|
| CS8602 | Dereference of a possibly null reference | Prefer annotation-only fixes: make the upstream type nullable (T?) if null is valid, or use ! if you can verify the value is never null at this point. Adding a null check or ?. changes runtime behavior — reserve those for a separate commit (see zero-behavior-change rule above) |
| CS8600 | Converting possible null to non-nullable type | Add ? to the target type if null is valid, or use ! if you can verify the value is never null. Adding a null guard changes runtime behavior |
| CS8603 | Possible null reference return | Change the return type to nullable (T?) if the method can genuinely return null. Do not suppress with ! if the method can genuinely return null — fix the return type instead. This is the single most important rule in NRT migration: a non-nullable return type is a promise to every caller that null will never be returned |
| CS8604 | Possible null reference argument | Mark the parameter as nullable if null is valid, or use ! if the argument is verifiably non-null. Adding a null check before passing changes runtime behavior |
❌ Do not use
?.as a quick fix for dereference warnings. Replacingobj.Method()withobj?.Method()silently changes runtime behavior — the call is skipped instead of throwing. Only use?.when you intentionally want to tolerate null.
❌ Do not sprinkle
!to silence warnings. Each!is a claim that the value is never null. If that claim is wrong, you have hidden aNullReferenceException. Add a null check or make the type nullable instead.
❌ Never use
return null!to keep a return type non-nullable. If a method returnsnull, the return type must beT?. Writingreturn null!hides a null behind a non-nullable signature — callers trust the signature, skip null checks, and getNullReferenceExceptionat runtime. This applies tonull!,default!, and any cast that makes the compiler accept null in a non-nullable position. The only acceptable use of!on a return value is when the value is provably never null but the compiler cannot see why.
⚠️ Do not add
?to value types unless you intend to change the runtime type. For reference types,?is metadata-only. For value types (int, enums, structs),?changes the type toNullable<T>, altering the method signature, binary layout, and boxing be
Truncated for display — read the full file on GitHub.
Related Skills
Agent-Reach
85.6kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
headroom
73.9kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
ai-job-search
44.0kThe job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.
claude-howto
41.7kA visual, example-driven guide to Claude Code — from basic concepts to advanced agents, with copy-paste templates that bring immediate value.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
