---
title: Enforcing Architecture Decisions at the Merge Gate | Autter Blog
description: How autter automatically enforces architectural boundaries, dependency rules, and design patterns — so your ADRs don't become wishful thinking.
url: https://autter.dev/blog/enforcing-architecture-decisions
date: 2026-03-18
author: Sagnik
tags: Code Quality, Architecture, Code Review, Governance
reading_time: 5 min
site: Autter - Autter is the assurance layer for the AI coding era: it reviews code, tests product impact, checks security, governs releases, and closes the loop from production failure to verified fix.
---

[← All posts](https://autter.dev/blog)

Mar 18, 2026

# Enforcing Architecture Decisions at the Merge Gate

How autter automatically enforces architectural boundaries, dependency rules, and design patterns — so your ADRs don't become wishful thinking.

Sagnik, Founder, autter.dev 5 min read

- Code Quality
- Architecture
- Code Review
- Governance

Your team spent two weeks designing the new module boundary. You wrote an Architecture Decision Record. You presented it at the engineering all-hands. Everyone nodded. And three sprints later, there are seventeen imports that cross the boundary you defined, four direct database calls from the API layer, and a circular dependency that's going to cost a week to untangle.

Architecture decisions don't fail because people disagree with them. They fail because nobody enforces them at the point where code is actually written.

## The enforcement gap

Most teams document architectural decisions in one of two ways: ADRs (Architecture Decision Records) stored in a docs folder, or tribal knowledge shared in Slack threads and design reviews. Both approaches have the same fundamental problem — they rely on human reviewers remembering the rules and catching violations during review.

This worked when teams were smaller, codebases were simpler, and every PR was written by a human who attended the design review. It breaks down when:

- **AI coding assistants generate code** that has never been exposed to your architectural decisions
- **New team members** haven't internalised the unwritten rules yet
- **Review fatigue** causes experienced reviewers to miss violations in large PRs
- **Cross-team PRs** touch modules whose architectural constraints the reviewer doesn't know

## How autter enforces architecture

autter lets you encode architectural decisions as machine-enforceable rules that are checked on every pull request, automatically.

### Module boundary enforcement

Define which modules can import from which, and autter will block PRs that violate the boundaries:

```
# autter.config.yml
architecture:
  boundaries:
    # API layer can import from services, not directly from database
    - module: "src/api/**"
      can_import:
        - "src/services/**"
        - "src/types/**"
        - "src/utils/**"
      cannot_import:
        - "src/database/**"
        - "src/infrastructure/**"

    # Services can import from database, not from API
    - module: "src/services/**"
      can_import:
        - "src/database/**"
        - "src/types/**"
        - "src/utils/**"
      cannot_import:
        - "src/api/**"

    # Shared types must not import from any layer
    - module: "src/types/**"
      can_import:
        - "src/types/**"
      cannot_import:
        - "src/api/**"
        - "src/services/**"
        - "src/database/**"
```

When an AI assistant generates code that imports the database client directly in an API route, autter catches it immediately:

```
// src/api/routes/users.ts
// autter: ARCHITECTURE — direct database import violates layer boundary
// This module (src/api/**) cannot import from src/database/**
// Suggested: import from src/services/user-service instead
import { db } from "../../database/client";

export async function getUser(id: string) {
  return db.users.findUnique({ where: { id } });
}
```

### Dependency direction rules

Beyond module boundaries, autter enforces dependency direction — ensuring your dependency graph flows in one direction and doesn't develop cycles:

| Rule | What it prevents |
| --- | --- |
| No circular dependencies | Module A imports B, B imports A |
| Layer direction | Presentation → Business → Data, never reversed |
| Package isolation | Feature packages don't cross-import |
| Shared kernel restriction | Only approved types in the shared layer |

### Pattern enforcement

Some architectural decisions aren't about *what* you import but *how* you write code. autter can enforce structural patterns:

```
architecture:
  patterns:
    # All API routes must use the standard error handler
    - name: "api-error-handling"
      match: "src/api/routes/**/*.ts"
      require:
        - pattern: "withErrorHandler"
          message: "All API routes must be wrapped in withErrorHandler()"

    # Database queries must go through the repository pattern
    - name: "repository-pattern"
      match: "src/services/**/*.ts"
      forbid:
        - pattern: "db\.(select|insert|update|delete)"
          message: "Services must use repository methods, not direct DB queries"

    # All new events must include a schema version
    - name: "event-versioning"
      match: "src/events/**/*.ts"
      require:
        - pattern: "schemaVersion"
          message: "All event types must include a schemaVersion field"
```

### ADR-linked rules

autter lets you link rules to the ADR that motivated them. When a violation is flagged, the developer sees not just *what* they did wrong, but *why* the rule exists:

```
architecture:
  boundaries:
    - module: "src/api/**"
      cannot_import:
        - "src/database/**"
      adr: "docs/adr/003-layered-architecture.md"
      rationale: >
        Direct database access from API routes bypasses business logic
        validation. This caused incident INC-2026-041 where an API route
        updated user data without triggering the audit log.
```

The PR comment includes:

> **Architecture violation:** Direct import from `src/database/client` in API layer.
> 
> **Why this rule exists:** Direct database access from API routes bypasses business logic validation. This caused incident INC-2026-041. See [ADR-003](docs/adr/003-layered-architecture.md) for details.
> 
> **Suggested fix:** Import `UserService` from `src/services/user-service` and call `userService.getById(id)`.

## The difference enforcement makes

Teams that enforce architecture at the merge gate report fundamentally different outcomes than teams that rely on documentation alone:

| Metric | Documentation only | autter enforcement |
| --- | --- | --- |
| Boundary violations per sprint | 12-20 | 0-2 |
| Time spent on architecture review | ~8 hrs/week | ~1 hr/week |
| Months before major refactor needed | 6-9 months | 18+ months |
| New developer time to compliance | 4-6 weeks | Immediate |

The most significant impact is on AI-generated code. AI assistants have no awareness of your architectural decisions. Without enforcement at the merge gate, they will violate your boundaries in every PR — not out of malice, but out of ignorance.

## Getting started

```
# Generate an initial architecture config from your codebase
npx autter architecture init

# Scan existing code for boundary violations
npx autter architecture check --report

# Enable enforcement on PRs
npx autter architecture enforce
```

autter can generate an initial boundary configuration by analysing your existing import graph — showing you where boundaries already exist naturally and where they're being violated. Start with what you have, tighten as you go.

### See Autter on your own code.

A 30-minute walkthrough with the founders. Bring a repo and watch Autter review a live pull request.

[Book a demo](https://autter.dev/contact)

## Put an assurance layer on every release.

Autter reviews every pull request before it merges, backed by humans. Connect a repo and see your first findings today.

[Get started](https://app.autter.dev/login)

## Keep reading

[Mar 15, 2026 · 4 min read Measuring Code Quality in the Age of AI-Assisted Development Use autter's analytics to track how AI-generated code impacts your team's quality metrics, review velocity, and production stability.](https://autter.dev/blog/team-code-quality-metrics) [Mar 19, 2026 · 4 min read We were called Shipgate. Shipgate made sense on paper, but it never made people feel anything. Then I drew an otter at a harbour gate, and the product (and the name) snapped into focus.](https://autter.dev/blog/we-were-called-shipgate)
