LLM-говнокод и deptrac: как спасти Symfony-архитектуру | AiManual
AiManual Logo Ai / Manual.
11 Июл 2026 Гайд

Как избежать LLM-говнокода: архитектурные практики и deptrac для Symfony-проектов

LLM генерирует удобный код, но нарушает слои. Узнайте, как deptrac и контракты держат архитектуру в рамках. Пошаговый гайд для Symfony + CI.

Когда LLM-агент превращает ваш Symfony-проект в свалку зависимостей

Вы даете задачу нейросети: «Сделай CRUD для пользователей с DDD». Через минуту получаете код, который работает. Контроллер дергает репозиторий, репозиторий ходит в ORM, а в сервисе лежит вызов внешнего API. Запускаете тесты — зеленые. Но через месяц вы не можете понять, почему изменение в UserService ломает отправку email. Это и есть LLM-говнокод: внешне рабочий, но архитектурно разлагающийся со скоростью гниющих бананов.

Проблема не в том, что LLM плохо пишет код. В 2026 году модели вроде GPT-5 или Claude 4 Opus генерируют синтаксически безупречный PHP. Проблема в нарушении границ. Нейросеть не видит архитектурных контрактов вашего проекта. Она оптимизирует под «работает быстрее» или «проще написать», а не под «соответствует слоистой архитектуре». Если вы не зафиксируете эти границы в коде и CI, любой AI-ассистент будет превращать ваш код в лапшу.

В этой статье я покажу, как deptrac в паре с субагентом-ревьювером превращает LLM из халявщика в дисциплинированного инженера. Мы пройдем путь от хаоса к порядку: настроим слои, пропишем правила и заставим AI-агентов подчиняться архитектуре. Без соплей, только боевые конфиги.

Актуальность на июль 2026: Все примеры проверены на PHP 8.4, Symfony 7.2, deptrac 2.1. Используем Composer 2.7 и GitHub Actions c Ubuntu 24.04 LTS. LLM-модели — GPT-5, Claude 4 Opus, Gemini 2 Ultra.

«Но ведь тесты проходят!» — иллюзия безопасности

Вы наверняка сталкивались: CI зеленый, code coverage 90%, но каждый новый PR увеличивает связность. Контроллер начинает импортировать классы из Infrastructure, сервисы — из Presentation. Это не баг тестов — это отсутствие архитектурных проверок. LLM не знает, что ваш Application слой не должен видеть Doctrine. Ей все равно. Она просто выполняет промпт.

Я как-то получил от Claude 4 код, который в UseCase-классе дергал $this->entityManager->flush(). Формально — работает. Фактически — пробивает доменную стену. И это не единичный случай. Модели понимают цель, но игнорируют архитектурные ограничения, если их не подкрепить автоматикой.

Лекарство: архитектурные контракты + субагент-ревьювер

Решение состоит из двух частей:

  • Статический анализ зависимостей — deptrac проверяет, что слои не нарушают правила.
  • AI-ревьювер в пайплайне — субагент (GPT-5, Claude или локальная модель) анализирует код на соответствие deptrac-конфигу до коммита.

Вместе они образуют архитектурный контракт: LLM может писать любой код, но если он нарушает границы — CI его отклонит. А ревьювер еще и объяснит, что именно пошло не так, чтобы разработчик (или агент) мог исправить.

Кстати, проектирование архитектуры под ИИ-кодирование — тема отдельная, но здесь мы фокусируемся на защите уже существующей.

Пошаговый план: от нуля до железобетонного контракта

1 Определите слои и правила до того, как LLM напишет хоть строчку

Без четкой карты слоев deptrac бесполезен. Каноничная слоистая архитектура для DDD-проекта:

  • Domain — сущности, Value Objects, события. Не зависит ни от чего, кроме PHP.
  • Application — UseCases, DTO, интерфейсы репозиториев. Может зависеть от Domain.
  • Infrastructure — Doctrine, репозитории, внешние API. Зависит от Application и Domain.
  • Presentation — контроллеры, формы, шаблоны. Зависит от Application.

Типичная ошибка: слой Shared или Common. Его часто делают «свалкой всего». Лучше разделить на отдельные модули с четкими границами.

2 Настройте deptrac — пример конфига для Symfony 7.2

Установка: composer require --dev deptrac/deptrac:^2.1. Создайте depfile.yaml в корне проекта:

# depfile.yaml
paths:
  - ./src
  - ./tests
exclude_files:
  - .*\.test\..*
layers:
  - name: Domain
    collectors:
      - type: directory
        value: src/Domain/.*
  - name: Application
    collectors:
      - type: directory
        value: src/Application/.*
  - name: Infrastructure
    collectors:
      - type: directory
        value: src/Infrastructure/.*
  - name: Presentation
    collectors:
      - type: directory
        value: src/Presentation/.*
ruleset:
  Domain:
  Application:
    - Domain
  Infrastructure:
    - Application
    - Domain
  Presentation:
    - Application
skipped_violations:
  # Иногда тестам можно больше
  Infrastructure\Tests:
    - Domain

Проверка: php vendor/bin/deptrac analyze. Если есть нарушение — deptrac упадет с ошибкой и покажет, какой класс откуда импортирует недопустимое.

💡
В deptrac 2.1 появилась поддержка контрактов на уровне трейтов и улучшенный парсер PHP 8.4 (атрибуты, property hooks). Если мигрируете с 1.x — проверьте гайд по обновлению.

3 Интегрируйте deptrac в CI/CD — никаких PR без проверки слоев

Пример workflow для GitHub Actions (используйте актуальную версию PHP и deptrac):

# .github/workflows/architecture.yml
name: Architecture Check
on:
  pull_request:
    branches: [main, develop]
jobs:
  deptrac:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          tools: composer:v2
      - run: composer install --no-interaction --prefer-dist
      - run: php vendor/bin/deptrac analyze --no-cache

Теперь каждый PR, нарушающий слои, будет помечен как неудачный. LLM-агент не сможет протащить код, который дергает EntityManager из контроллера, если в deptrac это запрещено.

4 Создайте субагента-ревьювера, который проверяет deptrac до коммита

Идея: запускаем агента (GPT-5 или Claude 4), который анализирует код из PR и сверяет его с depfile.yaml. Если LLM-генерацию нужно остановить до CI — используем pre-commit hook. Но более эффективно — подключать ревьювера как отдельный этап, который объясняет нарушения.

Пример скрипта (Python 3.12), который запускает deptrac и парсит вывод:

import subprocess
import json
from openai import OpenAI

def check_architecture():
    result = subprocess.run(
        ['php', 'vendor/bin/deptrac', 'analyze', '--format=json'],
        capture_output=True,
        text=True
    )
    return json.loads(result.stdout)

def review_violations(violations):
    client = OpenAI(api_key="sk-...", base_url="https://api.gpt-5.example")  # гипотетический эндпоинт
    for v in violations:
        prompt = f"""
        В Symfony-проекте обнаружено нарушение слоя:
        Класс {v['class']} импортирует {v['dependency']}, что запрещено правилами deptrac.
        Объясни кратко, почему это плохо и как исправить. Не предлагай конкретный код, только направление.
        """
        response = client.chat.completions.create(
            model="gpt-5-turbo",
            messages=[{"role": "user", "content": prompt}]
        )
        print(response.choices[0].message.content)

if __name__ == "__main__":
    data = check_architecture()
    if data['violations']:
        review_violations(data['violations'])
        exit(1)

📌 Важно: Не давайте агенту писать код за вас — его задача объяснить почему нарушение плохо. Это учит и человека, и AI генерировать более чистые решения. Про то, как организовать такие чаты с LLM, я писал в статье «Процесс разработки с LLM: как организовать чаты и контекст».

5 Включите deptrac в контекст каждого промпта для LLM

Самый мощный прием: передавайте актуальный depfile.yaml как часть системного сообщения. Для инструментов вроде Awesome Claude Code это тривиально — добавьте правило читать конфиг. Для GPT — вставьте содержимое файла в промпт. Пример:

Ты пишешь код для Symfony 7.2. Обязательно соблюдай архитектурные слои, описанные в этом файле:
```yaml
layers:
  - name: Domain
  ...
ruleset:
  Application: [Domain]
  Infrastructure: [Application, Domain]
  Presentation: [Application]
```
Не нарушай эти зависимости. Если сомневаешься — спроси.

Теперь LLM будет генерировать код, который почти гарантированно проходит deptrac. Это снижает количество правок в разы. Стратум-методология рекомендует использовать такой подход для детерминированности AI.

Типичные ошибки и как их избежать

  • Слишком строгие правила. Например, запрет Presentation -> Infrastructure напрямую — хорош, но если в коде есть простой вызов Twig::render из контроллера, это нормально. Используйте skipped_violations или создайте исключения для фасадов.
  • Игнорирование тестовых слоев. В тестах часто нужно подмешивать Infrastructure (например, Fixtures). Выделите отдельный слой Tests и разрешите ему все — иначе будете тратить время на правки легитимных тестов.
  • Не обновляете deptrac при рефакторинге. Как только вы переименовали папку или слой — сразу правите depfile.yaml. Иначе LLM начнет генерировать код в старую структуру. Зеленый CI и пустая архитектура — статья о том, почему это критично.
  • Не включаете deptrac в контекст self-hosted LLM. Если используете локальную модель, обязательно упакуйте конфиг в промпт. Иначе она будет игнорировать архитектуру. Про развертывание локальных LLM читайте здесь.
Ошибка Последствие Решение
Отсутствие тестовых слоевВечно красный CIДобавить слой Tests
Запрет на любой прямой вызовНельзя использовать фасадыИспользовать skipped_violations
Не обновлять depfile.yamlLLM генерирует в пустотуGitHook на коммит в src/

Неочевидный совет: используйте deptrac как источник истины для LLM-промптов

Вы когда-нибудь задумывались, что depfile.yaml — это формальный язык описания архитектуры? Подавайте его LLM не статично, а динамически. В CI генерируйте SLO (Service Level Objectives) на основе deptrac: если количество нарушений растет — откатывайте PR. Или создайте агента, который при каждом изменении depfile.yaml обновляет промпты во всех чатах. Деградация контекста побеждается актуальностью — и deptrac тут лучший друг.

И последнее. Не верьте в «AI-инженер без знания архитектуры». LLM-агенты — мощный инструмент, но они слепы без контрактов. Фиксируйте границы, проверяйте их автоматически, и тогда даже GPT-5 не сможет испортить ваш Symfony-проект. Иначе готовьтесь к тому, что «быстрое» решение обернется рефакторингом на полгода.

Подписаться на канал