refactor(rules): split C# guidance by topic
This commit is contained in:
1 parent
d4f45a3320
commit
27db155b85
10 files changed
+118
-117
No files matched your search
@@ -1,117 +0,0 @@
|
||||
# C# Code Guidelines
|
||||
|
||||
* Write clear, readable, maintainable code first.
|
||||
* Optimize only based on measurements and profiling.
|
||||
* Prefer simple solutions over clever abstractions.
|
||||
* Follow current C# and .NET conventions.
|
||||
* Prefer immutable data where practical.
|
||||
* Keep methods small and focused.
|
||||
* Keep classes focused on a single responsibility.
|
||||
* Avoid unnecessary inheritance.
|
||||
* Prefer composition over inheritance.
|
||||
* Use `sealed` when inheritance is not intended.
|
||||
* Avoid unnecessary abstractions and wrapper layers.
|
||||
* Avoid premature generalization.
|
||||
* Prefer explicit code over hidden magic.
|
||||
* Avoid unnecessary reflection.
|
||||
* Avoid unnecessary runtime type checks.
|
||||
* Avoid unnecessary allocations in hot paths.
|
||||
* Avoid unnecessary collection copies.
|
||||
* Avoid unnecessary string allocations.
|
||||
* Avoid unnecessary LINQ in performance-critical paths.
|
||||
* Use LINQ where it improves readability and performance is irrelevant.
|
||||
* Choose collections based on access patterns.
|
||||
* Preallocate collection capacity when the expected size is known.
|
||||
* Avoid large mutable structs.
|
||||
* Use `Span<T>` and related APIs only when they provide a measurable benefit.
|
||||
* Prefer `Task` over `ValueTask` unless `ValueTask` is justified by profiling.
|
||||
* Use async only for genuinely asynchronous operations.
|
||||
* Keep async code async throughout the call chain.
|
||||
* Avoid `.Result`, `.Wait()` and synchronous blocking of async operations.
|
||||
* Propagate `CancellationToken` through asynchronous APIs.
|
||||
* Do not create unnecessary background tasks.
|
||||
* Limit concurrency when processing large workloads.
|
||||
* Avoid uncontrolled `Task.WhenAll` over large collections.
|
||||
* Avoid exceptions for normal control flow.
|
||||
* Fail fast on invalid arguments and invalid state.
|
||||
* Avoid swallowing exceptions.
|
||||
* Preserve meaningful exception context.
|
||||
* Use dependency injection where dependencies require substitution or lifecycle management.
|
||||
* Avoid service locator patterns.
|
||||
* Avoid excessive constructor dependencies.
|
||||
* Keep application, domain and infrastructure concerns separated.
|
||||
* Avoid unnecessary cross-layer coupling.
|
||||
* Prefer explicit dependencies.
|
||||
* Minimize global and static mutable state.
|
||||
* Design APIs with clear contracts.
|
||||
* Prefer strongly typed models over loosely typed dictionaries or dynamic objects.
|
||||
* Validate input at system boundaries.
|
||||
* Avoid duplicate validation deep inside trusted internal code.
|
||||
* Treat external input as untrusted.
|
||||
* Do not expose internal implementation details through public APIs.
|
||||
* Avoid leaking sensitive information through logs or exceptions.
|
||||
* Use structured logging.
|
||||
* Avoid excessive logging in hot paths.
|
||||
* Avoid logging sensitive data.
|
||||
* Prefer database-side filtering, projection and aggregation.
|
||||
* Do not load data that is not needed.
|
||||
* Avoid N+1 database queries.
|
||||
* Use no-tracking queries for read-only EF Core operations where appropriate.
|
||||
* Keep database round trips minimal.
|
||||
* Avoid unnecessary serialization and deserialization.
|
||||
* Reuse expensive resources such as HTTP clients and database infrastructure correctly.
|
||||
* Do not optimize micro-level CPU operations before fixing architectural, database or I/O bottlenecks.
|
||||
* Prefer algorithms and data structures with appropriate complexity.
|
||||
* Measure allocations, GC pressure, CPU usage and latency when investigating performance.
|
||||
* Write code that is easy to test.
|
||||
* Avoid tests coupled to implementation details.
|
||||
* Prefer deterministic behavior.
|
||||
* Avoid hidden side effects.
|
||||
* Keep naming consistent and descriptive.
|
||||
* Avoid abbreviations unless they are established domain terminology.
|
||||
* Remove dead code instead of keeping it as commented-out code.
|
||||
* Add comments or API documentation when they clarify non-obvious decisions, public contracts, or project conventions require them; otherwise prefer self-explanatory code.
|
||||
* Follow existing project conventions unless there is a strong reason to change them.
|
||||
* Do not introduce new libraries when the BCL or existing dependencies already solve the problem adequately.
|
||||
* Prefer fewer dependencies.
|
||||
* Keep dependency versions current and supported.
|
||||
* Treat warnings seriously.
|
||||
* Do not suppress analyzers without a concrete reason.
|
||||
* Keep performance improvements readable unless the hot path clearly justifies additional complexity.
|
||||
* Prefer correctness, maintainability and predictable behavior over theoretical micro-optimizations.
|
||||
|
||||
## C# Style & Safety
|
||||
|
||||
* Prefer modern, readable C# syntax and current .NET conventions.
|
||||
* Prefer simple, explicit code over outdated or unnecessarily verbose patterns.
|
||||
* Treat collection-type guidance as a default; follow established project conventions and public API contracts when they require a different choice.
|
||||
* Prefer concrete collection types for internal parameters, properties, and local APIs when they express the intended contract without reducing needed flexibility.
|
||||
* Do not introduce collection interfaces solely for abstraction. Use `IReadOnlyList<T>`, `IEnumerable<T>`, `IDictionary<TKey, TValue>`, `IList<T>`, or `ICollection<T>` only when a public API or boundary intentionally exposes that capability.
|
||||
* Prefer `Dictionary<TKey, TValue>`, `List<T>`, arrays, or other concrete types when the implementation type is known and intentional.
|
||||
* Prefer strongly typed models over `object`, `dynamic`, loosely typed dictionaries or string-based contracts.
|
||||
* Validate external input at system boundaries.
|
||||
* Fail fast on invalid arguments and impossible states.
|
||||
* Avoid unsafe code unless explicitly required.
|
||||
* Avoid reflection when a strongly typed solution is practical.
|
||||
* Avoid hidden side effects and implicit global state.
|
||||
* Prefer immutable or readonly data where practical.
|
||||
* Avoid exposing mutable internal collections directly.
|
||||
* Prefer safe framework APIs over custom low-level implementations.
|
||||
* Never weaken validation, authorization, certificate checks, encryption or other security controls for convenience.
|
||||
* Do not log secrets, tokens, credentials or sensitive data.
|
||||
* Treat all external data as untrusted.
|
||||
* Prefer correctness and security over minor convenience or micro-optimizations.
|
||||
|
||||
## Performance Priority
|
||||
|
||||
Prioritize performance work in this order:
|
||||
|
||||
* Architecture
|
||||
* Database access
|
||||
* Network and I/O
|
||||
* Algorithms and data structures
|
||||
* Concurrency
|
||||
* Allocations and GC pressure
|
||||
* CPU micro-optimizations
|
||||
|
||||
Never optimize based purely on assumptions.
|
||||
@@ -0,0 +1,14 @@
|
||||
# C# Rules
|
||||
|
||||
For C# or .NET work, open only the topic rules relevant to the task. Follow repository conventions and load applicable shared security rules as well.
|
||||
|
||||
| Topic | Codex rule | Claude skill |
|
||||
| --- | --- | --- |
|
||||
| Language style and code organization | `references/coding-style.md` | `rules-csharp-coding-style` |
|
||||
| Types, collections, and public contracts | `references/types-and-collections.md` | `rules-csharp-types-and-collections` |
|
||||
| Architecture and API boundaries | `references/architecture-and-apis.md` | `rules-csharp-architecture-and-apis` |
|
||||
| Async work and concurrency | `references/async-and-concurrency.md` | `rules-csharp-async-and-concurrency` |
|
||||
| Performance | `references/performance.md` | `rules-csharp-performance` |
|
||||
| Security | `references/security.md` | `rules-csharp-security` |
|
||||
| Validation, errors, and logging | `references/safety-and-errors.md` | `rules-csharp-safety-and-errors` |
|
||||
| Tests, dependencies, and analyzers | `references/testing-and-dependencies.md` | `rules-csharp-testing-and-dependencies` |
|
||||
@@ -0,0 +1,15 @@
|
||||
# C# Architecture and APIs
|
||||
|
||||
Apply these rules when designing public APIs, service boundaries, or application architecture.
|
||||
|
||||
* Give APIs explicit, strongly typed contracts. Validate external input at the boundary and keep trusted internal paths free of redundant checks.
|
||||
* Separate request models from persistence and domain models. Bind only fields the caller may set, and project only fields the caller may read.
|
||||
* Authenticate and authorize before accessing protected resources. Enforce resource ownership and tenant scope in the data access path, not only in the UI or route layer.
|
||||
* Bound page size, payload size, query complexity, and operation cost. Use explicit pagination for potentially large collections.
|
||||
* Keep application, domain, and infrastructure concerns separate. Avoid cross-layer coupling and service-locator patterns.
|
||||
* Use dependency injection when substitution or lifecycle management is needed. Keep dependencies explicit and avoid excessive constructor dependencies.
|
||||
* Prefer composition over inheritance; use inheritance only when the contract requires it. Seal types when extension is not intended.
|
||||
* Keep classes and methods focused. Avoid wrappers, abstractions, reflection, and global mutable state without a concrete need.
|
||||
* Preserve established project and public API conventions unless a concrete compatibility or correctness issue requires a change.
|
||||
* Keep authorization decisions close to the protected operation and fail closed when identity, ownership, or policy evaluation is unavailable.
|
||||
* Avoid returning persistence entities, internal exception details, or fields that are not part of the public contract.
|
||||
@@ -0,0 +1,13 @@
|
||||
# C# Async and Concurrency
|
||||
|
||||
Apply these rules when adding or changing asynchronous APIs, background work, or concurrent processing.
|
||||
|
||||
* Use asynchronous APIs for genuinely asynchronous I/O; do not wrap synchronous work in unnecessary tasks.
|
||||
* Keep asynchronous operations async through the call chain. Avoid `.Result`, `.Wait()`, and other synchronous waits that can block threads or deadlock.
|
||||
* Accept and propagate `CancellationToken` through work that can take meaningful time or be cancelled by its caller.
|
||||
* Bound concurrency for large or unbounded workloads. Avoid creating a task per item or using uncontrolled `Task.WhenAll`; use an appropriate queue or concurrency limiter when work must be throttled.
|
||||
* Use `ValueTask` only when profiling supports it and its usage contract is clear; otherwise prefer `Task`.
|
||||
* Keep shared state safe under concurrent access and preserve cancellation and exception behavior when coordinating tasks. Prefer immutable messages or synchronization primitives with clear ownership over unsynchronized shared mutation.
|
||||
* Avoid holding locks across `await`, blocking thread-pool threads, and starting background work whose lifetime is detached from its owner.
|
||||
* For producer/consumer work, apply backpressure and bound buffered items so a fast producer cannot exhaust memory.
|
||||
* Retry only transient failures, with bounded attempts and delay. Preserve idempotency and cancellation, and avoid multiplying retries across nested layers.
|
||||
@@ -0,0 +1,13 @@
|
||||
# C# Coding Style
|
||||
|
||||
Apply these rules when editing C# syntax, class structure, and everyday code organization.
|
||||
|
||||
* Follow current C# and .NET conventions and the style already used in the project.
|
||||
* Prefer direct, readable code over clever control flow, hidden magic, or outdated verbosity.
|
||||
* Keep classes, methods, and responsibilities focused. Use descriptive names and established domain terms; avoid unexplained abbreviations.
|
||||
* Prefer composition over inheritance. Use inheritance only for a real substitutable contract, and seal types when extension is not intended.
|
||||
* Avoid unnecessary wrappers, abstractions, reflection, runtime type checks, and global mutable state.
|
||||
* Prefer immutable or readonly state when it simplifies ownership and reduces accidental mutation.
|
||||
* Keep side effects visible. Make behavior deterministic and easy to test where practical.
|
||||
* Add comments or API documentation only for non-obvious decisions, public contracts, or documented project requirements.
|
||||
* Remove dead code instead of leaving commented-out implementations.
|
||||
@@ -0,0 +1,14 @@
|
||||
# C# Performance
|
||||
|
||||
Apply these rules when optimizing code or reviewing runtime and resource costs.
|
||||
|
||||
* Establish a representative baseline before changing code. Measure the affected workload, input size, concurrency, and environment; distinguish average latency from tail latency and throughput.
|
||||
* Find the dominant cost before optimizing: architecture, database access, network and I/O, algorithms, concurrency, allocations and GC, then CPU-level work.
|
||||
* Choose collections and algorithms for actual access patterns. Check asymptotic cost and data growth; avoid repeated scans, unnecessary sorting, copies, and unbounded result sets.
|
||||
* Keep data close to where it is processed. Filter and project in the database, avoid N+1 queries, bound page sizes, and avoid materializing rows or columns the caller does not use.
|
||||
* Stream large inputs and outputs when buffering the whole value would waste memory. Bound queues, batches, and parallel work to match downstream capacity.
|
||||
* Reduce allocations in measured hot paths: avoid repeated string construction, iterator/delegate capture, boxing, and collection resizing when profiling identifies them as material costs.
|
||||
* Consider pooling only for expensive reusable objects with clear ownership and reset rules. Pooling can retain memory or leak state between users; measure it and cap retained capacity.
|
||||
* Use `Span<T>`, `ValueTask`, preallocation, or specialized low-level APIs only when their benefit is demonstrated and they preserve readable, safe ownership.
|
||||
* Cache only when repeated work is measurable and the key, lifetime, size bound, invalidation, and sensitive-data handling are explicit.
|
||||
* After an optimization, run the same workload and compare correctness, allocations, GC, CPU, latency, and throughput against the baseline. Keep the simplest change that meets the target.
|
||||
@@ -0,0 +1,14 @@
|
||||
# C# Safety and Error Handling
|
||||
|
||||
Apply these rules when handling external input, failures, logs, or sensitive information.
|
||||
|
||||
* Treat external data as untrusted and validate it at system boundaries. Do not rely on static types to validate runtime input.
|
||||
* Validate size, range, format, and allowed values before expensive parsing or allocation. Keep validation consistent with downstream business and security constraints.
|
||||
* Fail fast for invalid arguments or impossible state. Do not use exceptions for routine control flow or swallow unexpected exceptions.
|
||||
* Preserve useful exception context without exposing secrets, personal data, or internal details in logs, APIs, or user-facing errors.
|
||||
* Use structured logging and avoid excessive logging, especially in hot paths. Never log credentials, tokens, or sensitive payloads.
|
||||
* Keep authorization, certificate validation, encryption, and other security controls intact. Follow the focused security rules for the affected boundary.
|
||||
* Keep side effects explicit and deterministic where practical; propagate cancellation and dispose owned resources correctly.
|
||||
* Use narrow exception handling around the operation that can fail. Do not catch broadly merely to retry, continue with partial state, or convert failures into success.
|
||||
* Avoid returning different error detail to unauthenticated callers when it would disclose whether protected accounts, records, or resources exist.
|
||||
* Treat resource limits as part of safe error handling: cap request bodies, decompressed data, collection sizes, parser depth, and other attacker-controlled work.
|
||||
@@ -0,0 +1,14 @@
|
||||
# C# Security
|
||||
|
||||
Apply these rules when implementing or reviewing security-sensitive .NET code. Load the matching shared security rule for the affected API, identity, data, file, network, cryptographic, or dependency boundary as well.
|
||||
|
||||
* Validate untrusted input at the boundary and enforce authorization for the specific resource and operation. Static types and client-side checks do not establish trust.
|
||||
* Use parameterized database APIs. If a query identifier or sort expression cannot be parameterized, select it from a fixed allowlist; never concatenate untrusted input into SQL.
|
||||
* For file access, canonicalize the resolved path and verify it remains inside the authorized root. Account for reparse points and symlinks when the attacker can affect directory contents.
|
||||
* For process execution, avoid a shell. Pass executable and arguments separately using APIs supported by the target framework, validate each argument, and restrict executable paths.
|
||||
* Deserialize into explicit DTOs or allowlisted types. Avoid unsafe polymorphic type activation and unbounded object graphs for untrusted data.
|
||||
* Bound request and parser work before allocating or processing large values. Apply timeouts to regular expressions that evaluate untrusted input and prefer patterns with predictable complexity.
|
||||
* Keep secrets in an approved secret store or protected configuration source. Never embed them in assemblies, client bundles, source, diagnostics, or exception messages.
|
||||
* Use platform TLS and established cryptographic APIs. Do not create custom cryptography or disable certificate validation; use cryptographically secure randomness for security tokens.
|
||||
* Use framework output encoding and safe APIs for HTML, XML, and other interpreters. Do not build executable markup or queries from untrusted strings.
|
||||
* Review security-sensitive changes with tests for rejected, malformed, oversized, unauthenticated, and cross-tenant inputs, as applicable.
|
||||
@@ -0,0 +1,10 @@
|
||||
# C# Testing and Dependencies
|
||||
|
||||
Apply these rules when changing tests, package references, analyzers, or build diagnostics.
|
||||
|
||||
* Write deterministic behavior-based tests. Avoid coupling assertions to private call order, implementation details, current time, or uncontrolled external services.
|
||||
* Make code testable through clear contracts and dependencies; do not add production hooks or abstractions solely for tests without a concrete need.
|
||||
* Prefer the .NET BCL and existing supported packages over new libraries when they solve the problem adequately.
|
||||
* Keep dependencies minimal, maintained, compatible, and updated through the repository's normal package-management process.
|
||||
* Treat compiler, analyzer, and nullable warnings seriously. Suppress a diagnostic only for a specific, documented reason and narrow the suppression to the affected code.
|
||||
* Run focused tests and analyzers for the changed path; broaden verification when shared behavior or compatibility could be affected.
|
||||
@@ -0,0 +1,11 @@
|
||||
# C# Types and Collections
|
||||
|
||||
Apply these rules when designing type contracts, choosing collection shapes, or exposing data through APIs.
|
||||
|
||||
* Prefer strongly typed models over `object`, `dynamic`, loosely typed dictionaries, or string-based contracts.
|
||||
* Prefer concrete collection types for internal parameters, properties, and local APIs when the contract does not need abstraction.
|
||||
* Expose collection interfaces such as `IReadOnlyList<T>`, `IEnumerable<T>`, `IDictionary<TKey, TValue>`, or `ICollection<T>` only when their capability is part of an intentional boundary or public contract.
|
||||
* Choose collection types based on lookup, ordering, mutation, and iteration needs. Pre-size collections only when expected capacity is known and material.
|
||||
* Avoid large mutable structs and direct exposure of mutable internal collections.
|
||||
* Prefer immutable data where practical, while preserving established API and serialization contracts.
|
||||
* Use `Span<T>` and related low-level types only when the performance rule's measurement justifies their lifetime and ownership constraints.
|
||||
Reference in new issue
Block a user