Skip to content

First custom rule

Expressing a team-specific convention as a CodeCharter rule.

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 .codecharter/rules

All .ccr files go into this directory.

Write the rule

.codecharter/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.StartsWith("Acme.Infrastructure.Repositories")
where !t.IsAbstract
where !t.Name.EndsWith("Repository")
select t

Anatomy:

  • @name is the human-readable title shown in tooling.
  • @severity is info, warn, or error.
  • @category groups the rule in the output.
  • @description explains what is being checked.
  • @recommendation says how to fix it.
  • 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

codecharter analyze .

If you have a class named UserData in the repository namespace, it should now appear as an error.

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.StartsWith("Acme.Domain")
where t.UsedTypes.Any(u => u.Namespace.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 .ccr per rule. This makes versioning and searching easier.
  • 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 warn or write it more specifically.

More under Best practices.