SkillAgentSearch skills...

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-references

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

87/100

Supported Platforms

Universal

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.

Substance
30/30
Structure
13/20
Description
15/15
Adoption
16/20
Freshness
15/15

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.

SkillScoreStarsUpdatedFormat
migrate-nullable-references (this skill)by dotnet875.5k2d agoSKILL.md
Agent-Reachby Panniantong10085.6k11d agoCLAUDE.md
headroomby headroomlabs-ai10073.9ktodayCLAUDE.md
ai-job-searchby MadsLorentzen10044.0k5d agoCLAUDE.md
claude-howtoby luongnv8910041.7ktodayCLAUDE.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.

name: 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 #nullable directives. 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 disable directives, ! operators, and #pragma warning disable CS86xx suppressions. Use -Json for machine-readable output.

  1. Identify how the project is built and tested. Look for build scripts (e.g., build.cmd, build.sh, Makefile), a .sln file, or individual .csproj files. If the repo uses a custom build script, use it instead of dotnet build throughout this workflow.
  2. Run dotnet --version to confirm the SDK is installed. Nullable reference types (NRTs) require C# 8.0+ (.NET Core 3.0 / .NET Standard 2.1 or later).
  3. Open the .csproj (or Directory.Build.props if 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.x without 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.

  1. Check whether <Nullable> is already set. If it is set to enable, skip to Step 5 to audit remaining warnings.
  2. 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.

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.

  1. Add <Nullable>enable</Nullable> to the <PropertyGroup> in the .csproj.
  2. 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.

  1. Add <Nullable>warnings</Nullable> to the .csproj. This enables warnings without changing type semantics.
  2. Build, fix all warnings from Step 3 onward.
  3. Change to <Nullable>enable</Nullable> to activate annotations — this triggers a second wave of warnings.
  4. 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.

  1. Set <Nullable>disable</Nullable> (or omit it) at the project level.
  2. Add #nullable enable at the top of each file as it is migrated.
  3. Prioritize files in dependency order: shared utilities and models first, then higher-level consumers.

Build checkpoint: After enabling <Nullable> (or adding #nullable enable to the first batch of files), do a clean build (e.g., dotnet build --no-incremental, or delete bin/obj first). 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. Replacing obj.Method() with obj?.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 a NullReferenceException. Add a null check or make the type nullable instead.

❌ Never use return null! to keep a return type non-nullable. If a method returns null, the return type must be T?. Writing return null! hides a null behind a non-nullable signature — callers trust the signature, skip null checks, and get NullReferenceException at runtime. This applies to null!, 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 to Nullable<T>, altering the method signature, binary layout, and boxing be

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars5.5k
CategoryDevelopment
Updated2d ago
Forks418

Languages

C#

Trust signals

100/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

No cautions