Agent skill

.NET Trimming and Native AOT

by Aaronontheweb in Aaronontheweb/dotnet-skills

Guides making .NET libraries trimming-safe and Native-AOT compatible: the MSBuild properties, trimming attributes, warning codes and a playbook of safe patterns.

MITAuto-check passedDevelopment

Install .NET Trimming and Native AOT

skills CLI
$ npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install Aaronontheweb/dotnet-skills aot-trimming --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/aot-trimming .claude/skills/aot-trimming && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
aot-trimming
GitHub stars
1.2k
Token cost
~2.9k tokens
SKILL.md length
1,015 words
Files
3
Skills in repo
35
Repo updated
First seen
Licence
MIT

At a glance

Guides making .NET libraries trimming-safe and Native-AOT compatible: the MSBuild properties, trimming attributes, warning codes and a playbook of safe patterns.

  • Works in 2 steps: Unseen reflection (trimming): the linker… → Dynamic code generation (AOT): the JIT…
  • Adding trimming or Native AOT support to a .NET library or application
  • SKILL.md covers When to Use, Core Goals, Core Model and Project Configuration, plus 5 more sections
  • Calls dotnet

What it does

Trimming runs the linker's reachability analysis and keeps only statically reachable code, warning when it cannot see reflection it needs to trim away; Native AOT makes trimming mandatory and removes the JIT entirely, so Reflection.Emit stops working and Expression.Compile may fall back to interpretation. Both run the same two families of static analysis, trimming warnings from RequiresUnreferencedCode propagation and DynamicallyAccessedMembers dataflow, and AOT and single-file warnings from RequiresDynamicCode and RequiresAssemblyFiles; a library can be trimmable without being AOT-compatible if it relies on Reflection.Emit.

The two problems to solve are unseen reflection, where the linker removes a member loaded by name or reached through a generic type parameter, and dynamic code generation, where the missing JIT breaks runtime code emission; both are solved by making the needed members statically visible to the analyzer or removing the need for reflection. The skill covers the MSBuild properties that enable this analysis, the attributes that express what the compiler cannot see, and a playbook of patterns including source generators, diagnostic suppressors, trimming-safe islands behind feature switches, and temporary warning-approval baselines, with a separate skill for JSON serialization's own AOT scenarios.

When your agent uses it

  • Adding trimming or Native AOT support to a .NET library or application
  • Resolving trimming or AOT warning codes
  • Making reflection-heavy code trimming-safe
  • Reviewing a PR for trimming and AOT compatibility

Example prompts

  • “This library throws trimming warnings when I enable PublishTrimmed. Help me fix them.”
  • “Make this reflection-heavy serializer code trimming-safe and AOT-compatible.”
  • “Review this PR for trimming and AOT compatibility before we merge.”

Requirements

  • A .NET project targeting PublishTrimmed or PublishAot

Workflow steps

2 steps, taken from the first numbered list in SKILL.md.

  1. Unseen reflection (trimming): the linker removes a member or type you load by name, or reach through typeof(T) in a generic method.
  2. Dynamic code generation (AOT): the JIT is gone, so Reflection.Emit is unsupported and constructing unknown generic instantiations is not…

What it can do on your machine

Read from SKILL.md and the folder at commit 46003af. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • dotnet

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • learn.microsoft.com
    • github.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

.NET Trimming and Native AOT loads about 2.9k tokens when it runs. Until then it costs about 219 tokens; SKILL.md has 1,015 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~219
When it runs · the whole SKILL.md, loaded when a task matches
~2.9k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from Aaronontheweb/dotnet-skills at commit 46003af, republished under its MIT licence (© Aaronontheweb). 1,015 words, ~2,915 tokens.

Download SKILL.mdSave it as .claude/skills/aot-trimming/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.
name
aot-trimming
description
Guidelines for making .NET libraries and applications trimming-safe and Native AOT compatible. Covers the trimming/AOT model, the MSBuild properties that enable analysis (IsTrimmable, IsAotCompatible, PublishTrimmed, PublishAot), the trimming attributes (RequiresUnreferencedCode, RequiresDynamicCode, DynamicallyAccessedMembers, UnconditionalSuppressMessage), IL2xxx/IL3xxx warning codes, and a pattern playbook: source generators and UnsafeAccessor, generated-interception diagnostic suppressors, intentional runtime scanning boundaries, trimming-safe islands and feature switches, migration analyzers, and temporary warning-approval baselines. Use when introducing trimming/AOT support, resolving IL2xxx/IL3xxx warnings, or making reflection-heavy code trimming-safe in C# / .NET codebases. For System.Text.Json AOT scenarios, see also the serialization skill.
version
1.0.0
tags
csharp, dotnet, aot, trimming, nativeaot, code-quality

.NET Trimming and Native AOT

When to Use

  • Adding trimming (PublishTrimmed) or Native AOT (PublishAot) support to a library or application
  • Resolving IL2xxx (trimming) and IL3xxx (AOT/single-file) warnings
  • Making reflection-heavy code trimming-safe
  • Replacing runtime reflection, assembly scanning, or Reflection.Emit with compile-time alternatives
  • Reviewing a codebase or PR for trimming/AOT compatibility, including generated interceptors and analyzer suppressors
  • Designing public APIs that must carry trimming annotations

Core Goals

  • Produce code that survives the linker's reachability analysis and runs under Native AOT (no JIT).
  • Express what the compiler cannot see — which members reflection needs — using the trimming attributes.
  • Keep trimming-safe and trimming-unsafe code separated so unsafe paths are explicit and contained.

Core Model

Trimming vs Native AOT
  • Trimming (PublishTrimmed) runs ILLink reachability analysis: only statically reachable code is kept. Reflection the analyzer cannot see is trimmed away and produces warnings.
  • Native AOT (PublishAot) makes trimming mandatory and removes the JIT. Reflection.Emit is unsupported, constructing unknown generic instantiations at runtime is not guaranteed, Expression.Compile() may fall back to interpretation, and reflection works only over members the linker preserved.
  • Both run the same static analysis:
    • IL2xxx — trimming warnings: RequiresUnreferencedCode propagation + DynamicallyAccessedMembers dataflow.
    • IL3xxx — AOT and single-file warnings: RequiresDynamicCode / RequiresAssemblyFiles.

A library can be trimmable but not AOT-compatible (it uses Reflection.Emit, which trimming tolerates but AOT does not).

The two problems to solve
  1. Unseen reflection (trimming): the linker removes a member or type you load by name, or reach through typeof(T) in a generic method.
  2. Dynamic code generation (AOT): the JIT is gone, so Reflection.Emit is unsupported and constructing unknown generic instantiations is not guaranteed.

Both are solved the same way: make the required members statically visible to the analyzer, or remove the need for reflection entirely.

Reflection is not the enemy

Reflection is a legitimate technique and is completely fine in ordinary JIT applications — you need none of this there. The trimming attributes exist to allow reflection under trimming/AOT, not to forbid it: annotate what reflection needs, and the linker keeps it. Reach for source generators, [UnsafeAccessor], or capability gating only when you want a specific reflective surface removed or contained (for example, a public registration API you do not want to ship with [RequiresUnreferencedCode]).

Project Configuration

For a library, declare compatibility so the analyzers surface warnings and consumers know:

xml
<PropertyGroup>
  <!-- Trim-compatible: enables trim warnings. -->
  <IsTrimmable>true</IsTrimmable>

  <!-- AOT-compatible: implies IsTrimmable + EnableTrimAnalyzer + EnableSingleFileAnalyzer + EnableAotAnalyzer. -->
  <IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</IsAotCompatible>
</PropertyGroup>

For an application, publish trimmed or AOT:

xml
<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
  <!-- or -->
  <PublishAot>true</PublishAot>
</PropertyGroup>

Verify by actually publishing — a trimmed/AOT app must produce zero warnings:

bash
dotnet publish -c Release -r <rid> -p:PublishTrimmed=true
dotnet publish -c Release -r <rid> -p:PublishAot=true

A test app that roots the library is the standard way to validate a library:

xml
<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
<ItemGroup>
  <ProjectReference Include="..\MyLibrary\MyLibrary.csproj" />
  <TrimmerRootAssembly Include="MyLibrary" />
</ItemGroup>

See aot-trimming-playbook-reference.md for the full property table, feature switches, and the warning-approval baseline pattern.

The Attribute Model

These live in System.Diagnostics.CodeAnalysis. See trimming-attributes-reference.md for the full catalog with exact signatures, warning codes, and rules.

  • [RequiresUnreferencedCode(message)] — marks code that needs members the linker cannot see. Suppresses warnings inside; emits IL2026 at every call site.
  • [RequiresDynamicCode(message)] — marks code that needs a JIT (emitting, dynamic). Emits IL3050 at call sites.
  • [DynamicallyAccessedMembers(memberTypes)] — declares which members of a Type/string must be preserved. Flows backward from the reflection site to the Type source. Scope it (and the other attributes) to a single accessor by placing the attribute directly on the accessor, or with the [method:], [field:], or [return:] targets.
  • [UnconditionalSuppressMessage(category, checkId, Justification = "...")] — last-resort, IL-persisted suppression; must carry a justification and a real invariant.
  • [DynamicDependency("Member", typeof(T))] — keeps named members but does not silence warnings.

Choose the narrowest DynamicallyAccessedMemberTypes for the reflection you actually perform:

ReflectionMember type
Activator.CreateInstance<T>(), new T(), Activator.CreateInstance(type)PublicParameterlessConstructor
DI activation (ActivatorUtilities.CreateFactory<T>)PublicConstructors
Explicit non-public activationNonPublicConstructors
type.GetInterfaces()Interfaces
type.GetMethods()PublicMethods (add NonPublicMethods when needed)
type.GetProperties()PublicProperties
type.GetFields()PublicFields
type.GetNestedTypes()PublicNestedTypes
Show full SKILL.md (428 more words)Show less

The Pattern Playbook (selection)

First decide whether the code must be trimming/AOT safe at all. A plain JIT app or library needs none of this — reflection is fine there. Only when you or a consumer publish trimmed/AOT does reflection need to become visible to the linker.

The goal is never "remove all reflection"; it is make the reflection statically visible so the linker keeps what it needs. Choose per call site:

  • Keep the reflection and annotate it — the common case. Put [DynamicallyAccessedMembers(...)] on the Type/string source and propagate it through the chain; mark entry points [RequiresUnreferencedCode]/[RequiresDynamicCode]. The attributes exist precisely to let reflection work under trimming. See trimming-attributes-reference.md.

  • Replace the reflection with a compile-time alternative — when you want a reflective surface gone (for example, a public registration API you do not want to ship with [RequiresUnreferencedCode]). A source generator emits direct type references (marker attribute → generated registration); [UnsafeAccessor] replaces FieldInfo/MethodInfo/ConstructorInfo. See aot-trimming-playbook-reference.md and aot-trimming-playbook-reference.md.

  • Isolate a whole dynamic path — when a path is inherently dynamic (emitting proxies, loading plugins by name), move it behind a boundary and provide a trimming-safe alternative selected by RuntimeFeature.IsDynamicCodeSupported. See aot-trimming-playbook-reference.md.

  • Suppress only as a last resort — a single [UnconditionalSuppressMessage(...)] at an invariant-proven leaf, with a Justification. Never #pragma (not persisted in IL).

See aot-trimming-playbook-reference.md for the full playbook, including object-overload → generic-overload redirects, generated-interception suppressors, intentional scanning boundaries, migration analyzers, strict-mode precedence, and temporary warning-approval baselines.

Reference Files

  • trimming-attributes-reference.md: The complete attribute catalog — RequiresUnreferencedCode, RequiresDynamicCode, DynamicallyAccessedMembers (full DynamicallyAccessedMemberTypes list), UnconditionalSuppressMessage, DynamicDependency — each with intent, exact signature, and rules, plus the IL2xxx/IL3xxx warning-code table.
  • aot-trimming-playbook-reference.md: The full pattern playbook — source generators vs reflection, trimming-safe islands and capability guards, separating safe/unsafe code, System.Text.Json source generation, object-overload → generic-overload redirects with [OverloadResolutionPriority] and opt-in analyzers, migration analyzers, warning-approval baselines, known gotchas, and the full generation checklist.

Generation Checklist (Summary)

  1. Project — IsTrimmable/IsAotCompatible on libraries; PublishTrimmed/PublishAot on apps; zero warnings on publish.
  2. Make reflection visible (or replace it) — annotate the reflection you keep (DynamicallyAccessedMembers/RequiresUnreferencedCode/RequiresDynamicCode); replace it with source generators, generic (typeof(T)) APIs, or [UnsafeAccessor] only where you want the reflective surface gone.
  3. Annotate — narrowest DynamicallyAccessedMembers on every Type/string source; propagate through the chain; [RequiresUnreferencedCode]/[RequiresDynamicCode] on unsafe entry points.
  4. Isolate — keep unsafe reflective/emit code behind a boundary; gate dynamic-code paths with RuntimeFeature.IsDynamicCodeSupported, and separate trimming paths with #if or annotations.
  5. Suppress — only with [UnconditionalSuppressMessage] + justification at invariant-proven leaves; never #pragma/SuppressMessage.
  6. Verify — publish trimmed and AOT, run analyzers, and lock the warning surface with an approval baseline.

References

© Aaronontheweb, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 2 other files in skills/aot-trimming of Aaronontheweb/dotnet-skills.

  • SKILL.md
  • aot-trimming-playbook-reference.md
  • trimming-attributes-reference.md

Open the folder on GitHubat commit 46003af

Compare with similar skills

.NET Trimming and Native AOT next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

.NET Trimming and Native AOT compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
.NET Trimming and Native AOT this skillAaronontheweb/dotnet-skills1.2k—~2.9kAutomated safety check: PassMIT
Analyzing .NET Performancedotnet/skills5.6k3 repos~3.1kAutomated safety check: PassMIT
MAUI PR Performance Analysisdotnet/maui23k—~2.4kAutomated safety check: PassMIT
Dotnet Debuggingnovotnyllc/dotnet-artisan232—~2.1kAutomated safety check: PassMIT
Build Dotnet Agentnewrelic/newrelic-dotnet-agent117—~871Automated safety check: PassApache-2.0
Asynkron Profilermanagedcode/dotnet-skills486—~1.7kAutomated safety check: PassMIT

Similar skills

  • Official

    Scans C# and .NET code for about 50 performance anti-patterns and reports prioritized findings with concrete fixes, at a scan depth you choose.

    5.6k GitHub starsUsed in 3 repos~3.1k tokens
    DevelopmentAuto-check passed
  • Official

    Interprets pinned managed benchmark evidence for a dotnet/maui pull request and writes a narrative for the performance review workflow, without running or publishing anything.

    23k GitHub stars~2.4k tokensUpdated today
    DevelopmentAuto-check passed
  • Dotnet Debugging

    novotnyllc/dotnet-artisan

    Debugs Windows and Linux/macOS applications (native, .NET/CLR, mixed-mode) with WinDbg MCP (crash dumps, !analyze, !syncblk, !dlk, !runaway, !dumpheap, !gcroot, BSOD), dotnet-dump, lldb with SOS…

    232 GitHub stars~2.1k tokensUpdated 2 days ago
    DevelopmentAuto-check passed
  • Build Dotnet Agent

    newrelic/newrelic-dotnet-agent

    Build the New Relic .NET agent locally and validate that code changes compile.

    117 GitHub stars~871 tokensUpdated today
    DevelopmentAuto-check passed
  • Asynkron Profiler

    managedcode/dotnet-skills

    Use the open-source free Asynkron.Profiler dotnet tool for CLI-first CPU, allocation, exception, contention, and heap profiling of .NET commands or existing trace artifacts.

    486 GitHub stars~1.7k tokensUpdated today
    DevelopmentAuto-check passed
  • Analyze Dotnet Agent Logs

    newrelic/newrelic-dotnet-agent

    Parse and diagnose New Relic .NET agent logs (newrelicagent.log) and profiler logs (NewRelic.Profiler.<pid.log) from a support ticket.

    117 GitHub stars~1.5k tokensUpdated today
    Sales & SupportAuto-check passed

More from Aaronontheweb/dotnet-skills

All 35 skills in this repo
  • Cscheck

    Aaronontheweb/dotnet-skills

    Write and simplify C property-based, model-based, and executable specification tests with CsCheck.

    1.2k GitHub stars~1.2k tokensUpdated today
    Auto-check passed
  • Akka.Hosting Actor Patterns

    Aaronontheweb/dotnet-skills

    Shows how to build entity actors with Akka.Hosting so the same code runs in local unit tests and in a sharded cluster in production.

    1.2k GitHub starsUsed in 1 repo~5k tokens
    Auto-check passed
  • SDK Container Publishing

    Aaronontheweb/dotnet-skills

    Publish .NET services as container images with the built-in SDK tooling (dotnet publish /t:PublishContainer, Microsoft.NET.Build.Containers) - no Dockerfile required.

    1.2k GitHub stars~1.9k tokensUpdated today
    Auto-check passed
  • Akka.NET Best Practices

    Aaronontheweb/dotnet-skills

    Guidance for Akka.NET actor systems covering EventStream versus DistributedPubSub, supervision, Props versus DependencyResolver, work distribution and testable cluster code.

    1.2k GitHub starsUsed in 1 repo~3.3k tokens
    Auto-check passed
  • Akka.NET Management and Discovery

    Aaronontheweb/dotnet-skills

    Sets up Akka.Management and Cluster.Bootstrap so Akka.NET clusters form through service discovery on Kubernetes, Azure or config instead of static seed nodes.

    1.2k GitHub starsUsed in 1 repo~2.5k tokens
    Auto-check passed
  • Akka.NET Testing Patterns

    Aaronontheweb/dotnet-skills

    Shows how to test Akka.NET actors with Akka.Hosting.TestKit: swapping services for fakes, using TestProbes, and checking persistence, plus when the older TestKit still fits.

    1.2k GitHub starsUsed in 1 repo~2.4k tokens
    Auto-check passed

Works with

Categories

Questions about .NET Trimming and Native AOT

What does .NET Trimming and Native AOT do?

Guides making .NET libraries trimming-safe and Native-AOT compatible: the MSBuild properties, trimming attributes, warning codes and a playbook of safe patterns. Compile may fall back to interpretation.Emit.

When should I use .NET Trimming and Native AOT?

.NET Trimming and Native AOT fits situations like: adding trimming or Native AOT support to a .NET library or application; resolving trimming or AOT warning codes; making reflection-heavy code trimming-safe; reviewing a PR for trimming and AOT compatibility.

How do I install .NET Trimming and Native AOT in Claude Code?

Run `npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a claude-code`. Or copy the skill folder (skills/aot-trimming in Aaronontheweb/dotnet-skills) into .claude/skills/aot-trimming in your project. Claude Code loads it when a task matches its description.

How do I install .NET Trimming and Native AOT in Codex?

Run `npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a codex`. Or copy the skill folder (skills/aot-trimming in Aaronontheweb/dotnet-skills) into .agents/skills/aot-trimming in your project. Codex loads it when a task matches its description.

Can I use .NET Trimming and Native AOT in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/aot-trimming, .gemini/skills/aot-trimming, .github/skills/aot-trimming and .opencode/skills/aot-trimming in your project.

What does .NET Trimming and Native AOT need to run?

Going by SKILL.md and its folder, .NET Trimming and Native AOT needs the command-line tools its instructions call (dotnet). Our summary lists: A .NET project targeting PublishTrimmed or PublishAot.

Does .NET Trimming and Native AOT access the network?

SKILL.md names 2 domains. As links in the text: learn.microsoft.com and github.com. This is read from the text; nothing was executed.

Is .NET Trimming and Native AOT safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does .NET Trimming and Native AOT use?

.NET Trimming and Native AOT is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does .NET Trimming and Native AOT use?

About 2.9k tokens (SKILL.md is roughly 12k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to .NET Trimming and Native AOT?

Skills that share tags, products or a category with .NET Trimming and Native AOT: Analyzing .NET Performance (dotnet/skills, 5.6k stars), MAUI PR Performance Analysis (dotnet/maui, 23k stars), Dotnet Debugging (novotnyllc/dotnet-artisan, 232 stars) and Build Dotnet Agent (newrelic/newrelic-dotnet-agent, 117 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains .NET Trimming and Native AOT?

Aaronontheweb (a GitHub user) maintains it in Aaronontheweb/dotnet-skills, which has 1,206 GitHub stars. The repository holds 35 skills in this directory. The repository was last updated on October 10, 2026.

Source: Aaronontheweb/dotnet-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.