Skip to content
Development
Skill

/aot-trimming

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

From plugin
aaronontheweb-dotnet-skills
1.2k37 skills6 agents
Install
$ npx -y skills add aaronontheweb/dotnet-skills --skill aot-trimming --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/aot-trimming

Context preview

The summary Claude sees to decide when to auto-load this skill.

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

SKILL.md

aot-trimming.SKILL.md
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:

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

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

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

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:

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

See [aot-trimming-playbook-reference.md](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](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.
  • `[UnconditionalSuppr
Read more
Ships withaaronontheweb-dotnet-skills

A comprehensive AI coding plugin with 30 skills and 5 specialized agents for professional .NET development. Battle-tested patterns from production systems covering C#, Akka.NET, Aspire, EF Core, testing, and performance optimization.

Get the whole plugin