Architecture Enforcement: Stop Claude from Breaking Your Patterns

Claude is helpful but does not know your architecture rules. Learn how to use structural constraints instead of hoping Claude follows instructions.

Introduction

Claude wants to help. That is simultaneously its greatest strength and a hidden danger for your codebase. When you ask Claude to add a feature, it will find a way to make it work. It will write the code, test it mentally, and deliver something functional. The problem is that "functional" and "architecturally correct" are not the same thing.

Your application has patterns. Services handle business logic. Views handle presentation. Models define data structures. These boundaries exist for reasons that took your team months to establish. Clean architecture is not about being pedantic. It is about maintainability, testability, and the ability to change one part of your system without breaking others.

Claude does not know your architecture rules. It has not attended your architecture review meetings. It has not read the pull request comments explaining why we never put database queries in view controllers. It sees a problem and solves it in the most direct way possible, which often means crossing boundaries that should not be crossed.

This article explains why architectural violations happen, why traditional solutions fail, and how to implement structural enforcement that makes violations impossible rather than merely discouraged.

Common Violations

Before we can prevent violations, we need to recognize them. These are the patterns we see broken most frequently when Claude Code generates new features.

Business Logic in Views

Claude needs to display a filtered list of items. The quickest path is to add the filtering logic directly in the view component. It works. The list filters correctly. But now your view contains business rules that should live in a service. When those rules change, you are editing view code. When you want to reuse that filtering elsewhere, you cannot.

The Violation
// UserListView.swift - Business logic should NOT be here
struct UserListView: View {
    @State private var users: [User] = []

    var activeUsers: [User] {
        // Business rule buried in view
        users.filter { user in
            user.status == .active &&
            user.subscriptionExpiry > Date() &&
            !user.isSuspended
        }
    }
}

UI Code in Services

The inverse problem also occurs. Claude needs a service to show an alert when something fails. Instead of returning an error for the view layer to handle, it imports UIKit into the service and shows the alert directly. Your service layer now depends on your UI framework. Unit testing becomes impossible without mocking the entire UI system.

Wrong Layer Access

Your architecture has layers: presentation, business logic, data access. Each layer should only communicate with adjacent layers. Claude, trying to be efficient, will skip layers. The view calls the repository directly instead of going through a service. The service imports the database driver instead of using the data access layer. These shortcuts work today but create tangled dependencies that are painful to untangle later.

Inconsistent Patterns Across Files

Perhaps the most insidious violation is inconsistency. File A uses one pattern for API calls. File B uses a different pattern because Claude generated it in a different session. Both work. Neither is necessarily wrong in isolation. But your codebase now has two ways of doing the same thing, and future developers must understand both.

The Real Cost: Architecture violations compound. One shortcut leads to another. Within months, your clean architecture becomes a web of cross-cutting dependencies that no one fully understands. Refactoring becomes expensive. New features take longer because developers must navigate the complexity.

Why Claude Breaks Patterns

Understanding why violations happen helps us design better prevention. Claude breaks your patterns for predictable reasons.

No Built-In Enforcement

Claude Code has no mechanism to enforce architectural rules. It can read your files, understand your instructions, and generate code. What it cannot do is refuse to put business logic in a view because your architecture says so. There is no lint rule that fires. No compiler error that blocks. Claude's knowledge of your architecture exists only in its context window, and context is not enforcement.

The Helpfulness Problem

Claude is optimized to be helpful. When you ask for a feature, Claude's goal is to deliver working code. If following your architecture would require creating three new files and Claude can accomplish the task by modifying one existing file, the single-file solution looks more helpful. Less code. Faster delivery. Works correctly. The architectural violation is an invisible cost that Claude does not weigh against the visible benefit of working code.

Shortcut Bias in Training

Large language models learn from code that exists. Much of that code takes shortcuts. Quick prototypes. Deadline-driven features. Code that worked well enough to ship. Claude has seen millions of examples where the expedient solution was chosen over the architecturally correct one. It is not surprising that Claude sometimes reproduces those patterns.

Context Window Limitations

Your architecture exists across hundreds of files. The patterns that define how your application works are distributed throughout the codebase. Claude can only see what fits in its context window. If your architectural rules were stated in the beginning of a long conversation, they may have been compressed or lost. If they exist only in files Claude has not read, they do not exist for Claude at all.

Traditional Solutions (And Why They Fail)

Most teams try to solve this problem with instructions. They document their architecture, explain the patterns to Claude, and hope for compliance. Here is why that approach disappoints.

System Prompts Get Forgotten

You carefully craft a system prompt explaining your architecture. "Services go in /services. Views should not contain business logic. Always use the repository pattern for data access." Claude reads this at the start of the conversation. Forty-five minutes later, after dozens of messages about implementation details, those instructions have faded. They are still technically in context, but their influence has diminished.

Manual Review Does Not Scale

You can catch violations during code review. But this requires that reviewers know your architecture deeply, have time to review every Claude-generated change carefully, and are willing to reject working code because of structural concerns. In practice, working code tends to get approved. "We can refactor it later" becomes the mantra. Later never comes.

Documentation Is Not Enforcement

Beautiful architecture diagrams and detailed ADRs (Architecture Decision Records) are valuable for human developers. They do nothing to prevent Claude from generating code that violates those patterns. Documentation tells Claude what it should do. It does not prevent Claude from doing something else.

The Hope-Based Approach
# System prompt attempt
You are working on a project with strict architecture:
- Views contain ONLY presentation logic
- Business logic goes in Services
- Data access uses Repositories
- Never skip layers

Please follow these patterns...

# Reality: Claude will follow these sometimes,
# forget them other times, and violate them
# whenever the shortcut seems helpful.
Key Insight: The fundamental problem with traditional solutions is that they rely on Claude's compliance. Instructions work when followed. The moment Claude decides a shortcut is more helpful, or the context degrades, or the instruction was not specific enough for this exact situation, violations occur.

Structural Enforcement

What if Claude could not violate your architecture because its role definition made violations impossible? This is the principle behind structural enforcement. Instead of telling Claude what to do and hoping for compliance, you define agents with capabilities that cannot cross boundaries.

Agent Roles with Hard Boundaries

A Frontend Designer agent that literally cannot write JavaScript will never put business logic in a view component. It cannot. The capability does not exist in its role definition. An iOS Developer agent that handles only Swift business logic will never add UI code to a service. These are not suggestions. They are structural constraints.

Structural Role Definition
# Frontend Designer Agent Definition

## Capabilities
- HTML structure and semantics
- CSS styling and animations
- Visual design decisions
- Responsive layouts

## Explicit Exclusions
- NO JavaScript (web-developer handles this)
- NO business logic
- NO data fetching
- NO state management beyond CSS

## Routing Rule
If it's in a .js file or a <script> tag,
it goes to web-developer. ALWAYS.

Separation by Definition, Not Discipline

Traditional approaches require discipline. Someone must remember the rules. Someone must enforce them. Someone must reject PRs that violate them. Structural enforcement removes the need for discipline by making violations structurally impossible. The Frontend Designer cannot write JavaScript because it is a web-developer capability. Period.

Coordination Through a Manager

When you separate capabilities into distinct agents, you need coordination. A Project Manager agent orchestrates work across specialists. It breaks features into pieces that respect boundaries. The PM might decompose "add a filtered list feature" into: (1) Frontend Designer creates the list view HTML/CSS, (2) Web Developer adds the filtering JavaScript, (3) iOS Developer implements the Swift equivalent. Each specialist works within their boundaries.

Coordinated Decomposition
# PM receives: "Add user filtering to dashboard"

# PM decomposes into bounded tasks:

Task 1 -> frontend-designer:
"Create filter UI with dropdown and search input.
HTML/CSS only. No JavaScript."

Task 2 -> web-developer:
"Add filtering logic in dashboard.js.
Filter users by status and search term.
Call UserService.getFilteredUsers()."

Task 3 -> firebase-developer (if needed):
"Add server-side filtering to getUsers function.
Accept status and searchTerm parameters."

Boundary Enforcement Example: Frontend Designer Cannot Write JS

Let us examine one concrete boundary and how it prevents violations.

The Problem It Solves

In traditional setups, a frontend developer might add a click handler, then realize they need some data transformation, then find themselves writing business logic in what started as a simple UI file. The boundary between presentation and logic blurs one helpful shortcut at a time.

The Structural Solution

The Frontend Designer agent handles HTML structure, CSS styling, and visual decisions. It cannot write JavaScript. When it needs interactive behavior, it documents what behavior is needed and hands off to the Web Developer agent. The handoff is not optional. It is the only path forward.

Frontend Designer Output
<!-- filter-panel.html -->
<div class="filter-panel">
  <select id="status-filter" class="filter-select">
    <option value="all">All Users</option>
    <option value="active">Active</option>
    <option value="inactive">Inactive</option>
  </select>
  <input
    type="search"
    id="search-filter"
    class="filter-search"
    placeholder="Search users..."
  >
</div>

<!--
HANDOFF TO WEB-DEVELOPER:
Need event listeners on #status-filter and #search-filter
When either changes, call filtering function
Update user list display with filtered results
-->

Why This Works

The Frontend Designer did its job: clean HTML structure with semantic classes and accessible form elements. It identified the interactive behavior needed but did not implement it. The Web Developer will add the JavaScript in a separate file with a clear responsibility. Business logic stays in services. Views stay presentation-only. The boundary is maintained not because someone remembered it, but because crossing it was not an option.

ECOSYSTEM.md as Contract

Structural enforcement prevents boundary violations. But what about consistency? How do you ensure that the Web Developer and iOS Developer use the same field names? How do you keep API contracts synchronized? Enter ECOSYSTEM.md: your project's single source of truth.

What Belongs in ECOSYSTEM.md

ECOSYSTEM.md defines the contracts that all agents must follow. Data models with exact field names. API endpoints with request and response shapes. Enums and constants that must match across platforms. Naming conventions that cannot vary.

ECOSYSTEM.md
## Data Models

### User
```typescript
interface User {
  id: string;           // Primary key
  email: string;        // Normalized, lowercase
  displayName: string;  // User-visible name
  status: UserStatus;   // See enum below
  createdAt: Timestamp;
  updatedAt: Timestamp;
}

type UserStatus = 'active' | 'inactive' | 'suspended';
```

## Naming Conventions
- Field names: camelCase (never snake_case)
- Timestamps: past tense (createdAt, not createTime)
- Booleans: prefix with is/has/can (isActive, not active)

## API Contracts

### getUsers
Request: { status?: UserStatus, searchTerm?: string }
Response: { users: User[], total: number }

Mandatory Reference Before Work

The Project Manager agent must read ECOSYSTEM.md before delegating work. This is not a suggestion. It is built into the delegation process. The PM cannot delegate a task involving users without first confirming the User model definition. When ECOSYSTEM.md says the field is displayName, that is what every agent uses. No agent decides independently that userName sounds better.

Single Source of Truth

When iOS uses userId and Web uses user_id, you have schema drift. Bugs appear in production because the backend expects one format and the frontend sends another. ECOSYSTEM.md eliminates this by establishing a single authority. Disagreements are resolved by updating the document, not by letting each platform diverge.

Validation Layer

Structural enforcement and ECOSYSTEM.md prevent most violations. But humans make mistakes. Agents occasionally interpret instructions differently than intended. A validation layer catches what prevention misses.

Pre-Commit Validation

Before code is committed, automated checks verify that architectural rules hold. Does any view file contain imports from the service layer? Does any service file import UI frameworks? Are all field names consistent with ECOSYSTEM.md?

validate.sh
#!/bin/bash

# Check for UI imports in service files
if grep -r "import UIKit" services/; then
  echo "ERROR: UIKit imported in service layer"
  exit 1
fi

# Check for business logic patterns in views
if grep -r "Repository\|Service" views/*.swift | grep -v "// Allowed:"; then
  echo "ERROR: Direct service/repo access in views"
  exit 1
fi

# Validate field name consistency
./scripts/check-schema-consistency.sh

echo "Validation passed"

PM Validation After Specialist Work

After each specialist completes their task, the Project Manager validates the work before marking it complete. Does the output match what was requested? Does it follow the patterns in ECOSYSTEM.md? Does it respect the boundaries defined in the role? Validation is part of the workflow, not an afterthought.

Catch Violations Before They Merge

The goal is not perfection. The goal is catching violations before they enter your codebase. A violation caught in validation is cheap to fix. A violation discovered in production after months of compounding is expensive. The validation layer exists to keep the cost of violations low.

Defense in Depth: Structural enforcement makes violations hard. ECOSYSTEM.md makes inconsistency hard. Validation catches what slips through. No single layer is perfect. Together, they create a system where architectural integrity is the default, not the exception.

Conclusion: From Hope to Enforcement

The difference between hoping Claude follows your architecture and enforcing that it does is the difference between a codebase that degrades over time and one that maintains its integrity.

Traditional approaches rely on instructions, documentation, and manual review. They work some of the time. When they fail, violations accumulate. Technical debt compounds. Eventually, the cost of maintaining the system exceeds the cost of rewriting it.

Structural enforcement changes the equation. Agent roles with hard boundaries cannot cross architectural lines. ECOSYSTEM.md establishes contracts that every agent must follow. Validation catches the edge cases before they merge.

You are not asking Claude to be careful. You are making it structurally impossible for Claude to violate your patterns. That is the difference between architecture documentation and architecture enforcement.

Enforce Your Architecture

Claude Architect gives you structural enforcement out of the box. Agent roles with hard boundaries, ECOSYSTEM.md as your contract, and validation that catches violations before they ship. Stop hoping Claude follows your patterns. Start enforcing them.

Join the Waitlist