@@ -149,3 +149,210 @@ Keep commit messages clear and concise:
149149- Auto-publish to NuGet on push to main (patches only)
150150- Auto-create GitHub Release on git tags for major/minor versions
151151- Dependabot for dependency updates
152+
153+ ## .NET 10 Best Practices (Research Updated: November 2025)
154+
155+ ### Key Improvements in .NET 10
156+
157+ ** Performance:**
158+ - AsyncTaskMethodBuilder optimizations - reduced async overhead
159+ - ValueTask<T > expansion - zero allocations for synchronous completions
160+ - Array enumeration inlining - faster iterations
161+ - Background GC compaction improvements - reduced pause times
162+
163+ ** C# 14 Features:**
164+ - Field-backed properties with ` field ` keyword
165+ - Extension members (properties, static methods)
166+ - Null-conditional assignment operator ` ?.= `
167+ - Implicit Span conversions
168+
169+ ** ASP.NET Core 10:**
170+ - Native SSE support via ` TypedResults.ServerSentEvents() `
171+ - OpenAPI 3.1 with JSON Schema 2020-12
172+ - Cookie authentication behavior changes (401/403 instead of redirects for APIs)
173+ - Built-in validation support for minimal APIs
174+
175+ ### Library-Specific Recommendations
176+
177+ #### SourceLink Configuration (Critical for Debugging)
178+ Add to ` .csproj ` for better consumer debugging experience:
179+ ``` xml
180+ <PropertyGroup >
181+ <PublishRepositoryUrl >true</PublishRepositoryUrl >
182+ <EmbedUntrackedSources >true</EmbedUntrackedSources >
183+ </PropertyGroup >
184+
185+ <ItemGroup >
186+ <PackageReference Include =" Microsoft.SourceLink.GitHub" Version =" *" PrivateAssets =" All" />
187+ </ItemGroup >
188+ ```
189+
190+ #### Nullable Reference Types
191+ - New projects have ` <Nullable>enable</Nullable> ` by default in .NET 10
192+ - Use "Boy Scout Rule" for existing code: enable file-by-file with ` #nullable enable `
193+ - Avoid null-forgiving operator ` ! ` when possible
194+
195+ #### NuGet Audit (.NET 10 Feature)
196+ - Automatically audits transitive dependencies for security issues
197+ - Run ` dotnet package update --vulnerable ` regularly
198+ - Integrates with GitHub Advisory Database
199+
200+ ### Async/Await Best Practices
201+
202+ #### Channel<T > Patterns
203+ ** Current Implementation is Good, but Consider:**
204+
205+ 1 . ** Add Channel Options** for better control:
206+ ``` csharp
207+ var options = new UnboundedChannelOptions
208+ {
209+ SingleReader = true , // Each client has one reader
210+ SingleWriter = false , // Multiple publishers
211+ AllowSynchronousContinuations = false // Avoid blocking publisher
212+ };
213+ ```
214+
215+ 2 . ** Handle Channel Completion** :
216+ ``` csharp
217+ public void Publish (T item )
218+ {
219+ foreach (var (clientId , channel ) in _channels )
220+ {
221+ if (! channel .Writer .TryWrite (item ))
222+ {
223+ // Channel completed or full - remove dead channels
224+ _channels .TryRemove (clientId , out _ );
225+ }
226+ }
227+ }
228+ ```
229+
230+ #### IAsyncEnumerable Guidelines
231+ - Use ` [EnumeratorCancellation] ` attribute on CancellationToken parameters
232+ - Methods returning ` IAsyncEnumerable<T> ` should end with "Async" suffix
233+ - Never call ` .Result ` or ` .Wait() ` - deadlock risk
234+ - Implement ` IAsyncDisposable ` for resources
235+
236+ #### ValueTask Optimization
237+ - Use ` ValueTask<T> ` for high-throughput scenarios (>90% synchronous completion)
238+ - .NET 10 has zero-allocation ValueTask for synchronous paths
239+ - Only apply after benchmarking shows benefit
240+
241+ ### Testing Best Practices
242+
243+ #### Avoid Flaky Tests
244+ ** Root cause of test flakiness:**
245+ - 54% race conditions (use condition-based waits, not arbitrary delays)
246+ - 30% timing issues (increase timeouts for CI environments)
247+ - 16% shared state pollution
248+
249+ ** Solution - Polling Helper Pattern:**
250+ ``` csharp
251+ public static async Task WaitUntilAsync (
252+ Func < bool > condition ,
253+ TimeSpan ? timeout = null ,
254+ string ? timeoutMessage = null ,
255+ CancellationToken cancellationToken = default )
256+ {
257+ timeout ??= TimeSpan .FromSeconds (10 );
258+ using var cts = CancellationTokenSource .CreateLinkedTokenSource (cancellationToken );
259+ cts .CancelAfter (timeout .Value );
260+
261+ try
262+ {
263+ while (! condition ())
264+ {
265+ await Task .Delay (TimeSpan .FromMilliseconds (50 ), cts .Token );
266+ }
267+ }
268+ catch (OperationCanceledException ) when (! cancellationToken .IsCancellationRequested )
269+ {
270+ throw new TimeoutException (timeoutMessage ?? " Condition not met within timeout" );
271+ }
272+ }
273+ ```
274+
275+ ** Usage:**
276+ ``` csharp
277+ // Instead of: while (sseStream.ClientCount == 0) await Task.Delay(50);
278+ await WaitUntilAsync (() => sseStream .ClientCount > 0 , TimeSpan .FromSeconds (10 ));
279+ ```
280+
281+ #### Testcontainers with IAsyncLifetime
282+ ** Best practice for shared containers:**
283+ ``` csharp
284+ public sealed class RabbitMqFixture : IAsyncLifetime
285+ {
286+ private readonly RabbitMqContainer _container = new RabbitMqBuilder ()
287+ .WithImage (" rabbitmq:3.13-management" )
288+ .Build ();
289+
290+ public string ConnectionString => _container .GetConnectionString ();
291+
292+ public async Task InitializeAsync () => await _container .StartAsync ();
293+ public async Task DisposeAsync () => await _container .DisposeAsync ();
294+ }
295+
296+ [CollectionDefinition (" RabbitMQ Collection" )]
297+ public class RabbitMqCollection : ICollectionFixture <RabbitMqFixture > { }
298+ ```
299+
300+ ** Benefits:**
301+ - Container started once per test collection (faster)
302+ - Proper async lifecycle management
303+ - Automatic cleanup
304+
305+ #### xUnit v3 Features
306+ - Use ` TestContext.Current.CancellationToken ` for test timeouts
307+ - Use ` IAsyncLifetime ` for async setup/teardown
308+ - Tests automatically cancelled after timeout
309+
310+ ### RabbitMQ Integration Patterns
311+
312+ #### Core Principles
313+ - ** Publish to exchanges, not queues** - enables flexible routing
314+ - Use ` BasicAck ` to confirm successful processing
315+ - Use ` BasicNack ` with requeue for transient failures
316+ - Implement Dead Letter Exchange (DLX) for permanent failures
317+
318+ #### Retry Patterns
319+ ** Quorum Queues (Modern Approach):**
320+ - Set "Delivery limit" argument for automatic retry limits
321+ - Automatically dead-letters after max retries
322+
323+ ** Exponential Backoff:**
324+ - Use multiple wait queues with TTL: 1min, 5min, 15min
325+ - Each queue dead-letters back to application exchange
326+ - Track retry count via ` x-death ` header (RabbitMQ 3.8+)
327+
328+ #### Framework Considerations
329+ - For complex scenarios, consider NServiceBus or MassTransit
330+ - Provide built-in retry, saga, and error handling patterns
331+
332+ ### Security & Breaking Changes
333+
334+ #### ASP.NET Core 10 Authentication
335+ - Unauthenticated API requests now return 401/403 instead of redirecting
336+ - Applies to endpoints with ` IApiEndpointMetadata `
337+ - If exposing HTTP endpoints, ensure proper 401/403 handling
338+
339+ #### NuGet Package Security
340+ - Always use SPDX license expressions (not deprecated LicenseUrl)
341+ - Include README.md in package for better first impressions
342+ - Enable NuGet audit in CI/CD pipelines
343+
344+ ### Sources & References
345+
346+ ** Official Microsoft:**
347+ - .NET 10 GA Release (November 2025)
348+ - ASP.NET Core 10 Release Notes
349+ - C# 14 What's New
350+
351+ ** Community Experts:**
352+ - Stephen Toub - .NET 10 Performance Improvements
353+ - Stephen Cleary - Async/Await Best Practices
354+ - Andrew Lock - .NET 10 Deep Dive Series
355+ - Milan Jovanovic - Testcontainers & Integration Testing
356+ - Khalid Abuhakmeh - Server-Sent Events in .NET 10
357+
358+ ** Last Updated:** November 20, 2025
0 commit comments