Skip to content

Hello-World DSL

The smallest possible CodeCharter rule of your own. Explained step by step.

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