You ask Claude Code to add a new feature. The PR is functionally fine but architecturally off: it extracted three internal helpers into a “shared service,” introduced a dependency-injection container nobody asked for, made everything a class when the codebase uses functions, or proposes splitting the new feature into its own microservice when your stack is deliberately a single Astro app.
Architecture mismatch happens when Claude applies generic “best practices” against deliberate choices your team made. The fix is making conventions inevitable — codifying them with “do this / don’t do that” pairs anchored to real files, so generic priors lose to specific examples.
Common causes
Ordered by hit rate, highest first.
1. CLAUDE.md missing or vague on architecture
Without explicit rules (“we are monolithic by choice; do not propose splits”), Claude defaults to “what looks professional in training data” — which often means microservices, dependency injection, repository patterns, etc.
How to spot it: grep -i "architecture\|monolith\|microservice\|pattern" CLAUDE.md returns nothing. Architecture is implicit, not codified.
2. Training prior stronger than your conventions
Claude saw 100,000 examples of “extract this into a class with DI” during training. Your project’s “functions only” preference appears in 0 examples it’s seen. Without explicit override, the prior wins.
How to spot it: Claude’s suggestion mirrors a popular framework pattern (NestJS DI, Spring beans). You’re getting framework defaults, not your code’s defaults.
3. Project has mixed patterns; agent picks the one it knows best
Half your codebase uses Redux, half uses Zustand (migration in progress). Claude sees both, doesn’t know which is canonical, defaults to Redux because it’s more common in training data.
How to spot it: Suggestion uses the older / more popular pattern, ignoring the newer / project-canonical one. Migration ambiguity.
4. CLAUDE.md has abstract rules without examples
“Prefer composition over inheritance” — fine principle, useless to Claude without a concrete example to copy from. Abstract rules don’t transfer; concrete examples do.
How to spot it: CLAUDE.md rules are statements without file pointers. Add a “canonical example: path/to/file.ts” for each.
5. The new task implicitly asks for a new pattern
“Build a billing service” — the word “service” cues service-oriented design. “Add billing functionality to src/features/” cues following existing feature structure.
How to spot it: Your prompt language echoes a framework’s vocabulary (“service”, “controller”, “repository”) that your project doesn’t use.
6. CLAUDE.md is stale
The architecture used to be different. CLAUDE.md still describes the old approach. Claude follows CLAUDE.md, produces code that contradicts the new (real) architecture, you blame Claude.
How to spot it: CLAUDE.md was last updated 6+ months ago and the architecture has evolved since. Stale doc is lying to Claude.
Shortest path to fix
Ordered by ROI. Steps 1–3 lock in the right pattern at the durable layer.
Step 1: Write explicit “we use” / “we don’t use” rules
In CLAUDE.md:
## Architecture
We use:
- Single Astro app (monolith); deploy as one unit
- Functions over classes (no class-based services)
- Direct DB queries via Drizzle (no repository pattern, no ORM abstractions)
- Local component state + URL state (no Redux/Zustand global store)
- Server actions for mutations (no API routes for first-party calls)
- Vitest for tests (no Jest)
We don't use:
- Microservices — single-process Astro is intentional
- Dependency injection containers
- Repository / unit-of-work patterns
- GraphQL — REST + server actions only
- Class-based components — functional only
If you find yourself proposing any of the "don't use" patterns, stop and ask first.
Explicit don’ts are as important as dos — they override training priors.
Step 2: Pair every rule with a canonical example
Abstract rules slide off; concrete examples stick:
## Canonical examples
- Feature module: src/features/auth/
- index.ts (public API)
- actions.ts (server actions)
- components/ (UI)
- Database access: src/db/queries/users.ts (direct Drizzle, no repo)
- Component state: src/features/billing/components/CheckoutForm.tsx (useState + URL params)
Claude reads these files and copies their shape — beating abstract priors.
Step 3: In the task, name the canonical example explicitly
Add the billing feature.
Architecture: follow `src/features/auth/` exactly — same file structure, same patterns.
DO NOT propose:
- Extracting anything into a separate service
- Adding a dependency injection layer
- Converting functions to classes
- A global state store
If the task seems to need any of these, stop and ask before changing direction.
The “stop and ask” clause is the safety valve — Claude can flag genuine architecture needs without unilaterally introducing them.
Step 4: Reject architecture-drift PRs at review
When Claude proposes a divergent design, push back instead of accepting:
Your PR introduces a `BillingService` class with DI.
This contradicts `src/features/auth/` and CLAUDE.md.
Refactor to:
- Functions in `src/features/billing/actions.ts`
- No service class
- No DI
Then resubmit.
Each rejection trains the session for the rest of the run.
Step 5: Audit CLAUDE.md against current code quarterly
Stale docs lie to Claude. Schedule a quarterly refresh:
# When did CLAUDE.md last update vs major architecture changes?
git log -1 --format="%ai" CLAUDE.md
git log --since="6 months ago" --oneline -- src/features/ | head
If lots of feature work happened since the CLAUDE.md update, the doc may not reflect current patterns. Refresh it.
Step 6: Avoid framework-vocabulary in prompts
Words shape design. Compare:
Bad: "Build a billing service with a controller and repository."
→ Claude builds NestJS-style code.
Good: "Add billing functionality to src/features/billing/.
Follow src/features/auth/ patterns."
→ Claude follows your conventions.
When you must use framework terms (“API endpoint”), pair them with your repo’s specific shape (“API endpoint = Astro server action in actions.ts”).
Prevention
- CLAUDE.md has explicit “we use” + “we don’t use” lists for architecture, refreshed quarterly
- Every architectural rule pairs with a canonical example file Claude can read and copy
- In task prompts, point to the canonical and explicitly forbid common divergent patterns
- Resolve in-repo pattern inconsistencies (declare a winner, migrate the other) so Claude can’t pick wrong
- Avoid framework-loaded vocabulary in prompts; let your example files set the shape
- Treat architecture-drift PRs as documentation gaps to fix in CLAUDE.md, not just one-off rejections