agentic-team-templates 0.13.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +6 -1
  2. package/package.json +1 -1
  3. package/src/index.js +22 -2
  4. package/src/index.test.js +5 -0
  5. package/templates/cpp-expert/.cursorrules/concurrency.md +211 -0
  6. package/templates/cpp-expert/.cursorrules/error-handling.md +170 -0
  7. package/templates/cpp-expert/.cursorrules/memory-and-ownership.md +220 -0
  8. package/templates/cpp-expert/.cursorrules/modern-cpp.md +211 -0
  9. package/templates/cpp-expert/.cursorrules/overview.md +87 -0
  10. package/templates/cpp-expert/.cursorrules/performance.md +223 -0
  11. package/templates/cpp-expert/.cursorrules/testing.md +230 -0
  12. package/templates/cpp-expert/.cursorrules/tooling.md +312 -0
  13. package/templates/cpp-expert/CLAUDE.md +242 -0
  14. package/templates/csharp-expert/.cursorrules/aspnet-core.md +311 -0
  15. package/templates/csharp-expert/.cursorrules/async-patterns.md +206 -0
  16. package/templates/csharp-expert/.cursorrules/dependency-injection.md +206 -0
  17. package/templates/csharp-expert/.cursorrules/error-handling.md +235 -0
  18. package/templates/csharp-expert/.cursorrules/language-features.md +204 -0
  19. package/templates/csharp-expert/.cursorrules/overview.md +92 -0
  20. package/templates/csharp-expert/.cursorrules/performance.md +251 -0
  21. package/templates/csharp-expert/.cursorrules/testing.md +282 -0
  22. package/templates/csharp-expert/.cursorrules/tooling.md +254 -0
  23. package/templates/csharp-expert/CLAUDE.md +360 -0
  24. package/templates/java-expert/.cursorrules/concurrency.md +209 -0
  25. package/templates/java-expert/.cursorrules/error-handling.md +205 -0
  26. package/templates/java-expert/.cursorrules/modern-java.md +216 -0
  27. package/templates/java-expert/.cursorrules/overview.md +81 -0
  28. package/templates/java-expert/.cursorrules/performance.md +239 -0
  29. package/templates/java-expert/.cursorrules/persistence.md +262 -0
  30. package/templates/java-expert/.cursorrules/spring-boot.md +262 -0
  31. package/templates/java-expert/.cursorrules/testing.md +272 -0
  32. package/templates/java-expert/.cursorrules/tooling.md +301 -0
  33. package/templates/java-expert/CLAUDE.md +325 -0
  34. package/templates/javascript-expert/.cursorrules/overview.md +5 -3
  35. package/templates/javascript-expert/.cursorrules/typescript-deep-dive.md +348 -0
  36. package/templates/javascript-expert/CLAUDE.md +34 -3
  37. package/templates/kotlin-expert/.cursorrules/coroutines.md +237 -0
  38. package/templates/kotlin-expert/.cursorrules/error-handling.md +149 -0
  39. package/templates/kotlin-expert/.cursorrules/frameworks.md +227 -0
  40. package/templates/kotlin-expert/.cursorrules/language-features.md +231 -0
  41. package/templates/kotlin-expert/.cursorrules/overview.md +77 -0
  42. package/templates/kotlin-expert/.cursorrules/performance.md +185 -0
  43. package/templates/kotlin-expert/.cursorrules/testing.md +213 -0
  44. package/templates/kotlin-expert/.cursorrules/tooling.md +258 -0
  45. package/templates/kotlin-expert/CLAUDE.md +276 -0
  46. package/templates/swift-expert/.cursorrules/concurrency.md +230 -0
  47. package/templates/swift-expert/.cursorrules/error-handling.md +213 -0
  48. package/templates/swift-expert/.cursorrules/language-features.md +246 -0
  49. package/templates/swift-expert/.cursorrules/overview.md +88 -0
  50. package/templates/swift-expert/.cursorrules/performance.md +260 -0
  51. package/templates/swift-expert/.cursorrules/swiftui.md +260 -0
  52. package/templates/swift-expert/.cursorrules/testing.md +286 -0
  53. package/templates/swift-expert/.cursorrules/tooling.md +285 -0
  54. package/templates/swift-expert/CLAUDE.md +275 -0
@@ -0,0 +1,206 @@
1
+ # C# Dependency Injection
2
+
3
+ The built-in Microsoft.Extensions.DependencyInjection container is the standard. Use it correctly.
4
+
5
+ ## Service Lifetimes
6
+
7
+ ```csharp
8
+ // Transient: new instance every time. Use for lightweight, stateless services.
9
+ builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();
10
+
11
+ // Scoped: one instance per request/scope. Use for DbContext, Unit of Work.
12
+ builder.Services.AddScoped<IOrderRepository, OrderRepository>();
13
+ builder.Services.AddDbContext<AppDbContext>();
14
+
15
+ // Singleton: one instance for app lifetime. Use for caches, config, HTTP clients.
16
+ builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
17
+ ConnectionMultiplexer.Connect(configuration.GetConnectionString("Redis")!));
18
+ ```
19
+
20
+ ### The Captive Dependency Problem
21
+
22
+ A singleton must NEVER depend on a scoped or transient service — it captures a stale instance.
23
+
24
+ ```csharp
25
+ // WRONG: singleton captures scoped DbContext
26
+ public class CachedUserService // Registered as Singleton
27
+ {
28
+ private readonly AppDbContext _db; // Scoped! This instance lives forever now.
29
+ public CachedUserService(AppDbContext db) => _db = db;
30
+ }
31
+
32
+ // RIGHT: use IServiceScopeFactory to create scopes
33
+ public class CachedUserService
34
+ {
35
+ private readonly IServiceScopeFactory _scopeFactory;
36
+
37
+ public CachedUserService(IServiceScopeFactory scopeFactory)
38
+ => _scopeFactory = scopeFactory;
39
+
40
+ public async Task<User?> GetUserAsync(int id)
41
+ {
42
+ using var scope = _scopeFactory.CreateScope();
43
+ var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
44
+ return await db.Users.FindAsync(id);
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## Registration Patterns
50
+
51
+ ```csharp
52
+ // Extension methods for clean composition root
53
+ public static class OrderServiceExtensions
54
+ {
55
+ public static IServiceCollection AddOrderServices(this IServiceCollection services)
56
+ {
57
+ services.AddScoped<IOrderRepository, OrderRepository>();
58
+ services.AddScoped<IOrderService, OrderService>();
59
+ services.AddScoped<IInventoryChecker, InventoryChecker>();
60
+ return services;
61
+ }
62
+ }
63
+
64
+ // Program.cs stays clean
65
+ builder.Services.AddOrderServices();
66
+ builder.Services.AddNotificationServices();
67
+ builder.Services.AddAuthenticationServices(builder.Configuration);
68
+ ```
69
+
70
+ ## Options Pattern
71
+
72
+ ```csharp
73
+ // Strongly typed configuration
74
+ public class SmtpOptions
75
+ {
76
+ public const string SectionName = "Smtp";
77
+
78
+ public required string Host { get; init; }
79
+ public int Port { get; init; } = 587;
80
+ public required string Username { get; init; }
81
+ public required string Password { get; init; }
82
+ public bool UseSsl { get; init; } = true;
83
+ }
84
+
85
+ // Registration with validation
86
+ builder.Services
87
+ .AddOptions<SmtpOptions>()
88
+ .BindConfiguration(SmtpOptions.SectionName)
89
+ .ValidateDataAnnotations()
90
+ .ValidateOnStart();
91
+
92
+ // Injection — use IOptions<T> for static config, IOptionsMonitor<T> for reloadable
93
+ public class EmailSender(IOptions<SmtpOptions> options)
94
+ {
95
+ private readonly SmtpOptions _smtp = options.Value;
96
+ }
97
+ ```
98
+
99
+ ## Keyed Services (.NET 8+)
100
+
101
+ ```csharp
102
+ // Register multiple implementations of the same interface
103
+ builder.Services.AddKeyedSingleton<INotifier, EmailNotifier>("email");
104
+ builder.Services.AddKeyedSingleton<INotifier, SmsNotifier>("sms");
105
+ builder.Services.AddKeyedSingleton<INotifier, SlackNotifier>("slack");
106
+
107
+ // Inject by key
108
+ public class NotificationService([FromKeyedServices("email")] INotifier emailNotifier)
109
+ {
110
+ public Task NotifyAsync(string message)
111
+ => emailNotifier.SendAsync(message);
112
+ }
113
+ ```
114
+
115
+ ## Interface Segregation
116
+
117
+ ```csharp
118
+ // Bad: fat interface
119
+ public interface IUserRepository
120
+ {
121
+ Task<User?> GetByIdAsync(int id, CancellationToken ct);
122
+ Task<User?> GetByEmailAsync(string email, CancellationToken ct);
123
+ Task<IReadOnlyList<User>> GetAllAsync(CancellationToken ct);
124
+ Task CreateAsync(User user, CancellationToken ct);
125
+ Task UpdateAsync(User user, CancellationToken ct);
126
+ Task DeleteAsync(int id, CancellationToken ct);
127
+ Task<int> GetCountAsync(CancellationToken ct);
128
+ Task<bool> ExistsAsync(string email, CancellationToken ct);
129
+ }
130
+
131
+ // Good: segregated interfaces
132
+ public interface IUserReader
133
+ {
134
+ Task<User?> GetByIdAsync(int id, CancellationToken ct);
135
+ Task<User?> GetByEmailAsync(string email, CancellationToken ct);
136
+ }
137
+
138
+ public interface IUserWriter
139
+ {
140
+ Task CreateAsync(User user, CancellationToken ct);
141
+ Task UpdateAsync(User user, CancellationToken ct);
142
+ }
143
+
144
+ // Consumers only depend on what they use
145
+ public class LoginService(IUserReader users) { }
146
+ public class RegistrationService(IUserReader users, IUserWriter writer) { }
147
+ ```
148
+
149
+ ## Decorator Pattern
150
+
151
+ ```csharp
152
+ // Scrutor or manual registration for decorators
153
+ public class CachedUserRepository : IUserReader
154
+ {
155
+ private readonly IUserReader _inner;
156
+ private readonly IMemoryCache _cache;
157
+
158
+ public CachedUserRepository(IUserReader inner, IMemoryCache cache)
159
+ {
160
+ _inner = inner;
161
+ _cache = cache;
162
+ }
163
+
164
+ public async Task<User?> GetByIdAsync(int id, CancellationToken ct)
165
+ {
166
+ return await _cache.GetOrCreateAsync($"user:{id}", async entry =>
167
+ {
168
+ entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
169
+ return await _inner.GetByIdAsync(id, ct);
170
+ });
171
+ }
172
+ }
173
+
174
+ // Registration
175
+ builder.Services.AddScoped<UserRepository>();
176
+ builder.Services.AddScoped<IUserReader>(sp =>
177
+ new CachedUserRepository(
178
+ sp.GetRequiredService<UserRepository>(),
179
+ sp.GetRequiredService<IMemoryCache>()));
180
+ ```
181
+
182
+ ## Anti-Patterns
183
+
184
+ ```csharp
185
+ // Never: service locator pattern
186
+ public class OrderService
187
+ {
188
+ private readonly IServiceProvider _sp;
189
+ public OrderService(IServiceProvider sp) => _sp = sp;
190
+ public void Process()
191
+ {
192
+ var repo = _sp.GetRequiredService<IOrderRepository>(); // Hidden dependency
193
+ }
194
+ }
195
+ // Use constructor injection instead
196
+
197
+ // Never: registering concrete types without interfaces (for testability)
198
+ builder.Services.AddScoped<OrderService>(); // Can't mock in tests
199
+ // Register with interface: AddScoped<IOrderService, OrderService>();
200
+
201
+ // Never: static service accessors
202
+ public static class ServiceLocator
203
+ {
204
+ public static IServiceProvider Provider { get; set; } = null!; // Global mutable state
205
+ }
206
+ ```
@@ -0,0 +1,235 @@
1
+ # C# Error Handling
2
+
3
+ Exceptions for exceptional conditions. Result types for expected failures. Never swallow errors.
4
+
5
+ ## The Two Categories
6
+
7
+ 1. **Exceptions** — Programming errors, infrastructure failures, genuinely unexpected conditions
8
+ 2. **Result patterns** — Validation failures, business rule violations, "not found" scenarios
9
+
10
+ ## Exception Best Practices
11
+
12
+ ```csharp
13
+ // Catch specific exceptions, never bare catch
14
+ try
15
+ {
16
+ await _httpClient.PostAsync(url, content, ct);
17
+ }
18
+ catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.TooManyRequests)
19
+ {
20
+ _logger.LogWarning(ex, "Rate limited by {Url}", url);
21
+ await Task.Delay(retryDelay, ct);
22
+ }
23
+ catch (TaskCanceledException) when (ct.IsCancellationRequested)
24
+ {
25
+ _logger.LogInformation("Request cancelled");
26
+ throw; // Let cancellation propagate
27
+ }
28
+ catch (HttpRequestException ex)
29
+ {
30
+ _logger.LogError(ex, "HTTP request to {Url} failed", url);
31
+ throw; // Re-throw — don't swallow
32
+ }
33
+
34
+ // Exception filters (when clause) — use them for conditional catch
35
+ catch (SqlException ex) when (ex.Number == 2627) // Unique constraint violation
36
+ {
37
+ throw new DuplicateEntityException("User with this email already exists", ex);
38
+ }
39
+ ```
40
+
41
+ ### Guard Clauses
42
+
43
+ ```csharp
44
+ public class OrderService
45
+ {
46
+ public async Task<Order> CreateAsync(CreateOrderRequest request, CancellationToken ct)
47
+ {
48
+ ArgumentNullException.ThrowIfNull(request);
49
+ ArgumentException.ThrowIfNullOrWhiteSpace(request.CustomerId);
50
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(request.Items.Count);
51
+
52
+ // Business logic follows clean preconditions
53
+ var customer = await _customers.GetByIdAsync(request.CustomerId, ct)
54
+ ?? throw new NotFoundException($"Customer '{request.CustomerId}' not found");
55
+
56
+ return Order.Create(customer, request.Items);
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## Result Pattern
62
+
63
+ ```csharp
64
+ // A generic Result type for operations that can fail predictably
65
+ public readonly record struct Result<T>
66
+ {
67
+ public T? Value { get; }
68
+ public Error? Error { get; }
69
+ public bool IsSuccess => Error is null;
70
+
71
+ private Result(T value) { Value = value; Error = null; }
72
+ private Result(Error error) { Value = default; Error = error; }
73
+
74
+ public static Result<T> Success(T value) => new(value);
75
+ public static Result<T> Failure(Error error) => new(error);
76
+
77
+ public TResult Match<TResult>(
78
+ Func<T, TResult> onSuccess,
79
+ Func<Error, TResult> onFailure)
80
+ => IsSuccess ? onSuccess(Value!) : onFailure(Error!);
81
+ }
82
+
83
+ public record Error(string Code, string Message)
84
+ {
85
+ public static Error NotFound(string entity, object id)
86
+ => new("NOT_FOUND", $"{entity} with id '{id}' was not found");
87
+
88
+ public static Error Validation(string message)
89
+ => new("VALIDATION", message);
90
+
91
+ public static Error Conflict(string message)
92
+ => new("CONFLICT", message);
93
+ }
94
+ ```
95
+
96
+ ### Using the Result Pattern
97
+
98
+ ```csharp
99
+ public class UserService
100
+ {
101
+ public async Task<Result<User>> RegisterAsync(
102
+ RegisterRequest request, CancellationToken ct)
103
+ {
104
+ // Validation
105
+ if (!EmailValidator.IsValid(request.Email))
106
+ return Result<User>.Failure(Error.Validation("Invalid email format"));
107
+
108
+ // Business rule check
109
+ var existing = await _users.GetByEmailAsync(request.Email, ct);
110
+ if (existing is not null)
111
+ return Result<User>.Failure(Error.Conflict("Email already registered"));
112
+
113
+ // Happy path
114
+ var user = User.Create(request.Name, request.Email);
115
+ await _users.CreateAsync(user, ct);
116
+ return Result<User>.Success(user);
117
+ }
118
+ }
119
+
120
+ // In the endpoint
121
+ app.MapPost("/users", async (RegisterRequest request, UserService service, CancellationToken ct) =>
122
+ {
123
+ var result = await service.RegisterAsync(request, ct);
124
+ return result.Match(
125
+ user => Results.Created($"/users/{user.Id}", user),
126
+ error => error.Code switch
127
+ {
128
+ "VALIDATION" => Results.BadRequest(error),
129
+ "CONFLICT" => Results.Conflict(error),
130
+ _ => Results.Problem(error.Message)
131
+ });
132
+ });
133
+ ```
134
+
135
+ ## Problem Details (RFC 9457)
136
+
137
+ ```csharp
138
+ // ASP.NET Core's built-in Problem Details support
139
+ builder.Services.AddProblemDetails(options =>
140
+ {
141
+ options.CustomizeProblemDetails = context =>
142
+ {
143
+ context.ProblemDetails.Extensions["traceId"] =
144
+ context.HttpContext.TraceIdentifier;
145
+ };
146
+ });
147
+
148
+ // Global exception handler middleware
149
+ app.UseExceptionHandler(exceptionApp =>
150
+ {
151
+ exceptionApp.Run(async context =>
152
+ {
153
+ var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
154
+ var problemDetails = exception switch
155
+ {
156
+ NotFoundException ex => new ProblemDetails
157
+ {
158
+ Status = 404,
159
+ Title = "Not Found",
160
+ Detail = ex.Message
161
+ },
162
+ ValidationException ex => new ProblemDetails
163
+ {
164
+ Status = 400,
165
+ Title = "Validation Error",
166
+ Detail = ex.Message
167
+ },
168
+ _ => new ProblemDetails
169
+ {
170
+ Status = 500,
171
+ Title = "Internal Server Error",
172
+ Detail = "An unexpected error occurred"
173
+ }
174
+ };
175
+
176
+ context.Response.StatusCode = problemDetails.Status ?? 500;
177
+ await context.Response.WriteAsJsonAsync(problemDetails);
178
+ });
179
+ });
180
+ ```
181
+
182
+ ## FluentValidation
183
+
184
+ ```csharp
185
+ public class CreateOrderValidator : AbstractValidator<CreateOrderRequest>
186
+ {
187
+ public CreateOrderValidator()
188
+ {
189
+ RuleFor(x => x.CustomerId)
190
+ .NotEmpty()
191
+ .WithMessage("Customer ID is required");
192
+
193
+ RuleFor(x => x.Items)
194
+ .NotEmpty()
195
+ .WithMessage("Order must contain at least one item");
196
+
197
+ RuleForEach(x => x.Items).ChildRules(item =>
198
+ {
199
+ item.RuleFor(i => i.Quantity)
200
+ .GreaterThan(0)
201
+ .WithMessage("Quantity must be positive");
202
+
203
+ item.RuleFor(i => i.Price)
204
+ .GreaterThan(0)
205
+ .WithMessage("Price must be positive");
206
+ });
207
+ }
208
+ }
209
+ ```
210
+
211
+ ## Anti-Patterns
212
+
213
+ ```csharp
214
+ // Never: catch-all that swallows exceptions
215
+ try { DoWork(); }
216
+ catch (Exception) { } // Silent failure — a bug hiding a bug
217
+
218
+ // Never: catch and throw new (loses stack trace)
219
+ catch (Exception ex) { throw new Exception("Failed", ex); }
220
+ // Use: throw; (preserves stack trace)
221
+ // Or: throw new SpecificException("context", ex); (wraps with context)
222
+
223
+ // Never: exceptions for control flow
224
+ try { return dict[key]; }
225
+ catch (KeyNotFoundException) { return default; }
226
+ // Use: dict.TryGetValue(key, out var value)
227
+
228
+ // Never: log and throw (produces duplicate log entries)
229
+ catch (Exception ex)
230
+ {
231
+ _logger.LogError(ex, "Failed");
232
+ throw; // Now it's logged twice — here and in global handler
233
+ }
234
+ // Choose one: log OR throw, not both
235
+ ```
@@ -0,0 +1,204 @@
1
+ # Modern C# Language Features
2
+
3
+ Deep knowledge of C# language evolution. Use modern features deliberately, not just because they exist.
4
+
5
+ ## Nullable Reference Types
6
+
7
+ The single most impactful feature for code correctness. Non-negotiable.
8
+
9
+ ```csharp
10
+ // The compiler is your partner — listen to it
11
+ public class UserService
12
+ {
13
+ // Non-nullable: guaranteed to have a value
14
+ private readonly IUserRepository _repository;
15
+
16
+ // Nullable: explicitly communicates "might not exist"
17
+ public async Task<User?> FindByEmailAsync(string email)
18
+ {
19
+ ArgumentException.ThrowIfNullOrWhiteSpace(email);
20
+ return await _repository.FindByEmailAsync(email);
21
+ }
22
+
23
+ // Null-forgiving operator (!) — document WHY
24
+ // Only when you genuinely know better than the compiler
25
+ var user = users.FirstOrDefault(u => u.Id == id)!; // Validated above
26
+ }
27
+ ```
28
+
29
+ ### Rules
30
+
31
+ - Never disable nullable warnings project-wide
32
+ - `string` means non-null. `string?` means nullable. Respect the distinction
33
+ - Use `ArgumentNullException.ThrowIfNull()` at public API boundaries
34
+ - Avoid the null-forgiving operator (`!`) — if you need it, the design may be wrong
35
+
36
+ ## Records
37
+
38
+ ```csharp
39
+ // Immutable data with value semantics — the default for DTOs and value objects
40
+ public record CreateUserRequest(string Name, string Email);
41
+
42
+ // Record structs for high-performance value types
43
+ public readonly record struct Coordinate(double Latitude, double Longitude);
44
+
45
+ // Records with validation
46
+ public record Money
47
+ {
48
+ public decimal Amount { get; }
49
+ public string Currency { get; }
50
+
51
+ public Money(decimal amount, string currency)
52
+ {
53
+ ArgumentOutOfRangeException.ThrowIfNegative(amount);
54
+ ArgumentException.ThrowIfNullOrWhiteSpace(currency);
55
+ Amount = amount;
56
+ Currency = currency.ToUpperInvariant();
57
+ }
58
+ }
59
+
60
+ // Non-destructive mutation
61
+ var updated = original with { Email = "new@example.com" };
62
+ ```
63
+
64
+ ## Pattern Matching
65
+
66
+ ```csharp
67
+ // Switch expressions — exhaustive, concise
68
+ public static string FormatStatus(OrderStatus status) => status switch
69
+ {
70
+ OrderStatus.Pending => "Awaiting processing",
71
+ OrderStatus.Shipped => "On its way",
72
+ OrderStatus.Delivered => "Delivered",
73
+ OrderStatus.Cancelled => "Cancelled",
74
+ _ => throw new ArgumentOutOfRangeException(nameof(status))
75
+ };
76
+
77
+ // Property patterns
78
+ public static decimal CalculateDiscount(Order order) => order switch
79
+ {
80
+ { Total: > 1000, Customer.IsPremium: true } => order.Total * 0.15m,
81
+ { Total: > 500 } => order.Total * 0.10m,
82
+ { Customer.IsPremium: true } => order.Total * 0.05m,
83
+ _ => 0m
84
+ };
85
+
86
+ // Type patterns with guards
87
+ public static string Describe(object value) => value switch
88
+ {
89
+ int n when n < 0 => "negative integer",
90
+ int n => $"positive integer: {n}",
91
+ string { Length: 0 } => "empty string",
92
+ string s => $"string: {s}",
93
+ null => "null",
94
+ _ => $"unknown: {value.GetType().Name}"
95
+ };
96
+
97
+ // List patterns (C# 11+)
98
+ public static bool IsValidSequence(int[] values) => values switch
99
+ {
100
+ [1, 2, 3] => true,
101
+ [1, .., 3] => true, // Starts with 1, ends with 3
102
+ [_, _, ..] => true, // At least 2 elements
103
+ _ => false
104
+ };
105
+ ```
106
+
107
+ ## LINQ — Use It Well
108
+
109
+ ```csharp
110
+ // Good: clear, composable, declarative
111
+ var activeUsers = users
112
+ .Where(u => u.IsActive)
113
+ .OrderBy(u => u.LastLogin)
114
+ .Select(u => new UserSummary(u.Id, u.Name, u.Email))
115
+ .ToList();
116
+
117
+ // Bad: LINQ for side effects
118
+ users.ForEach(u => u.Deactivate()); // Use a foreach loop instead
119
+
120
+ // Bad: multiple enumerations of IEnumerable
121
+ var count = users.Count(); // Enumerates
122
+ var first = users.FirstOrDefault(); // Enumerates again!
123
+ // Fix: materialize first with .ToList()
124
+
125
+ // Performance: use the right method
126
+ users.Any(u => u.IsAdmin) // Good: short-circuits
127
+ users.Count(u => u.IsAdmin) > 0 // Bad: counts everything
128
+ users.Where(u => u.IsAdmin).Any() // Acceptable but unnecessary
129
+ ```
130
+
131
+ ## Spans and Memory
132
+
133
+ ```csharp
134
+ // Span<T> for zero-allocation slicing
135
+ public static bool StartsWithDigit(ReadOnlySpan<char> input)
136
+ => !input.IsEmpty && char.IsDigit(input[0]);
137
+
138
+ // String parsing without allocation
139
+ public static (string Key, string Value) ParseHeader(ReadOnlySpan<char> header)
140
+ {
141
+ var separatorIndex = header.IndexOf(':');
142
+ if (separatorIndex < 0)
143
+ throw new FormatException("Invalid header format");
144
+
145
+ var key = header[..separatorIndex].Trim().ToString();
146
+ var value = header[(separatorIndex + 1)..].Trim().ToString();
147
+ return (key, value);
148
+ }
149
+
150
+ // stackalloc for small buffers
151
+ Span<byte> buffer = stackalloc byte[256];
152
+ var bytesWritten = Encoding.UTF8.GetBytes(input, buffer);
153
+ ```
154
+
155
+ ## Collection Expressions (C# 12+)
156
+
157
+ ```csharp
158
+ // Concise collection initialization
159
+ int[] numbers = [1, 2, 3, 4, 5];
160
+ List<string> names = ["Alice", "Bob"];
161
+ Dictionary<string, int> scores = new() { ["Alice"] = 95, ["Bob"] = 87 };
162
+
163
+ // Spread operator
164
+ int[] combined = [..firstHalf, ..secondHalf];
165
+ ```
166
+
167
+ ## Primary Constructors (C# 12+)
168
+
169
+ ```csharp
170
+ // For services — captures parameters as fields
171
+ public class OrderService(IOrderRepository repository, ILogger<OrderService> logger)
172
+ {
173
+ public async Task<Order> GetByIdAsync(int id)
174
+ {
175
+ logger.LogInformation("Fetching order {OrderId}", id);
176
+ return await repository.GetByIdAsync(id)
177
+ ?? throw new NotFoundException($"Order {id} not found");
178
+ }
179
+ }
180
+
181
+ // Caution: primary constructor parameters are mutable and capturable
182
+ // For DTOs, prefer records instead
183
+ ```
184
+
185
+ ## Anti-Patterns
186
+
187
+ ```csharp
188
+ // Never: stringly-typed code
189
+ void Process(string type, string data) { } // What types? What format?
190
+ // Use enums, records, or discriminated unions
191
+
192
+ // Never: throwing exceptions for control flow
193
+ try { return users.First(u => u.Id == id); }
194
+ catch (InvalidOperationException) { return null; }
195
+ // Use FirstOrDefault or TryGetValue patterns
196
+
197
+ // Never: mutable static state
198
+ static List<User> _cache = new(); // Thread-unsafe, untestable
199
+ // Use IMemoryCache or a proper caching abstraction
200
+
201
+ // Never: deep inheritance hierarchies
202
+ class SpecialPremiumInternationalCustomerOrder : PremiumCustomerOrder { }
203
+ // Use composition and interfaces
204
+ ```