A typical case: your team has agreed that repository classes must end
with Repository. So far this gets challenged in PR reviews.
With CodeCharter it is enforced by the build.
Create the rule set in the repo
In the repo root:
mkdir -p rules
All .ccr files go into this directory. The CLI auto-discovers rules
in a rules/ directory in the current working directory — no additional
configuration is needed. If your rules live somewhere else, point the CLI
at them with --rules <dir>.
Write the rule
rules/repository-naming.ccr:
@name "Repository class must end in 'Repository'"
@severity error
@category "Naming"
@description "Implementations of the Repository pattern must have a 'Repository' suffix for discoverability"
@recommendation "Rename the class to end with 'Repository', e.g. 'UserData' -> 'UserRepository'"
from t in Types
where t.Kind == "Class"
where t.Namespace.FullName.StartsWith("Acme.Infrastructure.Repositories")
where !t.IsAbstract
where !t.Name.EndsWith("Repository")
select t
Anatomy:
@nameis the human-readable title shown in tooling. The rule slug (used in findings output and suppressions) is the filename without extension, so this rule's slug isrepository-naming.@severityisinfo,warn, orerror. Defaults towarnif omitted.@categorygroups the rule in the output (optional).@descriptionexplains what is being checked (optional but recommended).@recommendationsays how to fix it (optional but recommended).- Body: a LINQ query against the schema. Here we look for classes in the repository namespace that are not abstract and whose name does not end with "Repository".
Try it locally
First check that the rule parses and type-checks:
codecharter validate rules/repository-naming.ccr
Then run the analysis. The analyze argument is the path to a .sln,
.slnx, or .csproj file:
codecharter analyze MySolution.sln
If your solution contains a class named UserData in the repository
namespace, it now appears as an error.
Note: Running with only a local
rules/directory and no platform profiles prints a[DEPRECATION]notice on stderr pointing to platform profiles. The analysis still runs normally.
To verify the rule actually fires, you can give it a spec with hit and
miss cases: codecharter test rules/repository-naming.ccr --scaffold
generates a repository-naming.spec.md sidecar next to the rule, and
codecharter test rules/ runs all specs.
What else you can express
The DSL is a subset of LINQ. You can access all type, method, property, field, event, namespace, and assembly properties. Examples:
// Find methods without CancellationToken
from m in Methods
where m.IsAsync
where !m.Parameters.Any(p => p.TypeShortName == "CancellationToken")
where m.AccessModifier == "Public"
select m
// Layer violation: Domain references Web
from t in Types
where t.Namespace.FullName.StartsWith("Acme.Domain")
where t.UsedTypes.Any(u => u.Namespace.FullName.StartsWith("Acme.Web"))
select t
// Classes with too many methods
from t in Types
where t.Kind == "Class"
where t.NumberOfMethods > 25
select t
Schema overview and further details under Syntax reference.
Best practices for custom rules
- One rule per
.ccrfile — this is a property of the format: each.ccrfile contains exactly one query, and the file name becomes the rule ID. - Descriptive file names. They appear in build logs.
- Honest
@recommendation. If the recommendation is unhelpful, developers will disable the rule rather than fix the code. - Start small. A rule that produces 50 findings cannot be rolled out.
Downgrade it to
warnor write it more specifically.
More under Best practices.