Guest User

Modern .NET Rule Generation Meta-Rule

a guest
Apr 14th, 2025
270
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.21 KB | None | 0 0
  1. ---
  2. description:
  3. globs:
  4. alwaysApply: true
  5. ---
  6. ---
  7. title: Modern .NET Rule Generation Meta-Rule
  8. description: Instructions for creating effective C# Project Rules that encode modern .NET, functional programming principles, and Vertical Slice Architecture patterns.
  9. glob: "**/*.{mdc}"
  10. alwaysApply: true
  11. ---
  12.  
  13. # Modern .NET Rule Generation Meta-Rule
  14.  
  15. This meta-rule provides comprehensive guidance on creating effective C# Project Rules that codify modern .NET development practices, functional programming principles, and Vertical Slice Architecture (VSA). Use this meta-rule when you want to create specific rules based on your existing codebase and prior conversations.
  16.  
  17. ## Creating Effective C# Project Rules
  18.  
  19. Rules should be stored in `.cursor/rules/` with `.mdc` extensions. Each rule should focus on a specific aspect of your codebase architecture, conventions, or patterns. This meta-rule will help you create rules that are understood by both Cursor AI and human team members.
  20.  
  21. ### Rule Categories to Consider
  22.  
  23. When generating C# project rules, consider creating specialized rules for:
  24.  
  25. * **Project Structure**: How code is organized at the folder and project level
  26. * **Architectural Patterns**: How components and services interact
  27. * **Coding Style**: Formatting, naming, and structural conventions
  28. * **Functional Patterns**: How to implement functional programming patterns in C#
  29. * **Testing Approach**: How tests should be structured and implemented
  30.  
  31. ## Rule File Structure
  32.  
  33. A well-structured rule file should include these key components:
  34.  
  35. ### 1. YAML Frontmatter (Required)
  36.  
  37. Start every rule with proper YAML frontmatter:
  38.  
  39. ```yaml
  40. ---
  41. title: Clear Title of Rule (e.g., C# Functional Programming Guidelines)
  42. description: Guidelines for [what this rule covers, e.g., implementing functional patterns in C#]
  43. glob: "[pattern to match relevant files, e.g., src/**/*.cs]"
  44. alwaysApply: false # Set to true for architectural rules that should always apply
  45. ---
  46. ```
  47.  
  48. * **`title`**: Concise title (5-7 words recommended)
  49. * **`description`**: Clear explanation starting with "Guidelines for..." or "Instructions on..."
  50. * **`glob`**: File pattern(s) that activate this rule. Examples:
  51. * `src/Features/**/*.cs` (All feature code)
  52. * `src/Features/**/Commands/**/*.cs` (Command handlers)
  53. * `src/Features/**/Queries/**/*.cs` (Query handlers)
  54. * `tests/**/*.cs` (Test files)
  55. * `*.csproj` (Project files)
  56. * **`alwaysApply`**: Set to `true` for foundational architecture rules
  57.  
  58. ### 2. Content Sections
  59.  
  60. Structure rule content with these recommended sections:
  61.  
  62. #### Introduction / Problem Statement
  63.  
  64. * Explain what problem this rule solves or what pattern it establishes
  65. * Clarify why this pattern is important for modern .NET development
  66. * Specify when the rule should be applied
  67.  
  68. ```markdown
  69. ## Introduction
  70.  
  71. This rule establishes our approach to functional programming in C#, emphasizing immutability, pure functions, and explicit side-effect management. These patterns improve code predictability, testability, and maintainability while reducing runtime errors.
  72. ```
  73.  
  74. #### Pattern Description
  75.  
  76. * Clearly document the recommended pattern with code examples
  77. * Use C# code blocks to illustrate proper implementation
  78. * Explain key concepts and components
  79.  
  80. ```markdown
  81. ## Pattern Description
  82.  
  83. Our code emphasizes functional programming principles adapted to C#:
  84.  
  85. ```csharp
  86. // Pure functions with explicit dependencies
  87. public static Result<OrderSummary> CalculateOrderTotal(
  88. IEnumerable<OrderLine> orderLines,
  89. ITaxCalculator taxCalculator,
  90. IDiscountPolicy discountPolicy)
  91. {
  92. // Validation
  93. if (orderLines is null || !orderLines.Any())
  94. return Result.Failure<OrderSummary>("Order must contain at least one item");
  95.  
  96. // Pure calculation
  97. var subtotal = orderLines.Sum(line => line.Quantity * line.UnitPrice);
  98. var discount = discountPolicy.CalculateDiscount(orderLines);
  99. var preTaxTotal = subtotal - discount;
  100. var tax = taxCalculator.CalculateTax(preTaxTotal);
  101.  
  102. // Return immutable result
  103. return Result.Success(new OrderSummary(
  104. SubTotal: subtotal,
  105. Discount: discount,
  106. Tax: tax,
  107. Total: preTaxTotal + tax
  108. ));
  109. }
  110. ```
  111. ```
  112.  
  113. #### Implementation Steps
  114.  
  115. * Provide step-by-step guidance for implementing the pattern
  116. * Identify key decision points and variations
  117. * Use numbered lists for clarity
  118.  
  119. ```markdown
  120. ## Implementation Steps
  121.  
  122. 1. Define immutable data structures using records
  123. 2. Implement business logic as pure functions
  124. 3. Make dependencies explicit through parameters
  125. 4. Return new instances rather than mutating state
  126. 5. Use pattern matching for conditional logic
  127. 6. Isolate side effects (I/O, database, etc.) in dedicated services
  128. 7. Use static extension methods for utility functions
  129. ```
  130.  
  131. #### Real-World Examples
  132.  
  133. * Link to actual files in your codebase
  134. * Explain why these examples embody the rule well
  135. * Focus on examples that clearly demonstrate the pattern
  136.  
  137. ```markdown
  138. ## Real-World Examples
  139.  
  140. * [OrderProcessing Handler](mdc:../src/Features/Orders/Commands/ProcessOrder/ProcessOrderHandler.cs) - Demonstrates pure functions with explicit dependencies
  141. * [Customer Record](mdc:../src/Features/Customers/Models/Customer.cs) - Shows immutable domain models using records
  142. * [Tax Calculator](mdc:../src/Shared/Services/TaxCalculator.cs) - Example of isolated side effects
  143. ```
  144.  
  145. #### Common Pitfalls / Anti-Patterns
  146.  
  147. * List common mistakes related to this rule
  148. * Explain how to recognize these issues
  149. * Suggest corrections or alternatives
  150.  
  151. ```markdown
  152. ## Common Pitfalls
  153.  
  154. * Using mutable classes instead of immutable records
  155. * Mixing business logic with side effects (database calls, logging)
  156. * Hidden dependencies through static classes or singletons
  157. * Excessive inheritance hierarchies instead of composition
  158. * Directly accessing global state instead of passing dependencies
  159. ```
  160.  
  161. ## C# Specific Rule Examples
  162.  
  163. ### Project Structure (VSA) Rule
  164.  
  165. ```markdown
  166. ---
  167. title: Vertical Slice Architecture Structure
  168. description: Guidelines for organizing code using Vertical Slice Architecture in .NET projects
  169. glob: "**/*.{cs,csproj}"
  170. alwaysApply: true
  171. ---
  172.  
  173. @file ../Directory.Build.props
  174. @file ../README.md
  175.  
  176. # Vertical Slice Architecture Structure
  177.  
  178. ## Introduction
  179.  
  180. This project uses Vertical Slice Architecture (VSA) to organize code by feature rather than by technical layer. This improves cohesion, reduces coupling between features, and makes navigation easier.
  181.  
  182. ## Pattern Description
  183.  
  184. Our project structure follows these conventions:
  185.  
  186. ```csharp
  187. /src
  188. /Features // Main container for all features
  189. /[FeatureName] // e.g., UserManagement, Orders, etc.
  190. /Commands // Write operations
  191. /[CommandName] // One folder per command
  192. [Command].cs // Command definition (immutable record)
  193. [Handler].cs // Command handler (pure function)
  194. [Validator].cs // Validation logic (if needed)
  195. /Queries // Read operations
  196. /[QueryName] // One folder per query
  197. [Query].cs // Query definition (immutable record)
  198. [Handler].cs // Query handler (pure function)
  199. /Models // Feature-specific domain models
  200. /Events // Domain events for this feature
  201. /Extensions // Feature-specific extensions
  202. /Shared // Cross-cutting concerns
  203. /Infrastructure // Technical services
  204. /Extensions // Common extension methods
  205. /Models // Shared models
  206.  
  207. /tests // Test projects
  208. /Features // Mirror the structure in src
  209. /[FeatureName]Tests // Tests for specific features
  210. /Integration // Integration tests
  211. /Performance // Performance tests
  212. ```
  213.  
  214. ## Implementation Steps
  215.  
  216. 1. Organize code by business feature rather than technical concern
  217. 2. Keep feature folders isolated and autonomous
  218. 3. Place interfaces and implementations together
  219. 4. Use clear naming that reflects domain concepts
  220. 5. Minimize cross-feature dependencies
  221. 6. Use message-based communication between features
  222.  
  223. ## Real-World Examples
  224.  
  225. * [User Management Feature](mdc:../src/Features/UserManagement)
  226. * [Order Processing Feature](mdc:../src/Features/Orders)
  227.  
  228. ## Common Pitfalls
  229.  
  230. * Creating traditional layers (Controllers, Services, Repositories) that cut across features
  231. * Sharing domain models between unrelated features
  232. * Direct dependencies between features (rather than using messaging)
  233. * Placing business logic in controllers or repositories
  234. * Circular dependencies between features
  235. ```
  236.  
  237. ### Functional Programming Rule
  238.  
  239. ```markdown
  240. ---
  241. title: Functional C# Coding Patterns
  242. description: Guidelines for applying functional programming principles in C# with modern language features
  243. glob: "src/**/*.cs"
  244. alwaysApply: false
  245. ---
  246.  
  247. # Functional C# Coding Patterns
  248.  
  249. ## Introduction
  250.  
  251. This project favors functional programming principles to improve code clarity, testing, and reduce bugs related to mutable state. We leverage modern C# features (C# ≥13) to implement functional patterns while maintaining performance.
  252.  
  253. ## Pattern Description
  254.  
  255. ### Immutable Data Structures
  256.  
  257. Use records for domain models and DTOs:
  258.  
  259. ```csharp
  260. // GOOD: Immutable record
  261. public record OrderItem(string ProductId, int Quantity, decimal UnitPrice)
  262. {
  263. // Derived property
  264. public decimal TotalPrice => Quantity * UnitPrice;
  265.  
  266. // Non-mutating transformation
  267. public OrderItem WithQuantity(int newQuantity) =>
  268. this with { Quantity = newQuantity };
  269. }
  270.  
  271. // GOOD: Record with validation
  272. public record CreateUserCommand
  273. {
  274. public string Username { get; init; }
  275. public string Email { get; init; }
  276.  
  277. // Validate at construction time
  278. public CreateUserCommand(string username, string email)
  279. {
  280. if (string.IsNullOrEmpty(username))
  281. throw new ArgumentException("Username cannot be empty", nameof(username));
  282.  
  283. Username = username;
  284. Email = email;
  285. }
  286. }
  287. ```
  288.  
  289. ### Pure Functions
  290.  
  291. Methods should be static and side-effect free:
  292.  
  293. ```csharp
  294. // GOOD: Pure function with explicit dependencies
  295. public static class TaxCalculator
  296. {
  297. public static decimal CalculateTax(
  298. decimal amount,
  299. decimal taxRate,
  300. TaxRoundingMode roundingMode)
  301. {
  302. var tax = amount * taxRate;
  303. return roundingMode switch
  304. {
  305. TaxRoundingMode.Round => Math.Round(tax, 2),
  306. TaxRoundingMode.Ceiling => Math.Ceiling(tax * 100) / 100,
  307. TaxRoundingMode.Floor => Math.Floor(tax * 100) / 100,
  308. _ => throw new ArgumentOutOfRangeException(nameof(roundingMode))
  309. };
  310. }
  311. }
  312. ```
  313.  
  314. ### Patterns for Managing Side Effects
  315.  
  316. Use Result/Either pattern for error handling:
  317.  
  318. ```csharp
  319. // GOOD: Return type indicates success/failure
  320. public static Result<User> CreateUser(string username, string email)
  321. {
  322. // Validation
  323. if (string.IsNullOrEmpty(username))
  324. return Result.Failure<User>("Username cannot be empty");
  325.  
  326. if (!IsValidEmail(email))
  327. return Result.Failure<User>("Invalid email format");
  328.  
  329. // Create user (pure transformation)
  330. var user = new User(Guid.NewGuid(), username, email);
  331.  
  332. return Result.Success(user);
  333. }
  334. ```
  335.  
  336. ## Implementation Steps
  337.  
  338. 1. Define domain models as immutable records
  339. 2. Implement business logic as static methods with explicit dependencies
  340. 3. Use Result/Either pattern for error handling instead of exceptions
  341. 4. Separate pure computation from side effects
  342. 5. Use LINQ for collection transformations
  343. 6. Apply pattern matching for conditional logic
  344. 7. Use high-performance features (Span<T>, stackalloc) where needed
  345.  
  346. ## Real-World Examples
  347.  
  348. * [Order Processing](mdc:../src/Features/Orders/Commands/ProcessOrder/ProcessOrderHandler.cs)
  349. * [User Management](mdc:../src/Features/Users/Queries/GetUserDetails/GetUserDetailsHandler.cs)
  350.  
  351. ## Common Pitfalls
  352.  
  353. * Mixing business logic with side effects
  354. * Using mutable collections when immutable ones would work
  355. * Not making methods static when possible
  356. * Using inheritance hierarchies instead of composition
  357. * Relying on exceptions for normal control flow
  358. * Using null instead of Option/Maybe pattern
  359. ```
  360.  
  361. ### Performance Optimization Rule
  362.  
  363. ```markdown
  364. ---
  365. title: .NET Performance Optimization Patterns
  366. description: Guidelines for implementing high-performance C# code with modern language features
  367. glob: "src/**/*.cs"
  368. alwaysApply: false
  369. ---
  370.  
  371. # .NET Performance Optimization Patterns
  372.  
  373. ## Introduction
  374.  
  375. This project prioritizes both functional programming principles and performance. This rule defines patterns for high-performance code using modern C# features while maintaining readability and functional approaches.
  376.  
  377. ## Pattern Description
  378.  
  379. ### Memory-Efficient Operations
  380.  
  381. Use `Span<T>` and `Memory<T>` for memory-efficient operations:
  382.  
  383. ```csharp
  384. // GOOD: Memory-efficient string parsing
  385. public static bool TryParseId(ReadOnlySpan<char> input, out Guid id)
  386. {
  387. id = default;
  388.  
  389. // Early validation without allocations
  390. if (input.Length != 36)
  391. return false;
  392.  
  393. return Guid.TryParseExact(input, "D", out id);
  394. }
  395.  
  396. // GOOD: Avoiding string allocations
  397. public static bool IsValidProductCode(ReadOnlySpan<char> code)
  398. {
  399. if (code.Length != 10)
  400. return false;
  401.  
  402. // Prefix check without allocations
  403. return code.StartsWith("PRD-".AsSpan()) &&
  404. char.IsDigit(code[4]) &&
  405. char.IsDigit(code[5]);
  406. }
  407. ```
  408.  
  409. ### High-Performance Data Structures
  410.  
  411. Use structs for small, frequently-used data types:
  412.  
  413. ```csharp
  414. // GOOD: Struct for small data that's used frequently
  415. public readonly struct Money
  416. {
  417. public decimal Amount { get; }
  418. public string Currency { get; }
  419.  
  420. public Money(decimal amount, string currency)
  421. {
  422. Amount = amount;
  423. Currency = currency ?? throw new ArgumentNullException(nameof(currency));
  424. }
  425.  
  426. // Pure operations
  427. public static Money Add(Money a, Money b)
  428. {
  429. if (a.Currency != b.Currency)
  430. throw new InvalidOperationException("Cannot add different currencies");
  431.  
  432. return new Money(a.Amount + b.Amount, a.Currency);
  433. }
  434.  
  435. // Zero allocation equality
  436. public bool Equals(Money other) =>
  437. Amount == other.Amount && Currency == other.Currency;
  438. }
  439. ```
  440.  
  441. ### Stack Allocation
  442.  
  443. Use stackalloc for small, short-lived buffers:
  444.  
  445. ```csharp
  446. // GOOD: Stack allocation for small buffers
  447. public static string FormatOrderNumber(int orderId)
  448. {
  449. Span<char> buffer = stackalloc char[12]; // No heap allocation
  450.  
  451. "ORD-".AsSpan().CopyTo(buffer);
  452. orderId.TryFormat(buffer.Slice(4), out int charsWritten, "D8");
  453.  
  454. return new string(buffer.Slice(0, 4 + charsWritten));
  455. }
  456. ```
  457.  
  458. ## Implementation Steps
  459.  
  460. 1. Identify performance-critical paths in your application
  461. 2. Use `Span<T>` and `Memory<T>` for string and array operations
  462. 3. Consider `struct` for small, frequently-used data types
  463. 4. Use `stackalloc` for small, short-lived buffers
  464. 5. Apply proper benchmarking to validate improvements
  465. 6. Document performance-focused code with comments
  466. 7. Maintain functional principles while optimizing (pure functions, immutability)
  467.  
  468. ## Real-World Examples
  469.  
  470. * [High-Performance Parser](mdc:../src/Shared/Utils/CsvParser.cs)
  471. * [Money Value Object](mdc:../src/Shared/Models/Money.cs)
  472.  
  473. ## Common Pitfalls
  474.  
  475. * Premature optimization before identifying bottlenecks
  476. * Using `Span<T>` or `stackalloc` unnecessarily for non-performance-critical code
  477. * Creating structs that are too large (should generally be <16 bytes)
  478. * Returning `Span<T>` from methods (use `Memory<T>` instead)
  479. * Not considering the memory safety implications of Span
  480. * Sacrificing readability for minor performance gains
  481. ```
  482.  
  483. ## How to Generate Rules
  484.  
  485. When using this meta-rule to generate specific rules for your project:
  486.  
  487. 1. **Analyze Existing Code**: Review your codebase for patterns and conventions
  488. 2. **Identify Core Principles**: Focus on your key architecture and style preferences
  489. 3. **Create Separate Rules**: Generate individual rules for different aspects (structure, style, etc.)
  490. 4. **Include Real Examples**: Reference actual files from your project where possible
  491. 5. **Test Rule Effectiveness**: Validate that Cursor correctly follows your rules
  492.  
  493. ## Best Practices for C# Rules
  494.  
  495. * **Be Specific to Your Project**: Customize rules to your specific architecture and patterns
  496. * **Use Modern C# Examples**: Include code samples using C# 13+ and .NET 9+ features
  497. * **Emphasize Functional Patterns**: Highlight immutability, pure functions, and side effect management
  498. * **Balance Performance and Readability**: Explain when to use performance-focused features
  499. * **Start Simple**: Begin with core architectural rules and expand as needed
  500. * **Update Regularly**: Refine rules as your codebase evolves
  501.  
  502. Remember that project rules should encode your team's specific conventions, not just general C# best practices. Use examples from your actual codebase to make rules more concrete and applicable.
Advertisement
Add Comment
Please, Sign In to add comment