Agentic Coding: Architecture

Agentic Coding: Architecture - THE LGTM

Agentic Coding: Architecture

Codebases built for AI agents look different. Clear boundaries, explicit conventions, and architectural guardrails let agents move fast without creating chaos. Here's how to design for agent-first development.

Last Updated: April 5, 2026

The Architecture Problem

AI agents excel at local changes. They struggle with:

  • Understanding implicit conventions
  • Maintaining architectural boundaries
  • Recognizing long-term patterns
  • Avoiding coupling and drift

The solution: Make architecture explicit. Remove guesswork. Encode constraints.

Agent-Friendly Architecture Principles

1. Explicit Boundaries

Every module should have clear:

  • Public interface (what's exposed)
  • Dependencies (what it can use)
  • Responsibilities (what it does)
  • Invariants (what must stay true)
// Good: Explicit module contract
/**
 * @module AuthService
 * @description Handles user authentication and session management
 * @dependencies UserRepository, TokenService, EmailService
 * @invariants Sessions expire after 24h, passwords never logged
 */

2. Convention Over Configuration

Agents work best with clear, consistent patterns:

src/
  features/
    user/
      api/           # API routes
      domain/        # Business logic
      infrastructure/ # External adapters
      index.ts       # Public exports
    order/
      api/
      domain/
      infrastructure/
      index.ts

Same structure everywhere. Agent knows where to look, where to add.

3. Dependency Rules as Code

Don't document architecture — enforce it:

// archunit test example
describe("Architecture Rules", () => {
  it("domain should not depend on infrastructure", () => {
    const domain = classes().that().resideInAPackage("domain")
    const infra = classes().that().resideInAPackage("infrastructure")
    
    expect(domain).toNotDependOn(infra)
  })
  
  it("api should only depend on domain", () => {
    const api = classes().that().resideInAPackage("api")
    const allowed = classes().that().resideInAnyPackage("domain", "shared")
    
    expect(api).toOnlyDependOn(allowed)
  })
})

Architecture Documentation for AI

The AGENTS.md Pattern

Inspired by OpenAI's Codex harness, AGENTS.md files tell agents how to work in your codebase:

# Project: E-Commerce Platform

## Architecture Overview
Hexagonal architecture with clear domain/infrastructure separation.

## Tech Stack
- TypeScript, Node.js
- PostgreSQL (database)
- Redis (caching)
- RabbitMQ (events)

## Conventions
- Use functional programming style in domain layer
- All DB access through repository pattern
- API validation with Zod schemas
- Errors use Result pattern, not exceptions

## File Organization
src/
  domain/          # Pure business logic, no deps
  application/     # Use cases, orchestration
  infrastructure/  # DB, HTTP, external APIs
  api/             # Routes, controllers

## Testing
- Unit: Jest, in __tests__ alongside code
- Integration: Supertest, in tests/integration
- Mock external services, never hit real APIs

## Common Tasks
### Adding a new feature
1. Define types in domain/types/
2. Implement logic in domain/services/
3. Add repository method if needed
4. Create API route in api/routes/
5. Write tests for all layers

Per-Module README

Complex modules get their own documentation:

src/features/payment/
├── README.md          # Module-specific guidance
├── ARCHITECTURE.md    # Design decisions, patterns
├── API.md            # Public interface documentation
└── .agent-instructions # Tool-specific hints

Multi-Agent Architecture Patterns

Pattern 1: Supervisor Pattern

One agent orchestrates, delegates to specialists:

Supervisor Agent
  ├── Planning Agent: breaks down tasks
  ├── Frontend Agent: implements UI
  ├── Backend Agent: implements API
  ├── Testing Agent: writes tests
  └── Integration Agent: coordinates PR

Use when: Complex features spanning multiple domains.

Pattern 2: Pipeline Pattern

Agents in sequence, each specializing in one phase:

Requirements Agent → Design Agent → Code Agent → Test Agent → Review Agent

Use when: Formal development process with handoffs.

Pattern 3: Parallel Agents

Multiple agents work simultaneously:

Task: Implement user dashboard

Agent A: Build API endpoints
Agent B: Create React components
Agent C: Write tests
Agent D: Update documentation

Use when: Tasks can be decomposed into independent workstreams.

Architectural Guardrails

Static Analysis

// eslint rules for architecture
{
  "rules": {
    "no-restricted-imports": ["error", {
      "patterns": ["../infrastructure/** from domain/**"]
    }],
    "boundaries/element-types": ["error", {
      "default": "disallow",
      "rules": [
        { "from": "domain", "allow": ["domain"] },
        { "from": "api", "allow": ["domain", "application"] }
      ]
    }]
  }
}

Code Generation Templates

Scaffold new modules correctly:

# Agent uses template to create new feature

def create_feature(name):
  generate(f"src/features/{name}/domain/types.ts")
  generate(f"src/features/{name}/domain/service.ts")
  generate(f"src/features/{name}/infrastructure/repository.ts")
  generate(f"src/features/{name}/api/routes.ts")
  generate(f"src/features/{name}/__tests__/service.test.ts")

Steering Files (Kiro Pattern)

Project-level instructions guide agent behavior:

# .kiro/steering.yaml
architecture:
  pattern: hexagonal
  layers:
    - domain
    - application
    - infrastructure

code_style:
  prefer: functional
  async: promises (not callbacks)
  errors: result type (not exceptions)

testing:
  coverage: 80% minimum
  mocking: required for external deps

Reducing Architectural Drift

Drift Detection

Automated checks for architectural erosion:

  • Circular dependencies: madge, dependency-cruiser
  • Complexity creep: CodeScene, SonarQube
  • Boundary violations: ArchUnit, eslint
  • Dead code: ts-prune, knip

Refactoring Agents

Agents can maintain architecture:

Agent Task: "Refactor auth module to hexagonal"

1. Identify current violations
2. Move domain logic to domain/
3. Extract repository interface
4. Move DB code to infrastructure/
5. Update imports
6. Run architecture tests
7. Verify all tests pass

Technology Choices for Agentic Work

Statically Typed Languages

TypeScript, Rust, Go, Kotlin → compiler catches agent mistakes

Explicit Dependency Injection

Constructor injection → clear dependency graph for agents

Clear Module Boundaries

ESM, packages → explicit exports, no implicit sharing

Standard Tooling

Prettier, ESLint, standard configs → deterministic formatting

Agent-Specific Patterns

1. Prompt-First Development

Write the prompt first, then implementation:

TODO.md:
"Implement user registration with email verification"

SPEC.md:
- Validate email format
- Check for existing users
- Hash password with bcrypt
- Send verification email
- Create session on verification

Agent implements SPEC.md, guided by TODO.md

2. Snapshot Architecture

Save architecture state for reference:

docs/architecture/
  2026-01-15-baseline.md
  2026-02-20-auth-refactor.md
  2026-03-10-payment-module.md

Agents reference these when making changes.

3. Context Windows as Architecture

Structure code so agents can understand:

  • Files < 500 lines (fits in context)
  • Functions with clear single purpose
  • Explicit dependencies (no magic)
  • Good naming (agent understands intent)

The Bottom Line

Architecture for agents is architecture for humans — just more explicit:

  • ✅ Clear boundaries
  • ✅ Documented conventions
  • ✅ Enforced rules
  • ✅ Explicit dependencies
  • ✅ Consistent patterns
  • ✅ Automated checks

Agents can write code fast. Good architecture ensures they write the right code.

Further Reading