Generic Math in .NET with INumber<T>
Before .NET 7, writing generic code that worked across numeric types meant ugly workarounds or multiple overloads. You couldn't write a generic Sum<T> that handled both int and double — T just didn't ...
Idempotency Keys in ASP.NET Core APIs
If you've ever had a client retry a POST after a timeout, you already know the pain: one user action can create two orders, two payments, or two emails. The retry was valid, but the side effect happen...
Outbox Pattern for Reliable Messaging in .NET
If you've ever saved data to your database and then tried to publish an event right after, you've probably wondered: what happens if the app crashes between those two steps? That's the classic dual-wr...
params Collections in C# 13
If you've ever written a helper method that accepts a variable number of arguments and then immediately needed to pass the results to something that wants a List<T> or a Span<T>, C# 13's expanded para...
CallerArgumentExpression in C# 10
If you've ever written guard clauses and felt annoyed repeating parameter names as strings, CallerArgumentExpression fixes that. It's one of those small C# 10 features that quietly makes utility APIs ...
Required Members in C# 11
If you've ever created a model with object initialisers and then realised someone forgot to set a critical property, C# 11's required keyword is the fix you've wanted for years. It lets you keep objec...
Collection Expressions in C# 12
C# 12 introduced collection expressions — a unified, concise syntax for initialising lists, arrays, spans, and any collection type that supports it. If you've ever thought "there are too many ways to ...
Keyed Services in .NET 8
.NET 8 quietly added a feature to the dependency injection container that solves an annoying problem: what do you do when you have multiple implementations of the same interface and you need to inject...
Primary Constructors in C# 12
C# 12 added primary constructors to classes and structs — a feature records had since C# 9, finally extended to the rest of the type system. If you've spent time writing boilerplate constructor bodies...
ValueTask vs Task in .NET
Task is the bread and butter of async .NET — you use it everywhere without thinking twice. But there's another option that's been in the framework since .NET Core 2.0 and is worth knowing: ValueTask. ...
Custom JSON Converters in System.Text.Json
System.Text.Json handles most serialization scenarios out of the box, but every sufficiently complex application eventually hits a case where the default behaviour just isn't right. Maybe you've got a...
TimeProvider in .NET
If you've ever written code that calls DateTime.UtcNow or DateTimeOffset.Now directly, you've probably felt the pain in tests. The current time is hard to control, which means any test that depends on...
The Result Pattern in C#
Throwing exceptions for expected failure cases has always felt a bit off. An exception should be exceptional — something you genuinely didn't anticipate. But in most applications, "user not found" or ...
Built-in OpenAPI in ASP.NET Core
For years, if you wanted OpenAPI support in an ASP.NET Core app, you reached for Swashbuckle. It's been the de-facto standard — add the NuGet package, sprinkle some XML doc comments, call AddSwaggerGe...
Native AOT in .NET 10
For most of .NET's history, your code ran with a JIT compiler warming things up at startup. That's fine for long-running services, but there are scenarios where you want a self-contained binary that s...