On this page
  1. The problem: AI generates code in the wrong place
  2. The fix: directory structure as architecture signal
  3. Hierarchy patterns for common architectures
    1. Web API (backend)
    2. Full-stack app
    3. CLI tool
  4. Preventing the monolith: split early
  5. When the AI gets it wrong
  6. Don’t nest too deep
  7. Related topics
how-to

How to Structure Files So AI Agents Don't Break Your Architecture

AI agents generate code in the wrong files, break module boundaries, and turn your project into a monolith. Here's how to structure directories and files so AI agents generate code that fits your architecture.

Quick answer

  • Create the directory structure before asking the AI to write code. Empty folders signal where code belongs.
  • One concept per file, files under 500 lines. Split before the AI loses context.
  • When the AI puts code in the wrong place, move it immediately — the AI learns from your existing structure.
  • Full guide: How to Make Your Codebase AI-Friendly

The problem: AI generates code in the wrong place

You ask the AI to add a feature. It adds the route handler, the database query, and the validation logic — all in one file. Six features later, that file is 1,200 lines and the AI can no longer read it all at once.

The AI doesn’t know your desired architecture unless it’s visible in the directory structure.

The fix: directory structure as architecture signal

Before generating code, create the directories. Even empty ones.

# Create the structure first — then ask the AI to add code
mkdir -p src/api/routes
mkdir -p src/api/middleware
mkdir -p src/domain/services
mkdir -p src/domain/entities
mkdir -p src/infra/repositories
mkdir -p src/infra/clients
mkdir -p tests/unit
mkdir -p tests/integration

Now when you ask the AI “add a user registration endpoint,” it has a destination: src/api/routes/auth.py. Without the structure, it defaults to adding everything to app.py.

Hierarchy patterns for common architectures

Web API (backend)

src/
  api/               # HTTP layer only
    routes/          # One file per resource: users.py, orders.py
    middleware/      # Auth, logging, rate-limiting
    schemas/         # Request/response validation
  domain/            # Business logic (no HTTP, no DB)
    services/        # Orchestration: create_order(), apply_discount()
    entities/        # Domain objects: Order, User
  infra/             # External dependencies
    repositories/    # Database access
    clients/         # External API clients
    config/          # Settings, env vars
tests/
  unit/              # Test domain logic
  integration/       # Test API + DB together

Full-stack app

backend/
  src/
    api/
    domain/
    infra/
frontend/
  src/
    components/      # Reusable UI
    pages/           # Route-level components
    hooks/           # Custom hooks
    services/        # API calls
    utils/           # Pure functions

CLI tool

src/
  commands/          # One file per command
  lib/               # Shared logic
  config/            # Settings
  main.py            # Entry point

Preventing the monolith: split early

When a file grows past 500 lines, split it before asking the AI to add more. The AI will put new code in whatever file is open. If one file is 800 lines, new code goes there — growing it further.

# Before: 800-line orders.py with routes, services, and queries

# After splitting:
# api/routes/orders.py    — 120 lines (routes only)
# domain/services/order.py — 180 lines (business logic only)
# infra/repositories/order.py — 90 lines (database only)

The split tells the AI: “routes go here, business logic goes here, database access goes here.” Future code lands in the right place.

When the AI gets it wrong

When the AI puts code in the wrong file:

  1. Move it immediately. If you leave it, the AI treats that location as the pattern.
  2. Tell the AI where it should have gone. “The order validation logic should be in src/api/schemas/orders.py, not in src/api/routes/orders.py.”
  3. Create the missing file even if it’s empty. The AI needs a target.
# The AI put validation in routes/orders.py
# Create the schemas file so the AI has a destination
touch src/api/schemas/orders.py

# Now tell the AI: "Move the validation logic from routes/orders.py to schemas/orders.py"

Don’t nest too deep

The AI struggles with deeply nested hierarchies — it can’t keep track of paths like src/core/domain/services/order/fulfillment/calculator.py. Keep nesting to three levels maximum:

# Good (3 levels):
src/api/routes/orders.py

# Too deep (6 levels):
src/core/domain/services/order/fulfillment/calculator.py

If you need that depth, factor it out:

src/order/fulfillment/calculator.py

Where this bites vibecoders

The vibecoder asks the AI for features without creating structure. The AI puts everything in app.py. Six months later, app.py is 3,000 lines and the AI generates broken code because it can’t read its own context. Creating the directory structure first — 30 seconds of mkdir — prevents this entirely.


Share: