Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ---
- description:
- globs:
- alwaysApply: true
- ---
- ---
- title: Modern .NET Rule Generation Meta-Rule
- description: Instructions for creating effective C# Project Rules that encode modern .NET, functional programming principles, and Vertical Slice Architecture patterns.
- glob: "**/*.{mdc}"
- alwaysApply: true
- ---
- # Modern .NET Rule Generation Meta-Rule
- 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.
- ## Creating Effective C# Project Rules
- 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.
- ### Rule Categories to Consider
- When generating C# project rules, consider creating specialized rules for:
- * **Project Structure**: How code is organized at the folder and project level
- * **Architectural Patterns**: How components and services interact
- * **Coding Style**: Formatting, naming, and structural conventions
- * **Functional Patterns**: How to implement functional programming patterns in C#
- * **Testing Approach**: How tests should be structured and implemented
- ## Rule File Structure
- A well-structured rule file should include these key components:
- ### 1. YAML Frontmatter (Required)
- Start every rule with proper YAML frontmatter:
- ```yaml
- ---
- title: Clear Title of Rule (e.g., C# Functional Programming Guidelines)
- description: Guidelines for [what this rule covers, e.g., implementing functional patterns in C#]
- glob: "[pattern to match relevant files, e.g., src/**/*.cs]"
- alwaysApply: false # Set to true for architectural rules that should always apply
- ---
- ```
- * **`title`**: Concise title (5-7 words recommended)
- * **`description`**: Clear explanation starting with "Guidelines for..." or "Instructions on..."
- * **`glob`**: File pattern(s) that activate this rule. Examples:
- * `src/Features/**/*.cs` (All feature code)
- * `src/Features/**/Commands/**/*.cs` (Command handlers)
- * `src/Features/**/Queries/**/*.cs` (Query handlers)
- * `tests/**/*.cs` (Test files)
- * `*.csproj` (Project files)
- * **`alwaysApply`**: Set to `true` for foundational architecture rules
- ### 2. Content Sections
- Structure rule content with these recommended sections:
- #### Introduction / Problem Statement
- * Explain what problem this rule solves or what pattern it establishes
- * Clarify why this pattern is important for modern .NET development
- * Specify when the rule should be applied
- ```markdown
- ## Introduction
- 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.
- ```
- #### Pattern Description
- * Clearly document the recommended pattern with code examples
- * Use C# code blocks to illustrate proper implementation
- * Explain key concepts and components
- ```markdown
- ## Pattern Description
- Our code emphasizes functional programming principles adapted to C#:
- ```csharp
- // Pure functions with explicit dependencies
- public static Result<OrderSummary> CalculateOrderTotal(
- IEnumerable<OrderLine> orderLines,
- ITaxCalculator taxCalculator,
- IDiscountPolicy discountPolicy)
- {
- // Validation
- if (orderLines is null || !orderLines.Any())
- return Result.Failure<OrderSummary>("Order must contain at least one item");
- // Pure calculation
- var subtotal = orderLines.Sum(line => line.Quantity * line.UnitPrice);
- var discount = discountPolicy.CalculateDiscount(orderLines);
- var preTaxTotal = subtotal - discount;
- var tax = taxCalculator.CalculateTax(preTaxTotal);
- // Return immutable result
- return Result.Success(new OrderSummary(
- SubTotal: subtotal,
- Discount: discount,
- Tax: tax,
- Total: preTaxTotal + tax
- ));
- }
- ```
- ```
- #### Implementation Steps
- * Provide step-by-step guidance for implementing the pattern
- * Identify key decision points and variations
- * Use numbered lists for clarity
- ```markdown
- ## Implementation Steps
- 1. Define immutable data structures using records
- 2. Implement business logic as pure functions
- 3. Make dependencies explicit through parameters
- 4. Return new instances rather than mutating state
- 5. Use pattern matching for conditional logic
- 6. Isolate side effects (I/O, database, etc.) in dedicated services
- 7. Use static extension methods for utility functions
- ```
- #### Real-World Examples
- * Link to actual files in your codebase
- * Explain why these examples embody the rule well
- * Focus on examples that clearly demonstrate the pattern
- ```markdown
- ## Real-World Examples
- * [OrderProcessing Handler](mdc:../src/Features/Orders/Commands/ProcessOrder/ProcessOrderHandler.cs) - Demonstrates pure functions with explicit dependencies
- * [Customer Record](mdc:../src/Features/Customers/Models/Customer.cs) - Shows immutable domain models using records
- * [Tax Calculator](mdc:../src/Shared/Services/TaxCalculator.cs) - Example of isolated side effects
- ```
- #### Common Pitfalls / Anti-Patterns
- * List common mistakes related to this rule
- * Explain how to recognize these issues
- * Suggest corrections or alternatives
- ```markdown
- ## Common Pitfalls
- * Using mutable classes instead of immutable records
- * Mixing business logic with side effects (database calls, logging)
- * Hidden dependencies through static classes or singletons
- * Excessive inheritance hierarchies instead of composition
- * Directly accessing global state instead of passing dependencies
- ```
- ## C# Specific Rule Examples
- ### Project Structure (VSA) Rule
- ```markdown
- ---
- title: Vertical Slice Architecture Structure
- description: Guidelines for organizing code using Vertical Slice Architecture in .NET projects
- glob: "**/*.{cs,csproj}"
- alwaysApply: true
- ---
- @file ../Directory.Build.props
- @file ../README.md
- # Vertical Slice Architecture Structure
- ## Introduction
- 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.
- ## Pattern Description
- Our project structure follows these conventions:
- ```csharp
- /src
- /Features // Main container for all features
- /[FeatureName] // e.g., UserManagement, Orders, etc.
- /Commands // Write operations
- /[CommandName] // One folder per command
- [Command].cs // Command definition (immutable record)
- [Handler].cs // Command handler (pure function)
- [Validator].cs // Validation logic (if needed)
- /Queries // Read operations
- /[QueryName] // One folder per query
- [Query].cs // Query definition (immutable record)
- [Handler].cs // Query handler (pure function)
- /Models // Feature-specific domain models
- /Events // Domain events for this feature
- /Extensions // Feature-specific extensions
- /Shared // Cross-cutting concerns
- /Infrastructure // Technical services
- /Extensions // Common extension methods
- /Models // Shared models
- /tests // Test projects
- /Features // Mirror the structure in src
- /[FeatureName]Tests // Tests for specific features
- /Integration // Integration tests
- /Performance // Performance tests
- ```
- ## Implementation Steps
- 1. Organize code by business feature rather than technical concern
- 2. Keep feature folders isolated and autonomous
- 3. Place interfaces and implementations together
- 4. Use clear naming that reflects domain concepts
- 5. Minimize cross-feature dependencies
- 6. Use message-based communication between features
- ## Real-World Examples
- * [User Management Feature](mdc:../src/Features/UserManagement)
- * [Order Processing Feature](mdc:../src/Features/Orders)
- ## Common Pitfalls
- * Creating traditional layers (Controllers, Services, Repositories) that cut across features
- * Sharing domain models between unrelated features
- * Direct dependencies between features (rather than using messaging)
- * Placing business logic in controllers or repositories
- * Circular dependencies between features
- ```
- ### Functional Programming Rule
- ```markdown
- ---
- title: Functional C# Coding Patterns
- description: Guidelines for applying functional programming principles in C# with modern language features
- glob: "src/**/*.cs"
- alwaysApply: false
- ---
- # Functional C# Coding Patterns
- ## Introduction
- 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.
- ## Pattern Description
- ### Immutable Data Structures
- Use records for domain models and DTOs:
- ```csharp
- // GOOD: Immutable record
- public record OrderItem(string ProductId, int Quantity, decimal UnitPrice)
- {
- // Derived property
- public decimal TotalPrice => Quantity * UnitPrice;
- // Non-mutating transformation
- public OrderItem WithQuantity(int newQuantity) =>
- this with { Quantity = newQuantity };
- }
- // GOOD: Record with validation
- public record CreateUserCommand
- {
- public string Username { get; init; }
- public string Email { get; init; }
- // Validate at construction time
- public CreateUserCommand(string username, string email)
- {
- if (string.IsNullOrEmpty(username))
- throw new ArgumentException("Username cannot be empty", nameof(username));
- Username = username;
- Email = email;
- }
- }
- ```
- ### Pure Functions
- Methods should be static and side-effect free:
- ```csharp
- // GOOD: Pure function with explicit dependencies
- public static class TaxCalculator
- {
- public static decimal CalculateTax(
- decimal amount,
- decimal taxRate,
- TaxRoundingMode roundingMode)
- {
- var tax = amount * taxRate;
- return roundingMode switch
- {
- TaxRoundingMode.Round => Math.Round(tax, 2),
- TaxRoundingMode.Ceiling => Math.Ceiling(tax * 100) / 100,
- TaxRoundingMode.Floor => Math.Floor(tax * 100) / 100,
- _ => throw new ArgumentOutOfRangeException(nameof(roundingMode))
- };
- }
- }
- ```
- ### Patterns for Managing Side Effects
- Use Result/Either pattern for error handling:
- ```csharp
- // GOOD: Return type indicates success/failure
- public static Result<User> CreateUser(string username, string email)
- {
- // Validation
- if (string.IsNullOrEmpty(username))
- return Result.Failure<User>("Username cannot be empty");
- if (!IsValidEmail(email))
- return Result.Failure<User>("Invalid email format");
- // Create user (pure transformation)
- var user = new User(Guid.NewGuid(), username, email);
- return Result.Success(user);
- }
- ```
- ## Implementation Steps
- 1. Define domain models as immutable records
- 2. Implement business logic as static methods with explicit dependencies
- 3. Use Result/Either pattern for error handling instead of exceptions
- 4. Separate pure computation from side effects
- 5. Use LINQ for collection transformations
- 6. Apply pattern matching for conditional logic
- 7. Use high-performance features (Span<T>, stackalloc) where needed
- ## Real-World Examples
- * [Order Processing](mdc:../src/Features/Orders/Commands/ProcessOrder/ProcessOrderHandler.cs)
- * [User Management](mdc:../src/Features/Users/Queries/GetUserDetails/GetUserDetailsHandler.cs)
- ## Common Pitfalls
- * Mixing business logic with side effects
- * Using mutable collections when immutable ones would work
- * Not making methods static when possible
- * Using inheritance hierarchies instead of composition
- * Relying on exceptions for normal control flow
- * Using null instead of Option/Maybe pattern
- ```
- ### Performance Optimization Rule
- ```markdown
- ---
- title: .NET Performance Optimization Patterns
- description: Guidelines for implementing high-performance C# code with modern language features
- glob: "src/**/*.cs"
- alwaysApply: false
- ---
- # .NET Performance Optimization Patterns
- ## Introduction
- 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.
- ## Pattern Description
- ### Memory-Efficient Operations
- Use `Span<T>` and `Memory<T>` for memory-efficient operations:
- ```csharp
- // GOOD: Memory-efficient string parsing
- public static bool TryParseId(ReadOnlySpan<char> input, out Guid id)
- {
- id = default;
- // Early validation without allocations
- if (input.Length != 36)
- return false;
- return Guid.TryParseExact(input, "D", out id);
- }
- // GOOD: Avoiding string allocations
- public static bool IsValidProductCode(ReadOnlySpan<char> code)
- {
- if (code.Length != 10)
- return false;
- // Prefix check without allocations
- return code.StartsWith("PRD-".AsSpan()) &&
- char.IsDigit(code[4]) &&
- char.IsDigit(code[5]);
- }
- ```
- ### High-Performance Data Structures
- Use structs for small, frequently-used data types:
- ```csharp
- // GOOD: Struct for small data that's used frequently
- public readonly struct Money
- {
- public decimal Amount { get; }
- public string Currency { get; }
- public Money(decimal amount, string currency)
- {
- Amount = amount;
- Currency = currency ?? throw new ArgumentNullException(nameof(currency));
- }
- // Pure operations
- public static Money Add(Money a, Money b)
- {
- if (a.Currency != b.Currency)
- throw new InvalidOperationException("Cannot add different currencies");
- return new Money(a.Amount + b.Amount, a.Currency);
- }
- // Zero allocation equality
- public bool Equals(Money other) =>
- Amount == other.Amount && Currency == other.Currency;
- }
- ```
- ### Stack Allocation
- Use stackalloc for small, short-lived buffers:
- ```csharp
- // GOOD: Stack allocation for small buffers
- public static string FormatOrderNumber(int orderId)
- {
- Span<char> buffer = stackalloc char[12]; // No heap allocation
- "ORD-".AsSpan().CopyTo(buffer);
- orderId.TryFormat(buffer.Slice(4), out int charsWritten, "D8");
- return new string(buffer.Slice(0, 4 + charsWritten));
- }
- ```
- ## Implementation Steps
- 1. Identify performance-critical paths in your application
- 2. Use `Span<T>` and `Memory<T>` for string and array operations
- 3. Consider `struct` for small, frequently-used data types
- 4. Use `stackalloc` for small, short-lived buffers
- 5. Apply proper benchmarking to validate improvements
- 6. Document performance-focused code with comments
- 7. Maintain functional principles while optimizing (pure functions, immutability)
- ## Real-World Examples
- * [High-Performance Parser](mdc:../src/Shared/Utils/CsvParser.cs)
- * [Money Value Object](mdc:../src/Shared/Models/Money.cs)
- ## Common Pitfalls
- * Premature optimization before identifying bottlenecks
- * Using `Span<T>` or `stackalloc` unnecessarily for non-performance-critical code
- * Creating structs that are too large (should generally be <16 bytes)
- * Returning `Span<T>` from methods (use `Memory<T>` instead)
- * Not considering the memory safety implications of Span
- * Sacrificing readability for minor performance gains
- ```
- ## How to Generate Rules
- When using this meta-rule to generate specific rules for your project:
- 1. **Analyze Existing Code**: Review your codebase for patterns and conventions
- 2. **Identify Core Principles**: Focus on your key architecture and style preferences
- 3. **Create Separate Rules**: Generate individual rules for different aspects (structure, style, etc.)
- 4. **Include Real Examples**: Reference actual files from your project where possible
- 5. **Test Rule Effectiveness**: Validate that Cursor correctly follows your rules
- ## Best Practices for C# Rules
- * **Be Specific to Your Project**: Customize rules to your specific architecture and patterns
- * **Use Modern C# Examples**: Include code samples using C# 13+ and .NET 9+ features
- * **Emphasize Functional Patterns**: Highlight immutability, pure functions, and side effect management
- * **Balance Performance and Readability**: Explain when to use performance-focused features
- * **Start Simple**: Begin with core architectural rules and expand as needed
- * **Update Regularly**: Refine rules as your codebase evolves
- 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