Overview
Prefer compile-time source generation over runtime reflection for repetitive cross-cutting
concerns like mapping, serialization, regex, and logging. Source generation provides
determinism, compile-time verification, reduced runtime overhead, and better AOT/trimming
compatibility.
When to Use
- Introducing or reviewing repetitive cross-cutting mechanisms (mappers, serializers, regex, logging templates)
- Building performance-sensitive systems or services with startup-time concerns
- Targeting AOT compilation or assembly trimming scenarios
- Evaluating mapping or serialization libraries for a new project
- Reviewing PRs that propose reflection-based or runtime codegen approaches
Core Workflow
- Identify the cross-cutting concern (mapping, serialization, regex, logging)
- Check if a source-generation library exists for the use case (e.g., Mapperly, System.Text.Json source generators, GeneratedRegex)
- Evaluate the source-gen library against requirements (API compatibility, feature set, maintainability)
- Implement using the source-generation approach
- Verify compile-time generation is working (check generated files, no runtime reflection warnings)
- Benchmark if performance is critical (use the minimal benchmark template in Load: advanced)
Core
When to use
- Introducing or reviewing repetitive cross-cutting mechanisms (mappers, serializers, regex, logging templates).
- Performance-sensitive systems, services with startup-time concerns, or AOT/trimming constraints.
Defaults (strong preference)
- Prefer source generation over:
- reflection-based scanning,
- runtime expression compilation,
- dynamic invocation for repetitive tasks.
Rationale
- Determinism and compile-time verification.
- Reduced runtime overhead and improved diagnosability.
- Better compatibility with AOT/trimming scenarios.
Review rules
- If a runtime/reflection-based tool is proposed, require explicit justification:
- functional necessity,
- measurable benefits,
- absence of acceptable OSS source-gen alternatives.
Load: examples
- Mapping: prefer a source-generated mapper (e.g., Mapperly-style approach).
- Regex: use compile-time generated regex for hot paths.
- Logging: prefer compile-time friendly patterns (e.g., message template generators where appropriate).
Load: advanced
AOT/trimming checklist
- Avoid reflection-based discovery for core execution paths.
- Ensure analyzers/source generators are included and pinned.
- Validate publish trimming warnings and address them as part of release readiness.
Benchmarking guidance
- Benchmark representative payload sizes and typical request flows.
- Focus on startup time, allocations, and throughput for mapping/serialization-heavy systems.
Minimal benchmark template
When evaluating reflection vs source-generated approaches, use this template:
[MemoryDiagnoser]
public class MappingBenchmark
{
private MySourceEntity _source = default!;
private Mapper _mapper = default!;
[GlobalSetup]
public void Setup() => _mapper = new Mapper();
[Benchmark]
public MyTargetDto Reflection_MapViaReflection() => MapViaReflection(_source);
[Benchmark]
public MyTargetDto SourceGenerated_MapViaSourceGen() => _mapper.Map(_source);
}
Focus areas:
- Startup time (cold startup with reflection vs AOT-friendly source gen)
- Allocations per operation
- Throughput (operations/sec) for hot paths
- Comparison baseline: measure reflection first, then source-gen
Acceptable exceptions to source-generation-first
Reflection/runtime codegen may be used when:
Ad-hoc/one-time operations: Not part of hot paths; cost is negligible.
- Example: Loading configuration at startup (once per app lifetime)
- Justification: Overhead is paid once, not per-request
Highly dynamic scenarios: Type information unavailable at compile-time.
- Example: Plugin systems where types loaded at runtime from external assemblies
- Justification: No source-gen alternative exists; runtime introspection necessary
Backwards compatibility constraints: Source-gen requires breaking API changes.
- Example: Maintaining legacy API surface while migrating to source-gen
- Justification: Breaking change risk outweighs performance benefit; schedule migration
Prototype/experimental phases: Validation before investing in source-gen.
- Example: Proof-of-concept that reflection will later be replaced
- Justification: Speed of iteration > performance; document migration plan
Required for exceptions:
- Explicit PR justification
- Clear scope boundary (not used in hot paths)
- Migration path documented (if temporary)
Load: enforcement
- Any PR adding reflection-based mapping or runtime codegen must include:
- a justification,
- a benchmark or measurable rationale,
- confirmation that no suitable OSS source-gen alternative exists.
Red Flags - STOP
These statements indicate source generation bypass:
| Thought |
Reality |
| "Reflection is more flexible" |
Source gen handles most cases; flexibility rarely needed |
| "Startup cost doesn't matter" |
AOT/trimming require source gen; plan for it early |
| "We'll optimize later" |
Retrofitting source gen is expensive; start with it |
| "No source-gen library exists" |
Check thoroughly; ecosystem is rapidly growing |
| "It's just a few operations" |
Hot paths compound; measure before dismissing |
| "Dynamic types require reflection" |
True for plugins; false for most business code |
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: dotnet-source-generation-first3description: Prefer compile-time source generation over runtime evaluation for repetitive cross-cutting concerns (mapping, logging, regex, etc.). Use when this capability is needed.4---56## Overview78Prefer compile-time source generation over runtime reflection for repetitive cross-cutting9concerns like mapping, serialization, regex, and logging. Source generation provides10determinism, compile-time verification, reduced runtime overhead, and better AOT/trimming11compatibility.1213## When to Use1415- Introducing or reviewing repetitive cross-cutting mechanisms (mappers, serializers, regex, logging templates)16- Building performance-sensitive systems or services with startup-time concerns17- Targeting AOT compilation or assembly trimming scenarios18- Evaluating mapping or serialization libraries for a new project19- Reviewing PRs that propose reflection-based or runtime codegen approaches2021## Core Workflow22231. Identify the cross-cutting concern (mapping, serialization, regex, logging)242. Check if a source-generation library exists for the use case (e.g., Mapperly, System.Text.Json source generators, GeneratedRegex)253. Evaluate the source-gen library against requirements (API compatibility, feature set, maintainability)264. Implement using the source-generation approach275. Verify compile-time generation is working (check generated files, no runtime reflection warnings)286. Benchmark if performance is critical (use the minimal benchmark template in Load: advanced)2930## Core3132### When to use3334- Introducing or reviewing repetitive cross-cutting mechanisms (mappers, serializers, regex, logging templates).35- Performance-sensitive systems, services with startup-time concerns, or AOT/trimming constraints.3637### Defaults (strong preference)3839- Prefer **source generation** over:40 - reflection-based scanning,41 - runtime expression compilation,42 - dynamic invocation for repetitive tasks.4344### Rationale4546- Determinism and compile-time verification.47- Reduced runtime overhead and improved diagnosability.48- Better compatibility with AOT/trimming scenarios.4950### Review rules5152- If a runtime/reflection-based tool is proposed, require explicit justification:53 - functional necessity,54 - measurable benefits,55 - absence of acceptable OSS source-gen alternatives.5657## Load: examples5859- Mapping: prefer a source-generated mapper (e.g., Mapperly-style approach).60- Regex: use compile-time generated regex for hot paths.61- Logging: prefer compile-time friendly patterns (e.g., message template generators where appropriate).6263## Load: advanced6465### AOT/trimming checklist6667- Avoid reflection-based discovery for core execution paths.68- Ensure analyzers/source generators are included and pinned.69- Validate publish trimming warnings and address them as part of release readiness.7071### Benchmarking guidance7273- Benchmark representative payload sizes and typical request flows.74- Focus on startup time, allocations, and throughput for mapping/serialization-heavy systems.7576### Minimal benchmark template7778When evaluating reflection vs source-generated approaches, use this template:7980```csharp81[MemoryDiagnoser]82public class MappingBenchmark83{84 private MySourceEntity _source = default!;85 private Mapper _mapper = default!;8687 [GlobalSetup]88 public void Setup() => _mapper = new Mapper();8990 [Benchmark]91 public MyTargetDto Reflection_MapViaReflection() => MapViaReflection(_source);9293 [Benchmark]94 public MyTargetDto SourceGenerated_MapViaSourceGen() => _mapper.Map(_source);95}96```9798**Focus areas:**99100- Startup time (cold startup with reflection vs AOT-friendly source gen)101- Allocations per operation102- Throughput (operations/sec) for hot paths103- Comparison baseline: measure reflection first, then source-gen104105### Acceptable exceptions to source-generation-first106107Reflection/runtime codegen may be used when:1081091. **Ad-hoc/one-time operations**: Not part of hot paths; cost is negligible.110 - Example: Loading configuration at startup (once per app lifetime)111 - Justification: Overhead is paid once, not per-request1121132. **Highly dynamic scenarios**: Type information unavailable at compile-time.114 - Example: Plugin systems where types loaded at runtime from external assemblies115 - Justification: No source-gen alternative exists; runtime introspection necessary1161173. **Backwards compatibility constraints**: Source-gen requires breaking API changes.118 - Example: Maintaining legacy API surface while migrating to source-gen119 - Justification: Breaking change risk outweighs performance benefit; schedule migration1201214. **Prototype/experimental phases**: Validation before investing in source-gen.122 - Example: Proof-of-concept that reflection will later be replaced123 - Justification: Speed of iteration > performance; document migration plan124125**Required for exceptions:**126127- Explicit PR justification128- Clear scope boundary (not used in hot paths)129- Migration path documented (if temporary)130131## Load: enforcement132133- Any PR adding reflection-based mapping or runtime codegen must include:134 - a justification,135 - a benchmark or measurable rationale,136 - confirmation that no suitable OSS source-gen alternative exists.137138## Red Flags - STOP139140These statements indicate source generation bypass:141142| Thought | Reality |143| ---------------------------------- | -------------------------------------------------------- |144| "Reflection is more flexible" | Source gen handles most cases; flexibility rarely needed |145| "Startup cost doesn't matter" | AOT/trimming require source gen; plan for it early |146| "We'll optimize later" | Retrofitting source gen is expensive; start with it |147| "No source-gen library exists" | Check thoroughly; ecosystem is rapidly growing |148| "It's just a few operations" | Hot paths compound; measure before dismissing |149| "Dynamic types require reflection" | True for plugins; false for most business code |150151---152> Converted and distributed by [TomeVault](https://tomevault.io/claim/mcj-coder) — claim your Tome and manage your conversions.153<!-- tomevault:4.0:skill_md:2026-04-14 -->