Skip to content

Regel-Beispiele

Sammlung fertig komponierter CodeCharter-Regeln, nach Kategorie sortiert. Kopierbar als Startpunkt für eure eigenen Konventionen.

Jedes Beispiel ist eine komplette .ccr-Datei. Kopiert was ihr braucht, passt die Schwellen oder Strings an und committet es in .codecharter/rules/ eures Repos.

Async

Async-Methode ohne Async-Suffix

@name "Async method missing Async suffix"
@severity warn
@category "Async"
@description "Async methods should end with 'Async' so callers spot await sites"
@recommendation "Rename Foo to FooAsync when the method returns Task or ValueTask"

from m in Methods
where m.IsAsync
where !m.Name.EndsWith("Async")
select m

Public-Async ohne CancellationToken

@name "Public async method missing CancellationToken"
@severity warn
@category "Async"
@description "Public async APIs should accept a CancellationToken so callers can cancel"
@recommendation "Add CancellationToken parameter with default value"

from m in Methods
where m.IsAsync
where m.AccessModifier == "Public"
where !m.Parameters.Any(p => p.TypeShortName == "CancellationToken")
select m

Fire-and-forget Async

@name "Async call not awaited"
@severity error
@category "Async"
@description "Calling an async method without awaiting silently drops exceptions"
@recommendation "Await the result, or assign to '_ = ...' if discard is intentional"

from m in Methods
where m.SyntaxIssues.Any(i => i.Kind == "FireAndForgetAsync")
select m

Error Handling

Leerer Catch-Block

@name "Empty catch block"
@severity error
@category "ErrorHandling"
@description "Empty catch silently swallows exceptions"
@recommendation "Log and rethrow, or catch the specific exception you can recover from"

from m in Methods
where m.SyntaxIssues.Any(i => i.Kind == "EmptyCatch")
select m

Generic Exception-Catch ohne Rethrow

@name "Generic exception caught without rethrow"
@severity warn
@category "ErrorHandling"
@description "Catching Exception swallows everything, including bugs"
@recommendation "Catch the narrowest exception type the caller can recover from"

from m in Methods
where m.SyntaxIssues.Any(i => i.Kind == "GenericExceptionCatch")
select m

Design

Konstruktor mit zu vielen Parametern

@name "Constructor with too many parameters"
@severity warn
@category "Design"
@description "Constructors with 5+ parameters usually indicate too many responsibilities"
@recommendation "Split the class or use a parameter object"

from m in Methods
where m.IsConstructor
where m.NumberOfParameters > 4
select m

Klasse zu groß

@name "Class too large"
@severity warn
@category "Design"
@description "Classes over 500 lines tend to mix responsibilities"
@recommendation "Extract collaborators or split by responsibility"

from t in Types
where t.Kind == "Class"
where t.LinesOfCode > 500
select t

Static-Klasse mit nicht-Extension public Methoden

@name "Static class has public non-extension methods"
@severity warn
@category "Design"
@description "Static utility classes should host extension methods only"
@recommendation "Convert to instance methods on a real type, or mark the parameter with 'this'"

from t in Types
where t.IsStatic
where t.Kind == "Class"
where t.Methods.Any(m => m.AccessModifier == "Public" && !m.IsExtensionMethod)
select t

Naming

Boolean-Property ohne Verb-Präfix

@name "Boolean property missing verb prefix"
@severity warn
@category "Naming"
@description "Booleans should be readable as questions: Is, Has, Can, Should, Enable, Disable"
@recommendation "Prefix the property to convey the state it represents"

from p in Properties
where p.TypeShortName == "Boolean" || p.TypeShortName == "bool"
where !p.Name.StartsWith("Is")     && !p.Name.StartsWith("Has")
where !p.Name.StartsWith("Can")    && !p.Name.StartsWith("Should")
where !p.Name.StartsWith("Enable") && !p.Name.StartsWith("Disable")
select p

Interface ohne I-Präfix

@name "Interface missing I prefix"
@severity warn
@category "Naming"
@description "Interface names should start with I, following C# convention"
@recommendation "Rename Foo to IFoo and update implementations"

from t in Types
where t.Kind == "Interface"
where !t.Name.StartsWith("I")
select t

Klasse mit "Manager"-Suffix

@name "Class named with vague Manager suffix"
@severity warn
@category "Naming"
@description "Classes called *Manager are vague: name them after what they actually do"
@recommendation "Pick a verb-based name: OrderProcessor, EmailDispatcher, PriceCalculator"

from t in Types
where t.Kind == "Class"
where t.Name.EndsWith("Manager")
select t

Determinism

Direkte DateTime-Nutzung

@name "Direct DateTime.Now / DateTime.UtcNow usage"
@severity error
@category "Determinism"
@description "Direct DateTime access makes tests non-deterministic"
@recommendation "Inject IClock and call _clock.UtcNow instead"

from m in Methods
where m.SyntaxIssues.Any(i => i.Kind == "DateTimeDirectUsage")
select m

Konstruktor ruft new auf

@name "Constructor allocates dependency directly"
@severity warn
@category "Determinism"
@description "Allocating dependencies in a constructor bypasses DI and breaks testability"
@recommendation "Inject the dependency through the constructor"

from m in Methods
where m.IsConstructor
where m.SyntaxIssues.Any(i => i.Kind == "NewInConstructor")
select m

Architecture

Domain hängt von Infrastructure ab

@name "Domain layer references Infrastructure"
@severity error
@category "Architecture"
@description "Domain layer must not depend on infrastructure concerns"
@recommendation "Move the call behind an interface in Domain that Infrastructure implements"

from t in Types
where t.Namespace.Contains(".Domain.")
where t.UsedTypes.Any(u => u.Namespace.Contains(".Infrastructure."))
select t

Controller ruft Repository direkt

@name "Controller calls Repository directly"
@severity warn
@category "Architecture"
@description "Controllers should go through the application layer, not access data directly"
@recommendation "Introduce an application service that wraps the repository call"

from t in Types
where t.Name.EndsWith("Controller")
where t.UsedTypes.Any(u => u.Name.EndsWith("Repository"))
select t

Komplexität

Methode zu lang

@name "Method too long"
@severity info
@category "Style"
@description "Methods over 60 lines are harder to read and harder to test"
@recommendation "Extract sub-routines once a method passes ~60 lines"

from m in Methods
where m.LinesOfCode > 60
select m

Hohe zyklomatische Komplexität

@name "High cyclomatic complexity"
@severity warn
@category "Style"
@description "Methods with branching depth above 10 are hard to test and reason about"
@recommendation "Extract guard clauses, replace nested ifs with early returns, or split the method"

from m in Methods
where m.CyclomaticComplexity > 10
select m

Hohe kognitive Komplexität

@name "High cognitive complexity"
@severity warn
@category "Style"
@description "Cognitive complexity above 15 indicates the method is hard for humans to follow"
@recommendation "Flatten nesting, extract intermediate variables with meaningful names"

from m in Methods
where m.CognitiveComplexity > 15
select m

Wo geht's weiter