# AI Coding Tip 027 - Force Code Standards

> TL;DR: Wire your standards into hooks, skills, and a judge, so the harness blocks violations before a human opens the diff.

# Common Mistake ❌

You paste your coding standards into [AGENTS.md](https://maximilianocontieri.com/ai-coding-tip-011-initialize-agents-md) and trust the AI to remember them.

Then [a human reviewer manually checks](https://maximilianocontieri.com/ai-coding-tip-006-review-every-line-before-commit) [naming](https://maximilianocontieri.com/code-smell-159-mixedcase), [indentation](https://maximilianocontieri.com/code-smell-164-mixed-indentations), and [spacing](https://maximilianocontieri.com/code-smell-211-tab-over-spaces) on every pull request.

Nobody wires a hook, [a skill](https://maximilianocontieri.com/ai-coding-tip-004-use-modular-skills), or a judge that blocks the diff before a human ever opens it.

The standards live in prose, and prose is optional.

# Problems Addressed 😔

- A [CodeRabbit analysis of 470 pull requests](https://www.coderabbit.ai/blog/state-of-ai-vs-human-code-generation-report) found AI-generated code carries roughly 1.7x more defects than human-only code, and nearly 2x more naming and style consistency errors

- [Human reviewers burn attention on indentation and casing instead of design](https://maximilianocontieri.com/ai-coding-tip-006-review-every-line-before-commit), feeding the same [broken windows](https://maximilianocontieri.com/code-smell-173-broken-windows) that erode a codebase over time

- Enforcement depends on someone remembering to check, so it drifts the moment nobody's watching

- The AI violates a rule it read in prose, because prose isn't a gate, only a suggestion

- Two human reviewers catch two different subsets of the same [code without standards](https://maximilianocontieri.com/code-smell-48-code-without-standards)

# How to Do It 🛠️

1. Turn each standard into a machine-checkable rule instead of a paragraph of prose.

2. Wire a pre-commit or pre-merge hook that runs the linter automatically on every change.

3. Add a validator skill that [reads the rule set fresh](https://maximilianocontieri.com/ai-coding-tip-005-keep-context-fresh) and re-checks the whole diff, not just what the AI remembers.

4. Route ambiguous rules, like naming intent, to a large language model (LLM) judge instead of a human reviewer.

5. Block the commit, the merge, or [the session end](https://maximilianocontieri.com/ai-coding-tip-024-force-a-criteria-check-before-the-task-ends) until every gate reports zero violations.

6. [Log every violation the judge catches as a new rule](https://maximilianocontieri.com/ai-coding-tip-016-feed-your-pr-lessons-into-the-ai-brain), so the harness never misses it again.

# Benefits 🎯

1. **Consistent gate:** Every diff hits the same rule set, so two reviewers never catch two different subsets of the same violation.

2. **Faster reviews:** [Humans spend their attention on design and correctness](https://maximilianocontieri.com/ai-coding-tip-006-review-every-line-before-commit), not indentation or casing.

3. **No memory decay:** A skill [reads the standards file fresh every session](https://maximilianocontieri.com/ai-coding-tip-005-keep-context-fresh) instead of trusting what the AI claims to remember.

4. **Judge for nuance:** An LLM judge catches the semantic violations a regular expression can't parse, like a misleading name.

5. **Compounding rules:** [Every violation the judge finds becomes a new check](https://maximilianocontieri.com/ai-coding-tip-025-pair-every-skill-with-a-pitfalls-file), so the harness gets stricter over time.

6. **Measurable drop:** You can track the naming and style defect rate over time and watch it fall toward zero.

# Context 🧠

Standards enforcement isn't new.

Stephen C. Johnson wrote the first lint in 1978 to catch mistakes in C code nobody wanted to check by eye.

Checkstyle, PMD, and ESLint followed, each one refusing to trust a human to notice a [mixed indentation](https://maximilianocontieri.com/code-smell-164-mixed-indentations).

[SonarQube](https://www.sonarsource.com/resources/library/linting-is-not-all-you-need/) added a server that gates a whole pipeline, not just a single file.

[Mago](https://mago.carthage.software/) does the same for PHP now, running a linter, a formatter, and a static analyzer in one Rust binary fast enough to run on every keystroke.

None of these tools ever asked a human to vote on a [tab versus a space](https://maximilianocontieri.com/code-smell-211-tab-over-spaces) or a [mixedCase versus snake_case](https://maximilianocontieri.com/code-smell-159-mixedcase) name.

AI coding didn't remove that need.

It multiplied it.

A [CodeRabbit analysis of 470 pull requests](https://www.coderabbit.ai/blog/state-of-ai-vs-human-code-generation-report) found AI-generated code carries about 1.7x more defects overall than human-only code.

Naming and style consistency errors came in at nearly 2x, the exact class of mistake a linter was built to catch decades ago.

A model can now write in [any human language](https://maximilianocontieri.com/code-smell-128-non-english-coding), [misspell an identifier](https://maximilianocontieri.com/code-smell-98-speling-mistakes), or [reorder parameters inconsistently](https://maximilianocontieri.com/code-smell-87-inconsistent-parameters-sorting) across two files it never compared side by side.

A linter still catches the syntactic version of these mistakes.

The semantic version, like a name that lies about its role, needs a judge that [reads intent, not just tokens](https://maximilianocontieri.com/ai-coding-tip-019-tell-the-ai-why-not-just-what).

That's where an [LLM-as-judge](https://arxiv.org/pdf/2508.02994) step fits: a second AI pass, wired into the same [harness](https://maximilianocontieri.com/ai-coding-tip-022-give-ai-a-harness-to-work-with) that runs the linter, checking the rules no regular expression can express.

[Skills](https://maximilianocontieri.com/ai-coding-tip-004-use-modular-skills) are the natural home for that judge.

A skill reads the standards file fresh every time, instead of trusting a memory that decays across [sessions](https://maximilianocontieri.com/ai-coding-tip-005-keep-context-fresh).

Pair the judge with a [criteria check before the task ends](https://maximilianocontieri.com/ai-coding-tip-024-force-a-criteria-check-before-the-task-ends), so the AI can't say a task is done until the standards gate reports zero violations.

A gate that blocks the diff is just another way to [force the AI to obey you](https://maximilianocontieri.com/ai-coding-tip-015-force-the-ai-to-obey-you), not by asking nicely, but by refusing to let a violation through.

This doesn't replace [reviewing every line before commit](https://maximilianocontieri.com/ai-coding-tip-006-review-every-line-before-commit).

It removes the mechanical part of that review, so [a human is free to judge design instead of counting spaces](https://maximilianocontieri.com/ai-coding-tip-006-review-every-line-before-commit).

When the judge catches a new violation, log it the same way you'd log a [pitfall](https://maximilianocontieri.com/ai-coding-tip-025-pair-every-skill-with-a-pitfalls-file), so the harness never makes that mistake twice.

## Prompt Reference 📝

## Bad Prompt 🚫

<!-- [Gist Url](https://gist.github.com/mcsee/0cb16aa4f4f5b217b6a6313b5b583b3b) -->

```markdown
Please follow our coding standards for this feature.
Use consistent naming and formatting like the rest of the codebase.
I'll check it during code review before we merge.
```

## Good prompt 👉

<!-- [Gist Url](https://gist.github.com/mcsee/5fa81ed7a20b27715261493648bf6ee5) -->

```markdown
Before you say this task is done, run this standards checklist:

- [ ] Run the linter on every changed file, fix every violation.

- [ ] Run the formatter, don't hand-format a single line.

- [ ] Invoke the code-standards-validator skill on the full diff.

- [ ] Check every identifier: no abbreviations, no misleading names.

- [ ] Check indentation matches the project config, no mixed tabs.

- [ ] Check casing: one convention, no mixedCase next to snake_case.

- [ ] Check parameter order against other functions in the file.

- [ ] Check spelling in every identifier, comment, and string.

- [ ] Judge comment quality: flag dead comments and restated code.

- [ ] Judge naming intent: does each name say what it does?

- [ ] Confirm no file mixes languages in identifiers or comments.

- [ ] Log any new violation type as a rule for the next run.

- [ ] Don't report the task done until every box above is checked.

Show me the completed checklist, not just the final code.
```

# Considerations ⚠️

A linter still beats an LLM judge on speed and cost for anything syntactic.

Reserve the judge for rules that need intent, not tokens.

A gate that blocks too aggressively gets bypassed with `--no-verify`, which defeats the whole point.

Review the judge's false positives the same way you'd review a flaky linter rule.

# Type 📝

[X] Semi-Automatic

# Limitations ⚠️

An LLM judge costs tokens and time on every gate, so it doesn't replace a linter.

It complements one.

A judge can disagree with itself across two runs on borderline style calls, so keep the deterministic rules in the linter and reserve the judge for what's genuinely ambiguous.

# Tags 🏷️

- Knowledge Management

# Level 🔋

[X] Intermediate

# Related Tips 🔗

%[https://maximilianocontieri.com/ai-coding-tip-004-use-modular-skills]

%[https://maximilianocontieri.com/ai-coding-tip-006-review-every-line-before-commit]

%[https://maximilianocontieri.com/ai-coding-tip-011-initialize-agents-md]

%[https://maximilianocontieri.com/ai-coding-tip-015-force-the-ai-to-obey-you]

%[https://maximilianocontieri.com/ai-coding-tip-016-feed-your-pr-lessons-into-the-ai-brain]

%[https://maximilianocontieri.com/ai-coding-tip-019-tell-the-ai-why-not-just-what]

%[https://maximilianocontieri.com/ai-coding-tip-022-give-ai-a-harness-to-work-with]

%[https://maximilianocontieri.com/ai-coding-tip-024-force-a-criteria-check-before-the-task-ends]

%[https://maximilianocontieri.com/ai-coding-tip-025-pair-every-skill-with-a-pitfalls-file]

# Conclusion 🏁

A linter never asked permission to reject bad code.

Neither should your harness.

Wire the standards into hooks, skills, and a judge, so the diff gets rejected before a human ever has to say so.

# More Information ℹ️

[Lint (software) - Wikipedia](https://en.wikipedia.org/wiki/Lint_(software))

[State of AI vs Human Code Generation Report - CodeRabbit](https://www.coderabbit.ai/blog/state-of-ai-vs-human-code-generation-report)

[When AIs Judge AIs: The Rise of Agent-as-a-Judge Evaluation for LLMs](https://arxiv.org/pdf/2508.02994)

[Awesome Static Analysis - curated list of linters and code quality tools](https://github.com/lukehutch/awesome-static-analysis)

# Also Known As 🎭

- Harness-Enforced-Standards
- Machine-Judged-Style
- Standards-as-Code
- Linter-Native-Review

# Tools 🧰

[Mago](https://mago.carthage.software/)

[ESLint](https://eslint.org/)

[SonarQube](https://www.sonarsource.com/products/sonarqube/)

[PHP_CodeSniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer)

[Ruff](https://docs.astral.sh/ruff/)

[RuboCop](https://rubocop.org/)

[Checkstyle](https://checkstyle.sourceforge.io/)

[golangci-lint](https://golangci-lint.run/)

[Clippy](https://doc.rust-lang.org/clippy/)

# Disclaimer 📢

The views expressed here are my own.

I am a human who writes as best as possible for other humans. 

I use AI proofreading tools to improve some texts.

I welcome constructive criticism and dialogue.

I shape these insights through 30 years in the software industry, 25 years of teaching, and writing over 500 articles and a book.

* * *

This article is part of the *AI Coding Tip* series.

%[https://maximilianocontieri.com/ai-coding-tips]

