| Abstract class not suffixed with Base |
warn |
Abstract base classes should have a name ending with 'Base' to clearly indicate their role in the hierarchy |
| Abstract class without abstract members |
warn |
An abstract class with no abstract methods or abstract properties provides no template for subclasses to implement |
| Async method missing Async suffix |
warn |
Async methods should end with 'Async' to clearly indicate their asynchronous nature |
| Async method without CancellationToken |
warn |
All public/protected async methods should accept a CancellationToken parameter |
| Async void method |
error |
Async void methods cannot be awaited and swallow exceptions. Use async Task instead |
| Async-suffixed method does not return Task |
error |
Methods ending with 'Async' must return Task or Task. A synchronous method should not have the Async suffix |
| Attribute class not suffixed with Attribute |
warn |
Classes that inherit from System.Attribute should have a name ending with 'Attribute' |
| Boolean method missing verb prefix |
warn |
Methods returning bool should start with a verb prefix like Is, Has, Can, Should, Try, Validate, Check, Contains, Exists, Equals, Supports, Needs, Allows |
| Boolean property missing verb prefix |
warn |
Boolean properties should start with a verb or adjective prefix indicating state |
| Calculate/Compute method returns void |
warn |
Methods starting with 'Calculate' or 'Compute' are expected to return the computed result |
| CancellationToken parameter missing default value |
warn |
CancellationToken parameters should have a default value of 'default' so callers are not forced to pass one explicitly |
| Catching System.Exception directly |
info |
Catching System.Exception is too broad and hides bugs by swallowing exceptions the code was never designed to handle, such as NullReferenceException or OutOfMemoryException |
| Class too large |
warn |
Classes exceeding 500 lines indicate a need for refactoring |
| Constant not in UPPER_SNAKE_CASE |
warn |
Constants (const fields) must use UPPER_SNAKE_CASE naming |
| Constructor has too many parameters |
warn |
Constructors with more than 4 real domain dependencies indicate too many responsibilities, violating Single Responsibility Principle. Cross-cutting concerns (CancellationToken, ILogger, TimeProvider, IMemoryCache, IDistributedCache) do not count toward the limit. |
| Constructor injects concrete class instead of interface |
error |
Dependencies should be injected via interfaces for loose coupling and testability. Concrete class parameters that are not data types indicate tight coupling |
| Conversion method returns void |
error |
Methods prefixed with Convert, Map, Transform, or To are expected to return the converted result |
| Count-prefixed method does not return int |
warn |
Methods starting with 'Count' are expected to return an integer count |
| Create-prefixed method returns void |
error |
Methods starting with 'Create' or 'Build' are expected to return the constructed object |
| Deep inheritance hierarchy |
info |
|
| Deep nesting |
warn |
|
| Direct DateTime/DateTimeOffset usage instead of TimeProvider |
warn |
Direct calls to DateTime.Now/UtcNow/Today or DateTimeOffset.Now/UtcNow make code untestable. Use System.TimeProvider (available since .NET 8) instead |
| Direct instantiation in constructor or field initializer |
error |
Dependencies should not be created directly in constructors or field initializers. Use dependency injection via the constructor instead. Nullable parameters with null-coalescing fallback (x ?? new Default()) are acceptable |
| Empty catch block |
error |
Empty catch blocks silently swallow exceptions, hiding bugs and making debugging impossible |
| Empty interface |
info |
|
| Exception class not suffixed with Exception |
warn |
Classes that inherit from Exception should have a name ending with 'Exception' |
| Explicit type when var is obvious |
info |
When the right-hand side of a variable declaration already makes the type obvious (new T(), cast, or type-named method), repeating the type on the left adds redundancy without improving readability |
| Field name too long |
info |
Excessively long field names reduce readability |
| Find-prefixed method returns void |
error |
Methods starting with 'Find' or 'Search' are expected to return the found result or a collection |
| Fire-and-forget async call |
error |
Calling an async method without awaiting, assigning, or returning the Task silently discards exceptions and makes failures invisible. The caller has no way to know if the operation succeeded |
| Get-prefixed method returns void |
error |
Methods starting with 'Get' are expected to return a value, not void |
| God class |
warn |
Classes with excessive size and method count indicate too many responsibilities |
| High afferent coupling |
info |
|
| High cognitive complexity |
warn |
Cognitive complexity measures how hard a method is to understand. Unlike cyclomatic complexity, it penalizes nested control flow more heavily |
| High cyclomatic complexity |
warn |
|
| High efferent coupling |
warn |
|
| High response for class |
info |
A class with a high number of callable methods (own plus called) has too much potential interaction surface, making it harder to test and predict side effects |
| High weighted methods per class |
warn |
The sum of cyclomatic complexity across all methods indicates a class that accumulates too much complex logic, even if individual methods seem small |
| Interface not prefixed with I |
error |
|
| Internal/Impl method should not be public |
warn |
Methods suffixed with 'Internal' or 'Impl' suggest implementation details that should not be part of the public API |
| Large enum |
info |
|
| Large void method |
info |
Large void methods often indicate missing return values or too many side effects |
| Logic class without interface |
warn |
Classes with significant logic should implement an interface for testability and dependency injection |
| Low cohesion |
warn |
|
| Low maintainability index |
warn |
The maintainability index combines complexity and size metrics into a single score. A low score indicates code that is hard to maintain, understand, and modify safely |
| Magic number literal |
info |
Numeric literals embedded directly in code are hard to understand and maintain. When the same value appears in multiple places, changing it requires finding every occurrence |
| Method has boolean parameter (flag argument) |
info |
Boolean parameters make call sites unreadable (e.g. Process(data, true, false)) and often indicate the method does two different things |
| Method name too long |
info |
Excessively long method names reduce readability |
| Method not in PascalCase |
error |
Methods must start with an uppercase letter (PascalCase) |
| Method parameter missing XML documentation |
info |
Public methods with XML docs should document all parameters with tags |
| Method too large |
warn |
|
| Missing early return / guard clause |
warn |
Methods that wrap their body in an if-statement should use the inverted condition as a guard clause with early return |
| Mutable struct |
warn |
Structs should be immutable. Mutable structs cause unexpected behavior due to value-type copy semantics |
| Mutually dependent types |
warn |
Two types that depend on each other create tight coupling and are harder to understand, test, and maintain independently |
| Namespace far from main sequence |
info |
Namespaces with a high distance from the main sequence (|Abstractness + Instability - 1|) may have an imbalanced mix of abstractions and implementations |
| Namespace with single type |
info |
Namespaces containing only a single type may indicate over-granular organization |
| Non-abstract class in deep hierarchy |
info |
|
| Non-public class without subclasses should be sealed |
info |
Internal or private classes with no derived types should be sealed for clarity and minor performance benefits |
| Non-readonly static field |
warn |
Mutable static fields represent shared mutable state, a common source of threading bugs and hidden coupling |
| Null check uses == instead of pattern matching |
warn |
Use 'is null' / 'is not null' instead of '== null' / '!= null' for null checks |
| Override method missing inheritdoc |
info |
Override methods should use to inherit documentation from the base class |
| Parameter not in camelCase |
error |
Method parameters must use camelCase naming (start with lowercase letter) |
| Parse method returns void |
error |
Methods starting with 'Parse' are expected to return the parsed result |
| Primitive type in logic class constructor |
warn |
Logic classes should not receive primitive values directly in their constructor. These are not registrable in DI containers. Wrap them in an Options record instead |
| Private field missing underscore prefix |
error |
Private fields must use _camelCase naming (underscore prefix followed by lowercase) |
| Property not in PascalCase |
error |
Properties must start with an uppercase letter (PascalCase) |
| Public member missing XML documentation |
warn |
All public types, methods, and properties should have XML documentation comments with |
| Public method uses ref or out parameter |
error |
Public methods should not use ref or out parameters. They make the API harder to use, prevent async usage, and break interface contracts. Use a return record instead |
| Public method uses Tuple in signature |
error |
Public methods should not use Tuple (ValueTuple or System.Tuple) as parameter or return type. Tuples have unnamed or weakly named members and provide no semantic context. Use a dedicated record instead |
| Public mutable field |
error |
|
| Public nested type |
info |
Nested types that are public should generally be top-level types. Public nested types are harder to discover and use |
| Record input parameter has wrong suffix |
warn |
Record parameters in public methods should use descriptive suffixes that indicate their role: Command, Query, Request, Filter, Input, or Dto |
| Record return type has wrong suffix |
warn |
Record return types in public methods should use descriptive suffixes that indicate their role: Result, Response, Dto, Output, or Info |
| Record should not have public methods |
warn |
Records are data classes and should only contain properties and constructors. Public methods indicate misplaced logic that belongs in a service or extension method |
| Sealed class with protected members |
warn |
Protected members in a sealed class are inaccessible since the class cannot be inherited. This misleads readers about the intended design |
| Static class has public non-extension methods |
warn |
Static classes should only contain public extension methods. Non-extension public static methods should be in a regular class or converted to extension methods |
| Static class with too many methods |
warn |
|
| Static readonly field not in UPPER_SNAKE_CASE |
warn |
Public and internal static readonly fields acting as constants must use UPPER_SNAKE_CASE naming |
| String concatenation with + operator |
info |
Repeated string concatenation with '+' creates intermediate string objects on each operation, which hurts readability and can degrade performance in loops or hot paths |
| TODO/FIXME comment in code |
warn |
TODO, FIXME, HACK, and UNDONE comments indicate unfinished work that should be tracked as issues |
| Too many local variables |
info |
|
| Too many overloads |
info |
|
| Too many parameters |
error |
|
| Too many public methods |
warn |
|
| Try-prefixed method does not return bool |
error |
Methods starting with 'Try' follow the TryParse pattern and must return a boolean indicating success or failure |
| Type implements too many interfaces |
warn |
A type implementing more than 5 interfaces likely violates Interface Segregation Principle or has too many responsibilities |
| Type name starts with lowercase |
error |
|
| Type name too long |
info |
|
| Unstable highly-coupled type |
warn |
|
| Unused method parameter |
warn |
Parameters that are declared but never used in the method body indicate dead code, an incomplete implementation, or a method signature that has drifted from its actual behavior |
| Unused using directive |
info |
Unused using directives add visual noise and can slow down compilation. They also make it harder to understand which dependencies a file actually relies on |
| Wide type hierarchy |
info |
|