Learn / .NET API Engineering Path
.NET Runtime and Diagnostics
A consolidated foundations lesson preserving 2 focused PTesting lessons without duplicating an unrelated authored PTLearn topic.
Course: .NET API Engineering Path. Level: Advanced. Topic: Typed backend.
Stage: basic - Foundation - Language and runtime foundations. Connect .net runtime and diagnostics to the professional workflow for .NET APIs.
Outcomes
- Describe managed execution, JIT compilation, and garbage collection.
- Identify allocation patterns that matter in server applications.
- Use diagnostic tools conceptually without premature optimization.
- Use analyzers, formatting, and central package management.
- Recognize common package choices and when they are appropriate.
- Avoid package sprawl and version drift.
Concepts
- Runtime, Memory, and Diagnostics
- Managed execution
- Garbage collection tradeoffs
- Modern C# and .NET foundations
- Guided practice
- Tooling and Popular Packages
- Quality tools
- Common packages
Concept flow
Show how language and runtime foundations moves from trigger to implementation outcome in .NET APIs.
- Language model
- Runtime behavior
- Engineering decision
- Verification evidence
Session flow
- Model Runtime, Memory, and Diagnostics (concept, 39 min) — Name the decisions behind Runtime, Memory, and Diagnostics before writing code.
- Describe managed execution, JIT compilation, and garbage collection.
- Explain where Runtime, Memory, and Diagnostics belongs in order management API.
- Build the vertical slice (walkthrough, 70 min) — Implement the smallest useful slice in legacy/dotnet-advanced/dotnet-runtime-and-diagnostics.txt.
- Identify allocation patterns that matter in server applications.
- Connect Managed execution to a working example.
- Verify and harden (exercise, 46 min) — Accepted answers: allocation
- Use diagnostic tools conceptually without premature optimization.
- Record one risk or follow-up before moving on.
- CLR, Garbage Collection, and Performance Clues: Managed execution (concept, 38 min) — The CLR manages type safety, method dispatch, exception handling, threads, and memory. Methods are commonly JIT-compiled the first time they run, with tiered compilation improving hot code over time.
- Retained source example: Measure an allocation-sensitive loop
long before = GC.GetAllocatedBytesForCurrentThread();
for (var i = 0; i < 1_000; i++)
{
_ = $"order-{i}";
}
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
Console.WriteLine(allocated);
This is a local measurement technique, not a benchmark. Use BenchmarkDotNet for rigorous comparisons.
- CLR, Garbage Collection, and Performance Clues: Garbage collection tradeoffs (walkthrough, 38 min) — The GC is generational: short-lived objects are usually cheap, long-lived object graphs are more expensive, and very large objects use the large object heap. In services, fewer avoidable allocations can reduce pause pressure and improve throughput.
- Retained source example: Use spans for slicing without new strings
static ReadOnlySpan<char> GetFileName(ReadOnlySpan<char> path)
{
var index = path.LastIndexOf('/');
return index < 0 ? path : path[(index + 1)..];
}
Span-based APIs can avoid allocations, but they add constraints. Prefer clarity until profiling shows a hot path.
- CLR, Garbage Collection, and Performance Clues: practices and mistakes (review, 5 min) — Retained guidance from the legacy PTesting lesson.
- Practice: Profile before optimizing and keep benchmark code separate from application code.
- Practice: Use built-in pooling and span APIs only where they simplify or measurably improve hot paths.
- Practice: Dispose resources that own unmanaged handles, such as streams, timers, and database connections.
- Avoid: Assuming the GC is a memory leak detector.
- Avoid: Optimizing micro-allocations while ignoring slow database calls.
- Avoid: Using unsafe code or pooling without tests for lifetime and reuse bugs.
- CLR, Garbage Collection, and Performance Clues: references (review, 2 min) — Original references retained from the legacy library.
- .NET garbage collection: https://learn.microsoft.com/dotnet/standard/garbage-collection/
- .NET diagnostics tools: https://learn.microsoft.com/dotnet/core/diagnostics/
- Professional .NET Toolchain: Quality tools (concept, 40 min) — Modern .NET projects can enforce style and correctness with analyzers, editorconfig, nullable warnings, formatting checks, and CI. These tools are most useful when they create consistent feedback before review.
- Retained source example: .editorconfig fragment
root = true
[*.cs]
dotnet_analyzer_diagnostic.category-Style.severity = warning
dotnet_diagnostic.CA2007.severity = none
dotnet_style_qualification_for_field = false:suggestion
EditorConfig lets formatting and analyzer preferences travel with the repository.
- Professional .NET Toolchain: Common packages (walkthrough, 40 min) — Popular packages solve recurring problems: Serilog for structured logging, FluentValidation for validation, Polly for resilience, Dapper for lightweight SQL mapping, EF Core for ORM data access, AutoMapper or Mapperly for object mapping, MediatR for in-process request dispatch, and Swashbuckle or Scalar for API documentation.
- Retained source example: Polly-style retry concept
builder.Services.AddHttpClient<PaymentsClient>()
.AddStandardResilienceHandler();
Use resilience policies for transient outbound failures. Keep retry counts modest and avoid retrying non-idempotent work blindly.
- Professional .NET Toolchain: practices and mistakes (review, 5 min) — Retained guidance from the legacy PTesting lesson.
- Practice: Use dotnet format or IDE formatting in CI for consistent code style.
- Practice: Adopt analyzers gradually and keep warnings actionable.
- Practice: Prefer well-maintained packages with clear ownership and release history.
- Practice: Centralize package versions in larger solutions.
- Avoid: Ignoring analyzer warnings until they become background noise.
- Avoid: Adding packages before checking whether the base class library already solves the problem.
- Avoid: Letting different projects drift to incompatible package versions.
- Professional .NET Toolchain: references (review, 2 min) — Original references retained from the legacy library.
- Code analysis in .NET: https://learn.microsoft.com/dotnet/fundamentals/code-analysis/overview
- NuGet package management: https://learn.microsoft.com/nuget/consume-packages/overview-and-workflow
Code example
csharp in legacy/dotnet-advanced/dotnet-runtime-and-diagnostics.txt.
Console.WriteLine("PTLearn foundation");
Walkthrough examples
- .NET Runtime and Diagnostics in a order management API — A team is extending a production-grade ASP.NET Core order API and needs this lesson's pattern to be clear enough for review, testing, and future maintenance.
- File: legacy/dotnet-advanced/dotnet-runtime-and-diagnostics.txt
- File: src/Orders.Application/dotnet-runtime-and-diagnostics.cs
- File: tests/Orders.Api.Tests/dotnet-runtime-and-diagnostics.Tests.cs
- File: docs/dotnet-advanced/dotnet-runtime-and-diagnostics.md
- Start from the provided csharp snippet and make the intent visible in names and boundaries.
- Apply the checklist item "Explain the language or runtime rule in your own words" before adding extra behavior.
- Write down how the implementation changes when Managed execution fails or becomes slow.
- Measure an allocation-sensitive loop — This is a local measurement technique, not a benchmark. Use BenchmarkDotNet for rigorous comparisons.
- Retained source code:
long before = GC.GetAllocatedBytesForCurrentThread();
for (var i = 0; i < 1_000; i++)
{
_ = $"order-{i}";
}
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
Console.WriteLine(allocated);
- Explain the expected behavior.
- Compare the example with the canonical PTLearn implementation.
- Use spans for slicing without new strings — Span-based APIs can avoid allocations, but they add constraints. Prefer clarity until profiling shows a hot path.
- Retained source code:
static ReadOnlySpan<char> GetFileName(ReadOnlySpan<char> path)
{
var index = path.LastIndexOf('/');
return index < 0 ? path : path[(index + 1)..];
}
- Explain the expected behavior.
- Compare the example with the canonical PTLearn implementation.
- Diagnostics tool examples — Counters show live runtime metrics. Traces capture deeper event data for later analysis.
- Retained source code:
dotnet-counters monitor --process-id <pid>
dotnet-trace collect --process-id <pid>
- Explain the expected behavior.
- Compare the example with the canonical PTLearn implementation.
- .editorconfig fragment — EditorConfig lets formatting and analyzer preferences travel with the repository.
- Retained source code:
root = true
[*.cs]
dotnet_analyzer_diagnostic.category-Style.severity = warning
dotnet_diagnostic.CA2007.severity = none
dotnet_style_qualification_for_field = false:suggestion
- Explain the expected behavior.
- Compare the example with the canonical PTLearn implementation.
- Polly-style retry concept — Use resilience policies for transient outbound failures. Keep retry counts modest and avoid retrying non-idempotent work blindly.
- Retained source code:
builder.Services.AddHttpClient<PaymentsClient>()
.AddStandardResilienceHandler();
- Explain the expected behavior.
- Compare the example with the canonical PTLearn implementation.
- Central package management — Directory.Packages.props can centralize package versions across a multi-project solution.
- Retained source code:
<Project>
<ItemGroup>
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="FluentValidation" Version="12.0.0" />
</ItemGroup>
</Project>
- Explain the expected behavior.
- Compare the example with the canonical PTLearn implementation.
Practice
- Classify an optimization: A request handler creates thousands of temporary strings per request. Is this primarily CPU, allocation, or network optimization work?
- Starter code: classification = ____
- Expected output: allocation
- Hint: Temporary strings create managed objects for the GC to track and collect.
- Reference solution: This is primarily allocation optimization work, though reducing allocations can also reduce CPU used by GC.
- Accepted answers: allocation
- Match package to purpose: Which common package family is typically used for structured application logging?
- Starter code: package = ____
- Expected output: Serilog
- Hint: It is often configured with Serilog.AspNetCore.
- Reference solution: Serilog is a common structured logging package for .NET applications.
- Accepted answers: Serilog | Serilog.AspNetCore
Checklist
- Explain the language or runtime rule in your own words
- Run one focused example and record its output
- Name one failure mode or tradeoff
- Keep the verification evidence with the lesson
Quiz prompts
- A teammate wants to hide Runtime, Memory, and Diagnostics inside a convenient helper. What should you check first? — Place Runtime, Memory, and Diagnostics at the boundary that keeps order management API behavior explicit, testable, and reviewable.
- Which artifact best proves this .NET APIs lesson is ready for review? — Production-ready learning needs evidence: a test, trace, command, screenshot, or log that catches the risk again.
- What is the safest first step before optimizing allocations in production code? — Optimization should follow evidence. Many allocations are harmless compared with database, network, or algorithmic costs.
- What is the risk of adding a package for every small helper? — Every dependency has maintenance, security, compatibility, and review costs.
Flashcards
- In .NET APIs, what should you remember about Runtime, Memory, and Diagnostics? Runtime, Memory, and Diagnostics matters here because it supports "Describe managed execution, JIT compilation, and garbage collection.".
- In .NET APIs, what should you remember about Managed execution? Managed execution matters here because it supports "Identify allocation patterns that matter in server applications.".
- In .NET APIs, what should you remember about Garbage collection tradeoffs? Garbage collection tradeoffs matters here because it supports "Use diagnostic tools conceptually without premature optimization.".
- In .NET APIs, what should you remember about Modern C# and .NET foundations? Modern C# and .NET foundations matters here because it supports "Use analyzers, formatting, and central package management.".
Labs
- Ship a .net runtime and diagnostics slice — Extend a production-grade ASP.NET Core order API with a small but reviewable feature that proves the lesson's architecture in code.
- Classify an optimization: A request handler creates thousands of temporary strings per request. Is this primarily CPU, allocation, or network optimization work?
- Starter code: classification = ____
- Expected output: allocation
- Hint: Temporary strings create managed objects for the GC to track and collect.
- Reference solution: This is primarily allocation optimization work, though reducing allocations can also reduce CPU used by GC.
- Accepted answers: allocation
- The implementation demonstrates Runtime, Memory, and Diagnostics without hidden global state.
- At least one test or verification step covers the main success path.
- The review notes explain the tradeoff behind Managed execution.
Challenge
- Review-ready .net runtime and diagnostics (Core) — Turn the lesson work into a pull-request-sized change for order management API. Include the code, verification notes, one explicit tradeoff, and an updated concept diagram that names the riskiest handoff.
- All checklist items are either implemented or documented with a reason.
- The change can be understood from legacy/dotnet-advanced/dotnet-runtime-and-diagnostics.txt plus one short note.
- The concept diagram names ownership, failure handling, and verification points.
- A teammate could run the verification steps without asking for hidden context.
Canonical lesson URL