The simplest meaningful CodeCharter rule is six lines long. Here it is:
@name "Class must not be named Manager"
@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
Save this as .codecharter/rules/no-manager-suffix.ccr and run in your repo:
codecharter analyze .
If you have a class with a Manager suffix, it will appear as a Finding.
What's Happening Here
Six lines of metadata and four lines of query. Let's walk through it.
Metadata
Every rule needs at least @name and @severity. The others are optional but
recommended — they appear in the editor hover, the CI log, and in the output
formats.
@name "Class must not be named Manager"
@severity warn
@category "Naming"
@description "..."
@recommendation "..."
| Directive | Required | Description |
|---|---|---|
@name |
yes | Human-readable title |
@severity |
yes | info, warn, or error |
@category |
no | Freely chosen; groups related rules |
@description |
no | What the rule checks |
@recommendation |
no | How to fix it |
Query
The body is a LINQ query against the code model:
from t in Types # all types
where t.Kind == "Class" # classes only
where t.Name.EndsWith("Manager") # with Manager suffix
select t # returns all matches as findings
select t is the required closing clause. Whatever you select becomes a
Finding — t is a TypeModel, so the Finding will point to the file and line
where t is declared.
Variant: Banning Suffixes Dynamically
@name "No vague class suffixes"
@severity warn
from t in Types
where t.Kind == "Class"
where new[] { "Manager", "Helper", "Util", "Utility" }
.Any(suffix => t.Name.EndsWith(suffix))
select t
LINQ expressions are fully supported. You can build inline arrays, sort, and filter.
Variant: With a Sub-Query
A rule that finds only classes that are too large AND have too many fields:
@name "Large class with many fields, split candidate"
@severity warn
@category "Design"
from t in Types
where t.Kind == "Class"
where t.LinesOfCode > 400
where t.Fields.Count > 15
select t
Variant: Methods Instead of Types
@name "Public async method without CancellationToken"
@severity warn
@category "Async"
from m in Methods
where m.IsAsync
where m.AccessModifier == "Public"
where !m.IsOverride
where !m.Parameters.Any(p => p.TypeShortName == "CancellationToken")
select m
Same structure, just with Methods instead of Types as the starting point.
Where to Go Next
- File Structure — how the
.codecharterdirectory is organized - Syntax Overview — all available properties and operators
- Best Practices — what separates good rules from bad ones