dotnet-techne-qa-pipel…
Use when the user asks to verify a story or ticket implementation against its spec - per-AC verdicts, code reuse and design…
Use when designing types and collections for hot paths and low-allocation .NET code. Keywords: readonly struct, sealed class, ValueTask, Span, FrozenDictionary, FrozenSet, allocation optimisation.
Claude Code
$ npx -y skills add Metalnib/dotnet-episteme-skills --skill dotnet-techne-csharp-type-design-performance --agent claude-code
Codex
$ npx skills add Metalnib/dotnet-episteme-skills --skill dotnet-techne-csharp-type-design-performance -a codex -y
Cursor
$ npx skills add Metalnib/dotnet-episteme-skills --skill dotnet-techne-csharp-type-design-performance -a cursor -y
Gemini CLI
$ npx skills add Metalnib/dotnet-episteme-skills --skill dotnet-techne-csharp-type-design-performance -a gemini-cli -y
GitHub Copilot
$ npx skills add Metalnib/dotnet-episteme-skills --skill dotnet-techne-csharp-type-design-performance -a github-copilot -y
OpenCode
$ npx skills add Metalnib/dotnet-episteme-skills --skill dotnet-techne-csharp-type-design-performance -a opencode -y
Windsurf
$ npx skills add Metalnib/dotnet-episteme-skills --skill dotnet-techne-csharp-type-design-performance -a windsurf -y
name: dotnet-techne-csharp-type-design-performance
description: "Use when designing types and collections for hot paths and low-allocation .NET code. Keywords: readonly struct, sealed class, ValueTask, Span, FrozenDictionary, FrozenSet, allocation optimisation."
disable-model-invocation: false
user-invocable: true
metadata:
author: Metalnib
version: "1.0.0"
trigger_keywords:
- readonly struct
- sealed class
- valuetask
- span
- frozendictionary
- frozenset
- allocation optimisationUse this skill when:
---
1. **Seal your types** - Unless explicitly designed for inheritance 2. **Prefer readonly structs** - For small, immutable value types 3. **Prefer static pure functions** - Better performance and testability 4. **Defer enumeration** - Don't materialize until you need to 5. **Return immutable collections** - From API boundaries
---
Sealing classes enables JIT devirtualization and communicates API intent.
// DO: Seal classes not designed for inheritance
public sealed class OrderProcessor
{
public void Process(Order order) { }
}
// DO: Seal records (they're classes)
public sealed record OrderCreated(OrderId Id, CustomerId CustomerId);
// DON'T: Leave unsealed without reason
public class OrderProcessor // Can be subclassed - intentional?
{
public virtual void Process(Order order) { } // Virtual = slower
}**Benefits:**
---
Structs should be `readonly` when immutable. This prevents defensive copies.
// DO: Readonly struct for immutable value types
public readonly record struct OrderId(Guid Value)
{
public static OrderId New() => new(Guid.NewGuid());
public override string ToString() => Value.ToString();
}
// DO: Readonly struct for small, short-lived data
public readonly struct Money
{
public decimal Amount { get; }
public string Currency { get; }
public Money(decimal amount, string currency)
{
Amount = amount;
Currency = currency;
}
}
// DON'T: Mutable struct (causes defensive copies)
public struct Point // Not readonly!
{
public int X { get; set; } // Mutable!
public int Y { get; set; }
}| Use Struct When | Use Class When | |-----------------|----------------| | Small (≤16 bytes typically) | Larger objects | | Short-lived | Long-lived | | Frequently allocated | Shared references needed | | Value semantics required | Identity semantics required | | Immutable | Mutable state |
---
Static methods with no side effects are faster and more testable.
// DO: Static pure function
public static class OrderCalculator
{
public static Money CalculateTotal(IReadOnlyList<OrderItem> items)
{
var total = items.Sum(i => i.Price * i.Quantity);
return new Money(total, "USD");
}
}
// Usage - predictable, testable
var total = OrderCalculator.CalculateTotal(items);**Benefits:**
// DON'T: Instance method hiding dependencies
public class OrderCalculator
{
private readonly ITaxService _taxService; // Hidden dependency
private readonly IDiscountService _discountService; // Hidden dependency
public Money CalculateTotal(IReadOnlyList<OrderItem> items)
{
// What does this actually depend on?
}
}
// BETTER: Explicit dependencies via parameters
public static class OrderCalculator
{
public static Money CalculateTotal(
IReadOnlyList<OrderItem> items,
decimal taxRate,
decimal discountPercent)
{
// All inputs visible
}
}**Don't go overboard** - Use instance methods when you genuinely need state or polymorphism.
---
Don't materialize enumerables until necessary. Avoid excessive LINQ chains.
// BAD: Premature materialization
public IReadOnlyList<Order> GetActiveOrders()
{
return _orders
.Where(o => o.IsActive)
.ToList() // Materialized!
.OrderBy(o => o.CreatedAt) // Another iteration
.ToList(); // Materialized again!
}
// GOOD: Defer until the end
public IReadOnlyList<Order> GetActiveOrders()
{
return _orders
.Where(o => o.IsActive)
.OrderBy(o => o.CreatedAt)
.ToList(); // Single materialization
}
// GOOD: Return IEnumerable if caller might not need all items
public IEnumerable<Order> GetActiveOrders()
{
return _orders
.Where(o => o.IsActive)
.OrderBy(o => o.CreatedAt);
// Caller decides when to materialize
}Be careful with async and IEnumerable:
// BAD: Async in LINQ - hidden allocations
var results = orders
.Select(async o => await ProcessOrderAsync(o)) // Task per item!
.ToList();
await Task.WhenAll(results);
// GOOD: Use IAsyncEnumerable for streaming
public async IAsyncEnumerable<OrderResult> ProcessOrdersAsync(
IEnumerable<Order> orders,
[EnumeratorCancellation] CancellationToken ct = default)
{
foreach (var order in orders)
{
ct.ThrowIfCancellationRequested();
yield return await ProcessOrderAsync(order, ct);
}
}
// GOOD: Batch processing for parallelism
var results = await Task.WhenAll(
orders.Select(o => ProcessOrderAsync(o)));---
Use `ValueTask` for hot paths that often complete synchronously. For real I/O, just use `Task`.
// DO: ValueTask for cached/synchronous pa
DotNet Episteme Skills - a curated, manual-first .NET AI skills library rooted in systematic knowledge (episteme) and shaped by disciplined craft (techne), designed for engineers who prioritise precision over hype.
Use when the user asks to verify a story or ticket implementation against its spec - per-AC verdicts, code reuse and design…
Use when the user asks for a phase-gated refactoring or redesign loop - session-blind workers map and trace the area, empirical…
Use when the user asks for a multi-agent .NET code review - five parallel reviewers (correctness, performance,…
Use when reviewing PRs/diffs/branches/documents for .NET quality, correctness, performance, security, data access, messaging, and…
Use when you need CRAP score and coverage risk analysis to find high-risk code before refactoring or release. Keywords: CRAP…
Use when reviewing a .NET pull request for breaking changes that may affect other microservice repositories. Detects cross-repo…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.