Strategist · Designer · Operator

Launch
with Ben



Claude Code

AI Change Control: Keeping AI Code On-Architecture

A tight CLAUDE.md and custom skills still let Claude Code ship 104 violations into my Laravel codebase. AI Change Control is the layer that fixed it.


AI is allowed to build fast inside the architecture. It is not allowed to reinterpret the architecture.

The result first

A grep-based policy check found 104 real violations in a Laravel codebase I had been building for months. My own. It had a tight CLAUDE.md that named the exact patterns being caught, a stack of custom skills, and preflight checks on every deploy. None of it stopped the drift. The first scan that actually watched the code found it in under a second.

I’m not a coder. I plan and architect, and I have AI write the code. The bugs that kept appearing weren’t from a weak model or thin prompts. They came from AI doing one task well and breaking the big picture to do it: using a non-canonical function, hardcoding a value that belonged in an enum, inventing a near-duplicate helper, bypassing a service layer “just this once.” The task got done. The architecture got quietly reinterpreted.

This post is the framework that fixed it. AI Change Control is the enforcement layer that sits under your CLAUDE.md and your skills and makes misbehavior impossible to ship.

Three companion posts go deeper, and there’s a repo you can install today:

Why I’m calling it “AI Change Control”

I’m coining this name on purpose. “Change control” is an established term from ops and release engineering: a process that gates changes so they can’t land without review. That’s exactly what this does for AI-generated code. The “AI” prefix is a scope marker, not marketing. This is change control tuned to the failure modes of AI code assistants: canon bypass, hardcoded values, duplicate primitives, one-off logic in the wrong layer.

If you want to talk about it, use the name. “AI Change Control” is easier to reference than “that policy.json thing with the pre-commit hook.”

Soft enforcement vs hard enforcement

Your system is soft if the AI can still hardcode a value where canon exists, invent a near-duplicate helper, bypass the canonical service, or add one-off logic in a controller. If any of those are possible, drift isn’t likely. It’s guaranteed. The only question is how fast.

The fix is high velocity inside hard guardrails. AI should be free inside a lane and hit a wall when it leaves the lane, and the wall has to be machine enforced, not prompt enforced. Prompts shape behavior. Enforcement controls outcomes. I’d spent months treating those as the same thing.

Soft system: rules live in CLAUDE.md, skills, and prompts. AI is “encouraged” to follow canon.

Hard system: rules live in CI, static analysis, and pre-commit hooks. AI cannot ship code that violates canon.

Before the framework itself, the most common misread:

AI Change Control is: a cheap, grep-based first wall that catches 80% of drift in under a second, wired into pre-commit and CI. Zero dependencies. Works the same on Laravel, WordPress, Astro, or any stack.

AI Change Control is not: PHPStan. Not ESLint. Not Larastan. Not a replacement for AST analysis. Not semantic. Not a linter.

Why this matters: AST-based tools are more precise but expensive to set up and tune. Grep-based tools are imprecise but instant and portable. Ship the cheap wall first, let it catch the common drift, and only reach for AST tooling for the semantic patterns grep genuinely can’t see. Skip the cheap wall to go straight to the expensive one and you’ll spend weeks on config and still have hardcoded values in your commits.

The framework

Core principle

Fast by default. Strict where it matters.

Not every change needs ceremony. A blade tweak shouldn’t take 30 minutes. A shared service change should hit multiple walls before it lands.

The four layers

Canon (blessed primitives)→Boundaries (what can talk to what)→Enforcement (machine checks)→Verification (runtime backstop)

Layer 1: Canon. The blessed building blocks. Enums, DTOs, canonical helpers, query builders, service classes, component families, response shapes, event schemas. Rule: if something is used in 3+ places or affects shared behavior, it becomes canon.

Layer 2: Boundaries. What may talk to what. Controllers call actions or services, not raw DB. Blades use approved components. Feature code can’t bypass domain abstractions. Rule: every layer has allowed and forbidden dependencies.

Layer 3: Enforcement. The missing piece in most setups. PHPStan custom rules, pre-commit greps, Pint, architecture tests, CI failure on forbidden patterns, banned imports. Rule: if a rule matters, it must be machine enforced.

Layer 4: Verification. Catches what enforcement misses. Smoke tests, route tests, page load checks, critical flow tests, log checks. Rule: verification is the backstop, not the primary defense.

Change tiers

This is what prevents overkill. Not every change needs the same rigor.

TierScopeExamplesRequirements
Tier 1Local UICopy, blade tweaks, styling, presentational componentsExisting components only. No new abstractions. Smoke test. No ceremony.
Tier 2FeatureNew view logic, endpoints, form handling, data shapingMust use canon. No bypassing service/action layer. Targeted tests. Static checks. Flow smoke test.
Tier 3Shared/coreEvents, auth, billing, tracking, alerting, schema, shared servicesExplicit canon review. Update/create enforcement if introducing new canon. Full static checks. Regression tests. Audit impacted surfaces.

Simple rule: the more shared the blast radius, the less freedom the AI gets.

The non-negotiables

Architectural Non-Negotiables

Do

  • One concept, one primitive. If EventType exists, raw event strings are banned
  • One layer of responsibility. Controllers don’t quietly grow business logic
  • No local convenience if canon exists. AI loves shortcuts, ban them
  • No duplicated “almost same” helpers. This is where drift starts
  • Every important convention must become enforcement. If it only lives in docs, it’s optional

Prompts vs enforcement

I kept conflating these. They do different jobs.

Prompts are for: intent, scope, style, process, caution, preferred path

Enforcement is for: forbidden patterns, architecture boundaries, canonical primitives, banned literals, banned imports, required usage of shared helpers

Why it matters: prompts shape behavior, enforcement controls outcomes. If you want a guarantee, you need enforcement.

The workflow

1
Classify the change. Tier 1, 2, or 3. This alone changes how much freedom the AI gets.
2
Force path discovery first. Before coding, AI must answer: what existing primitive solves this? What layer owns this? What pattern is canonical? What should not be created again?
3
Implement the smallest valid change. Extend before inventing. Reuse before adding. Patch before refactor.
4
Run enforcement. Static analysis, lint, forbidden pattern checks.
5
Run verification. Tier 1: page check. Tier 2: flow + targeted tests. Tier 3: wider audit + regression + logs.
6
Force a post-change summary. What canon was used. What files changed. What was deliberately not created. What checks passed. What risk remains.

Step 6 is underrated. It surfaces the shortcuts you’d otherwise never see.

Metrics that actually matter

Don’t track vanity stuff like “more tests.” Track three things:

  • Drift: hardcoded canon violations caught, banned pattern violations caught in CI, duplicate helpers introduced, legacy bypasses still present.
  • Stability: regressions from AI changes, rollback count, bug count by tier, changes passing checks but failing at runtime.
  • Velocity: time to ship Tier 1, 2, and 3, plus the percent of changes blocked by enforcement.

The target: drift down, regressions down, velocity preserved for Tier 1 and Tier 2.

How to actually implement it (the three-layer split)

The framework above is the what. This is the how. The key insight: you don’t pick one implementation mechanism. You use three, with clearly separated jobs. Trying to make any single layer do all three is exactly how I ended up with a tight system that still drifts.

CLAUDE.md: what matters. The constitution. Short, load-bearing principles.

Skills: how to work. Operating procedures. Thinking modes, workflows, audits.

Machine enforcement: what is allowed. The guardrails. CI, pre-commit, static analysis.

Layer 1, CLAUDE.md, stays narrow. Load-bearing defaults only: architecture first, extend before invent, canon over convenience, don’t bypass shared primitives, identify the existing pattern before coding. If it grows past ~30 lines of principles, it becomes wallpaper and the model stops weighting it. Resist the urge to fix enforcement problems by stuffing more rules into it.

Layer 2, skills, is process enforcement. /think to pause and assess, a /checkfirst-style pattern discovery skill, /audit, /fix, /todo, /verify. Skills improve odds. They shift the average. They cannot prevent a violation. A determined model or a tired orchestrator can skip them, rush them, or read them loosely.

Layer 3, machine enforcement, is the part I didn’t have. Start cheap: pre-commit grep bans on changed files, the same bans in CI on every push, one or two PHPStan custom rules for your most recurring violations, architecture tests later once you know which boundaries matter in practice.

The rules themselves stack in three tiers, and this matters once you run more than one repo. A universal core (debug leftovers, generated-file edits, raw env access) is the same everywhere. Stack packs tune to a framework’s failure modes: thin controllers and no env() in app code for Laravel, no $wpdb for WordPress, content-collection discipline for Astro. Project canon is where the real leverage lives, because it encodes what’s load-bearing about this codebase: your services, your enums, your named routes, your banned legacy paths. Universal rules keep things sane. Stack packs catch stack-specific drift. Project canon catches the bugs that are actually yours. The build tutorial walks through all three.

One more reframe worth naming, because I wasted weeks on it. The goal is not “AI needs to understand the whole codebase.” More context, more memory, bigger windows: wrong abstraction. Even humans don’t hold a whole system in working memory. The job is to make local changes obey global rules by default, which is a constraint problem, not a comprehension problem.

The blunt version

If you only do layers 1 and 2, you will improve behavior but still get drift. I’m living proof. If you add layer 3, you change the system. That’s the whole game.

The shortest version

AI Change Control, Compressed

Do

  • Classify change by risk
  • Discover canon before coding
  • Extend, don’t invent
  • Enforce architecture automatically
  • Verify based on blast radius
  • Report what canon was used
You do not need AI to understand the whole codebase. You need a codebase where understanding the whole codebase is not required to make a correct local change.

Go deeper

The framework is the shape. The three spokes are the detail.


Related:

Ben Peetermans

Ben Peetermans

I build MVPs and run ad campaigns with AI, and I write these notes from real projects. Twenty years shipping for the web. If you want someone who works with these tools every day to build yours, that is what I do.