Skip to content
Bitloops - Git captures what changed. Bitloops captures why.
HomeAbout usDocsBlog
ResourcesSoftware ArchitectureHow AI-Generated Code Impacts Architecture

How AI-Generated Code Impacts Architecture

Agents solve problems fast; they don't respect boundaries. Without explicit architectural constraints, AI generates code that works locally but violates boundaries globally. Architecture matters more with AI, not less. Learn what agents break and how to protect against it.

9 min readUpdated March 4, 2026Software Architecture

Definition

AI-generated code is code written by an AI agent (like Claude, GPT, or purpose-built coding assistants) based on a prompt or context. It's faster to generate than humans write, but it doesn't inherently respect architectural boundaries.

The problem: an AI agent might generate code that works locally but violates architecture globally. A use case that imports a controller. A repository that calls another service's database. An entity that depends on an external API. These are easy mistakes for an agent to make, hard for humans to catch at scale.

Architecture becomes not just a design concern but a safety concern when AI agents are involved.

How AI Violates Architecture

The Boundary Violation Problem

An AI agent is asked: "Create a function that lists all users and their recent orders."

Without architectural context, the agent might generate:

// BAD: Violates service boundaries
class UserController {
  async listUsersWithOrders(req: Request, res: Response): Promise<void> {
    // User Service logic (OK)
    const users = await this.userRepository.findAll();

    // Order Service logic (WRONG - calling another service's data layer)
    for (const user of users) {
      user.recentOrders = await ordersDatabase.query(
        'SELECT * FROM orders WHERE user_id = $1 LIMIT 10',
        [user.id]
      );
    }

    res.json(users);
  }
}
javascript

The agent solved the problem: it returns users with their orders. But it violated architecture: User Service accessed Orders Service's database directly. Now:

  • User Service depends on Orders database schema
  • Orders Service can't change schema without coordinating with User Service
  • Data ownership is violated
  • The services aren't independent

The agent did exactly what it was asked—it didn't know about the architectural rule: "Services own their data."

The Coupling Problem

Another example:

// BAD: Domain object couples to infrastructure
class Order {
  private id: string;
  private customerId: string;
  private items: OrderItem[];

  // WRONG: Entity depends on external service
  async charge(): Promise<void> {
    const stripe = require('stripe')(process.env.STRIPE_KEY);
    const charge = await stripe.charges.create({...});
  }
}
typescript

The agent created a working order charging function. But it embedded the charging logic in the entity, coupling the domain to Stripe. Now:

  • The entity knows about Stripe (infrastructure detail)
  • Testing requires Stripe
  • Switching payment providers requires changing the entity
  • The domain isn't reusable

The agent violated the dependency rule: entities should never depend on external systems.

The Architectural Drift Problem

With AI generation, violations compound. Team A uses an agent to build Feature X. The agent shortcuts architecture. The code works. It ships.

Team B builds Feature Y using the same pattern. Now there are two violations.

Team C follows the same pattern. Three violations.

The architecture isn't updated—it just drifts. What was a violation becomes the norm. Within months, the architecture is unrecognizable.

This is much faster than human-driven drift. Humans might debate before violating architecture. AI agents generate and ship quickly. Drift accelerates.

Why Traditional Reviews Aren't Enough

Architectural review in code review is slow and unreliable:

Slow: A pull request with architectural violations might touch 5 files across 3 modules. A reviewer needs to understand the architecture, recognize the violation, and reject the PR. This takes time. With AI agents generating 10 PRs a day, review becomes a bottleneck.

Unreliable: Reviewers get tired. They miss subtle violations. A function that calls another module's repository might look innocent on its own. But in context, it's a violation.

Inconsistent: Different reviewers have different standards. One accepts a shortcut, another rejects the same pattern in a different PR. Rules aren't enforced consistently.

Fragile: As code grows, keeping architectural violations in mind becomes harder. New developers don't know the rules. Violations slip through.

Manual review is necessary but insufficient when AI generation speed exceeds human review capacity.

The Solution: Making Architecture Explicit and Enforceable

Architecture must be made explicit in code, not just documented. Then tools enforce it. This is especially critical in AI-native development workflows where code generation speed can outpace human review.

1. Explicit Boundaries

Define modules and their boundaries clearly:

// Module definition: Users Service
export interface IUsersModule {
  // Public API only
  getUser(id: string): Promise<UserDTO>;
  createUser(data: CreateUserRequest): Promise<UserDTO>;
  updateUser(id: string, data: UpdateUserRequest): Promise<void>;

  // NEVER expose the repository
  // repository is private to the module
}

export const usersModule: IUsersModule = {
  getUser: async (id: string) => { /* ... */ },
  createUser: async (data: CreateUserRequest) => { /* ... */ },
  updateUser: async (id: string, data: UpdateUserRequest) => { /* ... */ }
};

// Module definition: Orders Service
export interface IOrdersModule {
  getOrder(id: string): Promise<OrderDTO>;
  createOrder(data: CreateOrderRequest): Promise<OrderDTO>;

  // To get user data, call the Users module API
  // Never access users_db directly
}
typescript

Now the boundary is explicit. An AI agent might still generate code that violates it, but the structure makes it obvious.

2. Structural Tools: AST-Based Validation

Tools like Bitloops can parse code (AST - Abstract Syntax Tree) and enforce rules:

Flow diagram

Rule: "Orders module cannot import from users database"
Scans all imports in orders/**
If any import matches /users\/database|users_db|pg.query.*users/, FAIL
Provides feedback to AI agent

Bitloops lets you define rules:

architectureRules:
  - module: orders
    cannot_import_from:
      - users/database
      - payments/database
    can_import_from:
      - users/api
      - payments/api
      - shared/domain

  - module: domain
    cannot_import_from:
      - /http
      - /database
      - /external

  - layer: entity
    cannot_depend_on:
      - /api
      - /service
      - external services
YAML

When an AI agent generates code, Bitloops checks the output against these rules:

Generated:  import { usersDb } from 'users/database';
Rule:       orders/cannot_import_from users/database
Result:     ✗ VIOLATION - import rejected
Feedback:   "To get user data, use usersModule.getUser() instead"
Text

This is fast, consistent, and scalable. Enforcement happens automatically.

3. Explicit Contracts

Define service contracts precisely:

// Contract: Users Service API
export interface UsersAPI {
  getUser(id: string): Promise<UserDTO>;
  createUser(data: CreateUserRequest): Promise<UserDTO>;
  // Response structure is explicit
}

export type UserDTO = {
  id: string;
  email: string;
  name: string;
  status: 'active' | 'suspended';
  // No database internals exposed
};

// Contract: Orders Service can call Users Service
export class OrderService {
  constructor(
    private usersAPI: UsersAPI,  // Explicit dependency on contract
    private orderRepository: OrderRepository
  ) {}

  async createOrder(data: CreateOrderRequest): Promise<Order> {
    // Can only call usersAPI methods
    const user = await this.usersAPI.getUser(data.customerId);
    // Knows the exact response structure
    if (!user) throw new Error('User not found');
    // ...
  }
}
typescript

The contract is precise. An AI agent sees exactly what's available and what the response looks like. Violations are explicit and catchable.

4. Constraint Validation in Generation

The best approach is to validate constraints during generation, not after.

When an AI agent is asked to generate code, provide:

  1. Context: Here's the architecture. Here are the module boundaries.
  2. Constraints: You can call these APIs. You cannot access these databases. These rules are non-negotiable.
  3. Examples: Here's how other code in this module is structured.
Prompt to AI:
"Generate a use case for listing user orders.

Architecture:
- Users Service owns user data (users table, users API)
- Orders Service owns order data (orders table, orders API)
- To get user data, call UsersAPI.getUser()
- Do NOT call users database directly

You can use:
- usersAPI.getUser(userId): Promise<UserDTO>
- ordersRepository.findByUserId(userId): Promise<Order[]>

You cannot use:
- usersDatabase (not accessible)
- Any other service's repository

Generate code that respects these constraints."
typescript

With precise constraints, the agent is more likely to generate compliant code. And when it doesn't, it fails obviously (tries to call usersDatabase, which doesn't exist).

The Bitloops Angle

Bitloops provides structural tools for this:

  1. Dependency graph visualization. Shows what imports what, making violations obvious.
  2. AST-based constraint checking. Scans generated code against architectural rules before it's merged.
  3. Context retrieval. Provides AI agents with architectural context, rules, and examples, improving generation quality.
  4. Constraint definition. Simple syntax to define what's allowed and what's not.

When an AI agent generates code through a system using Bitloops, violations are caught immediately, not in code review weeks later.

Architectural Patterns for AI-Safe Code

Some architectural patterns are safer with AI:

Ports and adapters. Explicit contracts (ports) make it clear what an agent can depend on. Violations are obvious. See Hexagonal Architecture for detailed patterns.

Domain-driven design. Rich domain models with clear boundaries. Agents understand the ubiquitous language and respect domain rules.

Explicit module APIs. Only expose what's public. Everything else is inaccessible, so agents can't violate it accidentally.

Strong typing. TypeScript, not JavaScript. An agent can't call a method that doesn't exist. Type errors catch violations.

Less safe patterns:

Layered architecture without boundaries. Loose structure, implicit rules. Agents can violate boundaries easily.

Monolith without module separation. Everything is accessible. No enforcement. Drift happens fast.

Implicit contracts. "Services are supposed to use the API, not the database." But nothing enforces it. Agents don't know.

Practical Implementation

If you're using AI for code generation:

  1. Document architecture explicitly. Not just in design docs—in code structure and rules.
  2. Use structural tools. Add Bitloops or similar to your pipeline. Check generated code against rules automatically.
  3. Provide context. When calling an AI agent, include architectural rules, examples, and constraints in the prompt.
  4. Start strict. Forbid violations initially. Relax rules only when justified.
  5. Maintain a pattern library. Examples of correctly-implemented patterns. When generating similar code, reference the examples.
  6. Review strategically. Architectural violations (caught by tools). Logical errors (human review). Separation of concerns.
  7. Iterate on prompts. If an AI keeps generating violations, improve the prompt. Add constraints. Provide better examples.

The Broader Picture

AI generation forces architecture to mature. When everything was hand-written, we could be sloppy about boundaries. We can't afford that anymore.

This is actually good. It pushes teams toward:

  • Explicit boundaries. Instead of implicit, documented rules
  • Structural enforcement. Instead of hoping reviewers catch things
  • Clear contracts. Instead of vague expectations
  • Precise architecture. Instead of loose patterns

These are things we should have been doing anyway. AI forces the issue.

FAQ

Does AI-generated code automatically violate architecture?

Not always. With clear constraints and context, agents generate compliant code. The risk is when architecture is implicit or poorly defined.

Should I even use AI for architecture-critical code?

Yes, but carefully. Use AI for well-bounded code (pure functions, isolated modules). For cross-cutting architectural concerns, use AI to draft, then review carefully.

How do I catch AI violations in code review?

Use tools, not humans. Architectural rules should be enforced by static analysis (Bitloops, custom linters, type checking). Human review for logic, tools for structure.

Can I use AI to define architecture?

Cautiously. AI can help brainstorm options and structure. But architectural decisions involve tradeoffs and organizational constraints that require human judgment.

What if I don't have architectural tools?

Start with linting rules and type checking. TypeScript can catch many violations. Add custom rules. Make architectural violations compile-time errors where possible.

How do I ensure architectural decisions are followed?

Document them (ADRs). Implement them as rules (linting, type checking, Bitloops). Review them quarterly. When constraints change, update the architecture.

Primary Sources

Get Started with Bitloops.

Apply what you learn in these hubs to real AI-assisted delivery workflows with shared context, traceable reasoning, and architecture-aware engineering practices.

curl -sSL https://bitloops.com/install.sh | bash