Полгода назад я начал писать пет-проект практически полностью с помощью LLM-агентов. Первые недели это был чистый дофамин: фичи вылетали за вечер, я успевал за неделю столько, сколько раньше делала небольшая команда. А потом всё начало вязнуть — ровно так же, как вязнет любой проект с говнокодом. Каждая новая фича ломала две старые, агент правил код «на ощупь», а я всё больше времени тратил не на продукт, а на разгребание.
Эта статья — про то, что я понял и что в итоге сработало: почему LLM деградирует на плохой архитектуре точно так же, как команда людей; почему классика (SOLID, DDD, чистая архитектура) не только не устарела, но стала важнее; и как превратить архитектурные договорённости из «пожеланий в CLAUDE.md» в контракт, который машина не может нарушить. С конкретикой: два конфига deptrac для Symfony-бэкенда и пайплайн разработки с субагентом-ревьювером.
Медовый месяц и похмелье
Проект — сервис AI-обработки изображений: пользователь загружает фото, выбирает инструмент, платит кредитами, получает результат. Symfony на бэкенде, Next.js на фронте, ничего экзотического.
Первый месяц vibe coding выглядел как реклама будущего. «Добавь оплату кредитами» — готово. «Сделай очередь обработки с ретраями» — готово. Я почти не читал код, только проверял, что работает.
К третьему месяцу картина изменилась:
Агент добавляет проверку баланса — и она оказывается четвёртой реализацией этой проверки в проекте, чуть-чуть отличающейся от трёх предыдущих.
Правка в модуле обработки фото ломает админку, потому что они, оказывается, делят одну общую «помойку» — сервис на полторы тысячи строк.
Каждый диалог с агентом начинается с десяти минут «изучения кодовой базы», и всё равно он регулярно промахивается.
Знакомо? Это же классическая кривая легаси-проекта. Скорость внедрения фич падает по той же траектории, что и у команды людей — просто у LLM весь цикл «от стартапа до болота» занимает не три года, а три месяца.
И это логично, если вспомнить, на чём модель училась. LLM обучен на человеческом коде с GitHub — включая весь говнокод, которого там статистически больше, чем хорошего кода. Модель не знает «как правильно», она знает «как обычно». А «как обычно» — это god-объекты, копипаста и логика, размазанная по слоям.
Контекстное окно — это рабочая память джуна
Ключевая механика деградации простая.
LLM видит только то, что влезло в контекст. Чтобы безопасно изменить код, агент должен «загрузить в голову» всё, что этим кодом затронуто. И тут вступает связность:
Низкий coupling: правка локальна, агенту хватает одного-двух файлов, он делает изменение уверенно и правильно.
Высокий coupling: чтобы понять последствия правки, нужно прочитать 20 файлов. Агент читает 5, про остальные не догадывается — и правит на ощупь. Что-то ломается в месте, которое в контекст не попало.
Отсюда главный тезис статьи:
Архитектура — это не эстетика. Это способ минимизировать объём контекста, необходимый для одного изменения.
Хорошая архитектура делает изменения локальными. Локальные изменения влезают в контекстное окно целиком. Значит, LLM-friendly код — это ровно тот же код, который friendly для новичка в команде: понятный за пять минут, а не за две недели онбординга. Всё, что мы двадцать лет рассказывали друг другу про качество кода «для будущих поколений разработчиков», внезапно стало иметь мгновенный, измеримый эффект: либо агент справляется с фичей за итерацию, либо вы платите токенами и своим временем за его блуждания.
SOLID, DRY, KISS, YAGNI — теперь с LLM-оптикой
Классические принципы никуда не делись, но у каждого появилось новое прочтение.
SRP. Класс с одной ответственностью — маленький. Маленький класс целиком влезает в контекст вместе со своими тестами. Правка не задевает чужую ответственность, потому что чужой ответственности в файле нет. God-объект же гарантированно не влезет в голову агента вместе со всеми своими использованиями.
OCP + DIP. Если новая фича — это новый класс, реализующий существующий интерфейс, агенту вообще не нужно трогать старый код. Он смотрит на интерфейс и одну соседнюю реализацию как на образец — и пишет новую. Риск регрессии около нуля. Если же новая фича — это if в десяти местах, агент найдёт восемь из них.
DRY. У LLM патологическая тяга к копипасте. Ему всегда «дешевле» сгенерировать проверку заново, чем найти существующую: генерация — это его естественный режим, а поиск по кодовой базе — дорогая операция, съедающая контекст. Без внешнего контроля вы получите пять версий одной валидации, и при изменении бизнес-правила агент обновит три из них.
KISS + YAGNI. А вот тут обратная беда. LLM обожает «на всякий случай»: фабрику для класса с одной реализацией, конфигурируемость, которую никто не просил, слой абстракции над слоем абстракции. Выглядит солидно, ревью проходит легко («ну, аккуратно же написано»), а через месяц эта преждевременная гибкость становится тем самым лишним контекстом, который мешает следующим правкам.
Из этого следует неприятный вывод: знать принципы самому уже недостаточно. Когда код пишет агент со скоростью тысячи строк в час, ревьюить его глазами на соответствие архитектуре невозможно — вы станете бутылочным горлышком или начнёте пропускать. Принципы нужно кодифицировать так, чтобы их проверяла машина. К этому и придём.
DDD: бэкенду — да, фронтенду — нет
Прежде чем перейти к инструментам — два слова про доменно-ориентированное проектирование, потому что именно оно даёт LLM самое ценное: карту.
Ограниченные контексты и единый язык — это, по сути, способ разбить систему на куски, каждый из которых можно понять изолированно. Для агента неймспейс Core\Billing\Domain\Entity\CreditBalance — это уже документация: понятно, что это, где живёт и кого касается, ещё до чтения кода. Ubiquitous language работает с LLM даже лучше, чем с людьми: модель отлично оперирует словами, и если код называет вещи так же, как их называет постановка задачи, полдела сделано.
Но выстраданное уточнение: DDD — это про бэкенд. Именно там живёт то, ради чего DDD придуман: инварианты, транзакции, бизнес-правила, согласованность данных.
Я честно пробовал тащить ту же структуру на фронтенд — entities, репозитории, use cases в React-приложении. Получился оверинжиниринг в чистом виде: слои ради слоёв. Фронтенд — это отображение и UX-состояние. Его домен — «что сейчас на экране и что произойдёт по клику», а не «может ли пользователь потратить кредиты». Бизнес-инварианты фронту доверять всё равно нельзя — их проверит бэкенд. Фронту хватает компонентного подхода или feature-sliced design, а агенту на фронте нужна не доменная модель, а типизированный API-клиент, сгенерированный из контрактов бэкенда.
(Дисклеймер для комментариев: да, существуют rich-client приложения с настоящей доменной логикой на клиенте — графические редакторы, офисные пакеты. Речь про типичные продуктовые SPA и мобильные приложения.)
Пример: модульный монолит на Symfony
Дальше конкретика. Бэкенд проекта устроен так:
apps/backend/ ├── core/ # framework-agnostic бизнес-логика │ ├── Auth/ │ ├── Billing/ # кредиты, тарифы, платежи │ ├── Media/ # хранение файлов │ ├── Task/ # оркестрация AI-пайплайна │ ├── Tool/ # каталог инструментов обработки │ ├── Monitoring/ # здоровье AI-провайдеров │ ├── Shared/ # примитивы: Money, Uuid, Clock... │ └── Port/ # ICommandBus, IQueryBus, IJobDispatcher └── src/ # Symfony-адаптеры ├── Api/ # тонкие HTTP-контроллеры └── Infrastructure/ # реализации портов: Doctrine, S3, шины
Каждый контекст внутри повторяет одну и ту же структуру:
core/Billing/ ├── Domain/ # Entity, ValueObject, Enum, Exception, Repository (интерфейсы) ├── Application/ # Command + Handler, Query + Handler, Job, View (DTO ответов) └── Port/ # интерфейсы наружу: IPaymentGateway...
Правила игры:
core/не знает про Symfony, HTTP и Doctrine. Вообще.Контроллеры тонкие: валидируют вход, диспатчат Command/Query через порт шины, возвращают View. Вся логика — в хендлерах.
Зависимости между контекстами направленные:
Taskоркестрирует пайплайн, поэтому ему можно видетьBilling(списать кредиты),Media(взять файл) иTool(взять рецепт обработки).Monitoringвидит толькоTask. Листовые контексты (Auth,Billing,Media,Tool) не знают ни о ком, кромеShared.
Однотипность структуры — это отдельный подарок агенту: «новая фича = новый Command + Handler, вот три соседних примера» работает почти безотказно. LLM — машина продолжения паттернов; дайте ей чистый паттерн, и она будет его воспроизводить. Дайте ей зоопарк — и она будет воспроизводить зоопарк.
Проблема в одном: сами по себе эти правила — джентльменское соглашение. И LLM — не джентльмен.
Deptrac: архитектура, которую нельзя нарушить
Сначала я описал правила слоёв в CLAUDE.md. Агент их читал. И всё равно нарушал — не назло, а потому что в обучающей выборке инжектить репозиторий куда попало «нормально». Пара реальных примеров из моих диффов:
// core/Task/Domain/Entity/ProcessingTask.php use App\Infrastructure\Task\DoctrineTaskRepository; // инфраструктура в домене // core/Monitoring/Application/GetProviderHealthHandler.php use Core\Billing\Domain\Entity\CreditBalance; // чужой контекст напрямую
Работает? Работает. Тесты проходят? Проходят. PHPStan молчит? Молчит. А архитектура уже потекла, и следующие правки поедут по этой колее.
Вывод, к которому я пришёл: правила, которые не проверяются автоматически, для LLM не существуют. Нужен enforcement — на pre-commit и в CI. Для PHP это deptrac: статический анализатор, который строит граф зависимостей между классами и проверяет его против декларативных правил. Слои описываются коллекторами (по неймспейсу, регулярке, атрибуту), ruleset говорит, кому кого можно видеть. Любая зависимость вне правил — violation, ненулевой код выхода, красный CI.
Конфиг №1: вертикальные слои
Первый конфиг режет систему горизонтальными пластами — классическая луковица чистой архитектуры. Упрощённая версия:
# deptrac.yaml — вертикальное разделение слоёв deptrac: paths: [./src/, ./core/] layers: - name: Api collectors: - { type: classNameRegex, value: '#^App\\Api\\#' } - name: Infrastructure collectors: - { type: classNameRegex, value: '#^App\\Infrastructure\\#' } - name: Domain collectors: - { type: classNameRegex, value: '#^Core\\.*\\Domain\\#' } - name: Application collectors: - type: bool must: [{ type: classNameRegex, value: '#^Core\\.*\\Application\\#' }] must_not: # контракты выносим в отдельный слой - { type: classNameRegex, value: '#(Command|Query|View|Job)$#' } - name: Contracts # CQRS-DTO и порты — публичная поверхность core collectors: - { type: classNameRegex, value: '#^Core\\.*\\Application\\.*(Command|Query|View|Job)$#' } - { type: classNameRegex, value: '#^Core\\.*\\Port\\#' } - { type: classNameRegex, value: '#^Core\\Port\\#' } - name: Shared collectors: - { type: classNameRegex, value: '#^Core\\Shared\\#' } - name: Symfony collectors: - { type: classNameRegex, value: '#^Symfony\\#' } - name: Doctrine collectors: - { type: classNameRegex, value: '#^Doctrine\\#' } ruleset: Api: [Contracts, Shared, Symfony] # контроллер видит только DTO и порты Infrastructure: [Contracts, Application, Domain, Shared, Symfony, Doctrine] Application: [Domain, Contracts, Shared] Contracts: [Domain, Shared] Domain: [Shared] # домен не знает ни о чём Shared: ~ Symfony: ~ Doctrine: ~
Обратите внимание на слой Contracts. Контроллеру из Api нельзя видеть ни домен, ни хендлеры — только команды, запросы, view-DTO и интерфейсы шин. Это заставляет держать контроллеры тонкими автоматически: захотел агент «по-быстрому» дёрнуть доменную сущность из контроллера — deptrac не пустит.
Вендорные слои (Symfony, Doctrine) объявлены явно — так намерение видно в ruleset: Domain не имеет их в списке, значит попытка заюзать Doctrine-аннотацию в доменной сущности — это violation, а не «ну, все так делают».
Конфиг №2: горизонтальная изоляция контекстов
Первый конфиг ничего не говорит о том, может ли Billing залезть в Media. Это второй, отдельный конфиг:
# deptrac-contexts.yaml — изоляция ограниченных контекстов deptrac: paths: [./src/, ./core/] layers: - name: Api collectors: [{ type: classNameRegex, value: '#^App\\Api\\#' }] - name: Infrastructure collectors: [{ type: classNameRegex, value: '#^App\\Infrastructure\\#' }] - name: Auth collectors: [{ type: classNameRegex, value: '#^Core\\Auth\\#' }] - name: Billing collectors: [{ type: classNameRegex, value: '#^Core\\Billing\\#' }] - name: Media collectors: [{ type: classNameRegex, value: '#^Core\\Media\\#' }] - name: Task collectors: [{ type: classNameRegex, value: '#^Core\\Task\\#' }] - name: Tool collectors: [{ type: classNameRegex, value: '#^Core\\Tool\\#' }] - name: Monitoring collectors: [{ type: classNameRegex, value: '#^Core\\Monitoring\\#' }] - name: Shared collectors: - { type: classNameRegex, value: '#^Core\\Shared\\#' } - { type: classNameRegex, value: '#^Core\\Port\\#' } ruleset: # адаптеры обслуживают все контексты Api: [Auth, Billing, Media, Task, Tool, Monitoring, Shared] Infrastructure: [Auth, Billing, Media, Task, Tool, Monitoring, Shared] # листовые контексты не знают друг о друге Auth: [Shared] Billing: [Shared] Media: [Shared] Tool: [Shared] # Task оркестрирует пайплайн: кредиты, файлы, рецепты инструментов Task: [Billing, Media, Tool, Shared] # Monitoring смотрит на здоровье Task и не лезет никуда больше Monitoring: [Task, Shared] Shared: ~
Ruleset здесь — это, по сути, карта контекстов (context map) из DDD, записанная исполняемым кодом. Она читается как документация: видно, кто оркестратор, кто листья, и почему Monitoring не может дотянуться до Billing.
Почему два конфига, а не один
Первый порыв — засунуть всё в один файл: и слои, и контексты. Я попробовал, и это те грабли, ради которых стоило писать статью.
Deptrac позволяет классу принадлежать нескольким слоям и проверяет каждую пару «слой зависимого — слой зависимости». Класс Core\Billing\Domain\Entity\Invoice в объединённом конфиге принадлежит одновременно слою Domain и слою Billing. Его зависимость от Core\Billing\Domain\ValueObject\Money (тоже Domain + Billing) порождает четыре проверки: Domain→Domain, Domain→Billing, Billing→Domain, Billing→Billing. И если вертикальная матрица разрешает не всё то же самое, что горизонтальная, вы получаете ложные violations на абсолютно легальном коде. Начинаешь давить их разрешениями — и матрица правил превращается в решето, где уже непонятно, что вообще запрещено.
Решение: два независимых конфига с взаимоисключающими наборами слоёв. Один отвечает на вопрос «правильный ли это слой», второй — «правильный ли это контекст». Каждый файл маленький, читаемый, у каждого violation — однозначная причина. Бонус: правила эволюционируют с разной скоростью. Вертикаль застыла ещё в начале проекта, а вот контекстная карта живёт — новый контекст появляется, связь добавляется осознанным коммитом в deptrac-contexts.yaml, и этот коммит виден на ревью как архитектурное решение.
Запускается всё в pre-commit хуке и в CI:
vendor/bin/deptrac analyse --config-file=deptrac.yaml vendor/bin/deptrac analyse --config-file=deptrac-contexts.yaml
Теперь тот самый импорт чужого контекста заканчивается не молчаливой эрозией, а так:
----------- ------------------------------------------------------------ Reason Monitoring ----------- ------------------------------------------------------------ Violation Core\Monitoring\Application\GetProviderHealthHandler must not depend on Core\Billing\Domain\Entity\CreditBalance ----------- ------------------------------------------------------------
И — важный момент — агент читает эту ошибку и сам чинит код правильно: заводит порт, событие или запрос через шину. Ему не нужно объяснять, что не так: сообщение об ошибке само по себе обучающий сигнал, попавший точно в контекст в нужный момент. Это вообще главное свойство машиночитаемых правил: они превращают нарушение из «дефекта, который заметят через месяц» в «ошибку компиляции, которую агент исправит за одну итерацию».
CLAUDE.md: намерение против факта
Deptrac проверяет факт. Но агенту всё ещё нужно знать намерение — иначе он будет писать код методом «нагенерил → отхватил от линтера → переделал». Работает, но дорого и медленно.
Для намерений есть файл правил проекта (CLAUDE.md, AGENTS.md — у кого что). Что туда стоит класть, по опыту:
Карту репозитория — та схема каталогов, что выше, с одной строкой о назначении каждого куска.
Команды проверки. У меня это одна команда
make validate, которая гоняет cs-fixer, phpstan, оба deptrac-конфига и тесты. Агенту прописано запускать её после любых изменений бэкенда.Архитектурные инварианты словами: «core/ не зависит от Symfony и Doctrine», «новая фича = новый Command/Query + Handler», «контроллер: валидация → диспатч → View, ничего больше».
Запрещённые приёмы, на которых агент попадался: «никогда не редактируй файлы миграций руками — только генерация», «никакого PDO/SQL напрямую в обход репозиториев», «не добавляй абстракцию, пока нет второй реализации» (это против YAGNI-графомании).
Формулировки — короткие и императивные, в идеале с парой «плохо/хорошо». LLM следует правилам тем лучше, чем меньше они похожи на эссе.
Принцип, который я вывел для себя:
Правила описывают намерение. Deptrac проверяет факт. Одно без другого не работает.
Правила без enforcement агент нарушает. Enforcement без правил превращает разработку в игру «угадай, что запрещено, по сообщениям об ошибках». Вместе они дают агенту то же, что хорошему новому сотруднику: понятный онбординг плюс CI, который не пустит халтуру.
Субагент-ревьювер: второй взгляд до PR
Остался последний слой защиты — от того, что статика не ловит в принципе: соответствие реализации задаче, лишние абстракции, дубли логики, пропущенные edge cases.
Сначала я просто просил того же агента «проверь свой код». Это почти бесполезно: агент, только что написавший код, смотрит на него теми же глазами, тем же контекстом и с той же уверенностью, что всё правильно. Он «заражён» собственными решениями — как и человек, перечитывающий свой код через минуту после написания.
Работает другое: отдельный субагент-ревьювер с чистым контекстом. Он не видел рассуждений кодера, не привязан к его решениям и читает диф так, как читал бы чужой PR. В Claude Code это отдельный агент в .claude/agents/, а весь процесс оформлен скилом — тем самым «пайплайном разработки».
Скил: пайплайн разработки
--- name: development-pipeline description: Используй для нетривиальных задач разработки: новые фичи, изменения домена/API/схемы БД, многофайловые правки, рискованные рефакторинги. Цикл план → developer → reviewer → фиксы до принятия. --- # Пайплайн разработки Обязательный процесс для нетривиальных задач: 1. Главный агент составляет конкретный план. 2. Реализацию делает субагент `developer`. 3. Диф проверяет субагент `reviewer` (чистый контекст, диф + план, без истории рассуждений разработчика). 4. Блокирующие замечания → сфокусированный fix-план → снова `developer`. 5. Цикл повторяется, пока `reviewer` не примет диф. 6. Главный агент запускает `make validate` и отчитывается пользователю. ## Требования к плану для developer - цель и границы (что в скоупе, что нет); - файлы и документы, которые нужно посмотреть; - шаги реализации; - ограничения (слои, контексты, запрещённые приёмы); - обязательные проверки (`make validate`); - формат ответа: изменённые файлы + результаты проверок. ## Правило приёмки Задача не считается выполненной, пока у reviewer остаются блокирующие замечания или `make validate` красный. Замечания уровня «вкусовщина» reviewer помечает как non-blocking — они не крутят цикл.
Субагент-разработчик
--- name: developer description: Реализует переданный план. Не принимает решений о скоупе. model: sonnet --- Ты — разработчик. Тебе дают готовый план — выполни его точно. - Не выходи за скоуп плана. Заметил проблему рядом — упомяни в отчёте, но не чини. - Следуй паттернам соседнего кода: новая фича = Command/Query + Handler по образцу существующих. - После изменений запусти `make validate` и приложи результат. - Верни: список изменённых файлов, что сделано, результаты проверок.
Субагент-ревьювер
--- name: reviewer description: Ревьюит диф после developer. Чистый контекст, только диф и план. model: opus tools: Read, Grep, Glob, Bash --- Ты — архитектурный ревьювер. Тебе дают план задачи и диф. Кода ты не писал, решений разработчика не видел — оценивай только результат. Проверь по порядку: 1. **Соответствие плану.** Всё ли из скоупа сделано? Нет ли изменений вне скоупа? 2. **Архитектура.** Слои и контексты (сверх deptrac — семантика: доменная логика не утекла в контроллер или инфраструктуру?). 3. **DRY.** Поищи по кодовой базе: нет ли уже существующей реализации того, что написано заново? 4. **YAGNI.** Есть ли абстракции без второй реализации, параметры, которые никто не передаёт, «задел на будущее»? 5. **Корректность.** Edge cases, обработка ошибок, конкурентность, безопасность (авторизация, инъекции, доступ к чужим данным). 6. **Тесты.** Покрывают ли новое поведение? Не подогнаны ли под багованную реализацию вместо ожидаемого поведения? Формат ответа: список замечаний, каждое с пометкой BLOCKING или NON-BLOCKING, с файлом, строкой и предложением, как исправить. Если блокирующих нет — напиши APPROVED первой строкой.
Пара наблюдений после нескольких месяцев работы этой схемы.
Разделение окупается экономически. Кодеру уже не нужно быть самой умной моделью — план составлен, паттерны в кодовой базе чистые, за спиной линтеры. Реализацию тянет модель подешевле. А вот ревьювер и планировщик — та точка, где ум стоит своих денег: одна пойманная до PR проблема экономит целый цикл «человек посмотрел → написал замечание → агент переделал».
Чистый контекст ревьювера — не деталь, а суть. Пункты 3 и 4 его чек-листа (DRY и YAGNI) — это ровно те болезни LLM, о которых была первая половина статьи. Ловить их может только тот, кто не участвовал в написании: кодер всегда считает свою абстракцию нужной, а свою копипасту — уникальным случаем.
Человек смещается в конец конвейера. Итоговый пайплайн: план → генерация → pre-commit (cs-fixer, phpstan, deptrac ×2) → субагент-ревью → и только потом мои глаза. До меня доезжают споры и продуктовые решения, а не «тут забыли проверить null» и не «зачем здесь фабрика».
Выводы
Если сжать полгода набивания шишек в несколько строк:
LLM — усилитель, а не замена инженерной культуры. Хорошую архитектуру он масштабирует, плохую превращает в болото быстрее, чем любая команда людей.
Качество кода теперь измеряется в токенах. Чем меньше контекста нужно для изменения, тем лучше архитектура. SRP, низкий coupling, ограниченные контексты — это буквально оптимизация под контекстное окно.
Правила, которые не проверяются автоматически, для LLM не существуют. Намерение — в правилах проекта, факт — в deptrac/phpstan/тестах. Сообщение об ошибке — лучший способ доставить знание в контекст агента точно в момент, когда оно нужно.
DDD — на бэкенд, фронту — feature-sliced. Не тащите слои туда, где нет инвариантов.
Ревьювер должен быть отдельным агентом с чистым контекстом. Self-review — это плацебо.
Чек-лист внедрения на существующем PHP-проекте:
[ ] Зафиксировать целевые слои и контексты (хотя бы на салфетке — это будущий ruleset).
[ ] Поставить deptrac, описать вертикальные слои; текущие нарушения — в baseline, новые — в запрет.
[ ] Вторым конфигом описать карту контекстов. Два файла, не один.
[ ] Собрать одну команду
make validate(стиль + статика + deptrac + тесты) и включить её в pre-commit и CI.[ ] Написать правила проекта: карта репо, инварианты, запрещённые приёмы, команда проверки.
[ ] Завести субагентов developer и reviewer и оформить пайплайн скилом.
А дальше — тот же вечный процесс, что и с людьми: правила уточняются после каждого пойманного нарушения. Просто итерации теперь занимают минуты.
Комментарии (41)

al-chemist
11.07.2026 04:33Обезьянам раздали по гранате и теперь тут и там слышны взрывы, а форумы пухнут от историй «я не умею пользоваться инструментом, и поэтому он не работает».
Проблему узкого контекстного окна решили больше года назад, решение называется RAG, но все продолжают пытаться её решить никчемными текстами под разными названиями (скилл, субагент, и прочий мусор), которые только раздувают этот самый контекст.

johnnyBoy1984
11.07.2026 04:33а вы вообще поняли о чем статья? rag работает с семантическим слоем и отвечает на вопрос какие в базе есть релевантные по смыслу запроса чанки. в коде почти нет семантики и чанки не годятся для генерации кода потому что это не весь контекст, который нужен агенту.

al-chemist
11.07.2026 04:33Статья, my ass. Монография.
Этот, с позволения сказать, текстик — о том, что недостаток образования способен сотворить чудеса бессмысленной изобретательности.
rag работает с семантическим слоем и отвечает на вопрос какие в базе есть релевантные по смыслу запроса чанки
Да-да, конечно, жалко, что пацаны не в курсе, и давно уже используют RAG в связке с MCP для построения AST и отдачи релевантного контекста агенту, а не продолжают долбиться в набор буковок, который не влезает в контекстное окно.

johnnyBoy1984
11.07.2026 04:33ммм... срач в комментах, ну как тут устоять))) спасибо за прогрев
там помимо вашего rag еще куча тейков в статье, автору респект за труды! мега полезный материал

is_cake
11.07.2026 04:33А можно подробности? Статьи, примеры. Я как раз недавно задумался о возможности применения семантического поиска непосредственно к коду. И хотелось бы увидеть существующие наработки.

al-chemist
11.07.2026 04:33Было бы можно, но напыщенные невежи отрезали мне возможность делиться опытом, как это всегда и бывает на хабре; напишите в личку — дам и ссылки и примеры.

kotafey Автор
11.07.2026 04:33RAG поверх AST — это ответ на вопрос «как быстрее найти код». А статья — про то, что находить должно быть нечего.
Если проверка баланса живёт в пяти местах, идеальный retrieval честно притащит агенту все пять копий — и что дальше? Он всё так же должен удержать их в контексте и не забыть ни одну при правке. Вы просто построили скоростную магистраль к болоту. Архитектура — это когда проверка одна, лежит в предсказуемом месте, и никакой векторный индекс для этого не нужен: хватает неймспейса.
И про «никчемные тексты» — вторая половина статьи вообще-то про обратное: тексты без enforcement не работают, поэтому deptrac и ненулевой код выхода. RAG, кстати, тут бессилен: он подскажет агенту, где лежит Doctrine-репозиторий, но не помешает заинжектить его в доменную сущность. Semantic search не заменяет запрет — он просто помогает нарушать быстрее.
Так что нет, не альтернатива. Retrieval — про доступ к контексту, архитектура — про то, чтобы контекста нужно было меньше, enforcement — про то, чтобы правила нельзя было обойти. Первое без второго и третьего — автоматизация раскопок в легаси.

al-chemist
11.07.2026 04:33Если проверка баланса живёт в пяти местах […]
Ессесно, эта проблема же появилась вчера, и люди не научились искать Code Duplication статанализом 25 примерно лет назад.
Архитектура — это когда […]
Архитектура — существительное, поэтому отвечать на вопрос «когда» не может.
где лежит Doctrine-репозиторий, но не помешает заинжектить его в доменную сущность […]
Да, я помню, что вы про статический анализ слыхом не слыхивали, ведь гораздо проще взять через раз работающие промпты.
Так что нет, не альтернатива.
Да ладно! А я что, где-то говорил, что альтернатива? Архитектура — штука важная, полезная и нужная, вот только она к LLM вообще никакого отношения в принципе не имеет.
Retrieval — про доступ к контексту […]
Это хорошо, что вы аббревиатуру уже на ⅓ расшифровали. Продолжайте в том же духе, и под конец (даже прямо на второй букве) вам откроется много нового и интересного.

Dhwtj
11.07.2026 04:33Code Duplication статанализом 25 примерно лет назад.
Онанизм
нашваш лучший другУкрепляет мышцы рук
Стат анализ не отловит что ответственность дублирована и размазана
З. Ы. Не я токсичить начал

al-chemist
11.07.2026 04:33Стат анализ не отловит что ответственность дублирована и размазана
Да-да, конечно. Господи, когда ж вы все научитесь спрашивать: «как это сделать?» вместо огульного: «это сделать невозможно» на потеху публике, которая это делает уже сто лет в обед.

kotafey Автор
11.07.2026 04:33Про «слыхом не слыхивали про статический анализ» — половина статьи посвящена deptrac, это статический анализатор, конфиги приложены. Copy-paste-детекторы ловят текстуальные дубли; четыре чуть разные реализации одной проверки они как раз не ловят — это ровно то, о чём уже написал Dhwtj. А с тезисом «архитектура — штука важная, но к LLM отношения не имеет» спорит сама статья, аргументы там. На этом закончу, по кругу ходить не буду.

d3d14
11.07.2026 04:33Архитектура — это когда […]
Тот комментарий писала нейросеть. Не нужно отвечать на него.

Imidinti
11.07.2026 04:33Скорее всего имеется ввиду не классический RAG только с векторной базой. А более комплексное понятие, где уже несколько слоев поиска по разным индексам. Это AST, LSP сервер, графы вызовов, графы зависимостей и т.д. и рядом уже векторная база.
В этой статье описывается многоуровневый поиск https://yage.ai/share/why-coding-agents-still-use-grep-en-20260327.html
kotafey Автор
11.07.2026 04:33Спасибо, отличная ссылка — она, по-моему, ставит точку в споре выше. Индустриальный консенсус ровно обратный тезису «профессионалы используют RAG»: все шесть разобранных агентов (включая Cursor и Claude Code) базируются на grep, а эмбеддинги в этой иерархии — самый верхний и самый дорогой слой для редких чисто семантических запросов. И заметьте: чем чище архитектура, тем ниже по этим слоям можно оставаться — в кодовой базе с предсказуемыми неймспейсами grep по Core\Billing находит всё за один запрос, и никакой векторный индекс не нужен. Дешёвый поиск — это тоже дивиденд архитектуры.

al-chemist
11.07.2026 04:33Индустриальный консенсус […]
Рекламный текст ≠ индустриальный консенсус. Конечно, вам будут втюхивать греп, который токены жрет, как не в себя, — вместо инструментов, которые сокращают расходы пользователей в разы, а иногда — в десятки раз.
в кодовой базе с предсказуемыми неймспейсами
А куда делись размазанные по неправильным неймспейсам дубли из прошлого комментария?
ставит точку в споре
Никакого спора выше не было, мне недосуг спорить, особенно с твердолобыми. Я просто указал, как эту проблему решают профессионалы, если вам не надо — мне тем более.

maxim_ge
11.07.2026 04:33Точнее было бы сказать: индустриальный консенсус не “против RAG”, а “против embedding-based retrieval как retrieval-механизма по умолчанию”. Большинство инструментов используют текстовый (grep/regex) retrieval вместо векторного, оставляя эмбеддинги для узкого класса семантических запросов. Grep – это тоже форма retrieval, так что RAG как класс тут никуда не исключается. Меняется лишь то, чем наполнено
R.

Yevgeny_VS
11.07.2026 04:33Плюсую. Но на одного, кто эти вещи понимает и использует приходится 10, которые этим не заморачивается, и 100, кто даже не знает и гордится этим.

vagon333
11.07.2026 04:33Но на одного, кто эти вещи понимает и использует приходится 10, которые этим не заморачивается, и 100, кто даже не знает и гордится этим.
Разгребать после сотни и будет наш хлеб.

Dhwtj
11.07.2026 04:33Раз в 50 промптов или 20 коммитов остановка, выдох*, мини архитектурное ревью на свежую голову.
*Смотрим на часы и принудительно идём спать
По необходимости расширенное ревью и рефакторинг

digrobot
11.07.2026 04:33Архитектура — это не эстетика. Это способ минимизировать объём контекста, необходимый для одного изменения.
Ценная мысль. Я всегда подсознательно стремился к простоте в коде, и вот теперь этому есть обоснование.

dkfbm
11.07.2026 04:33Когда код пишет агент со скоростью тысячи строк в час, ревьюить его глазами на соответствие архитектуре невозможно — вы станете бутылочным горлышком или начнёте пропускать.
Это правда, если ревьювить каждую строчку, то наверное, было бы быстрее самому написать. Но выход есть: надо разбивать работу на этапы. Я все изменения (за исключением самых мелких) пускаю через
superpowers: сначала сам пишу описание фичи или изменения в отдельном.md, потом он пишет полную спецификацию, я просматриваю и корректирую по необходимости, и далее он на основе этой спецификации пишет план реализации. Код в нём тоже есть, но на него я особо не смотрю даже, а обращаю внимание в первую очередь именно на соответствие предложенного плана архитектурным решениям. Ну и на заключительном этапе, конечно, обязательное ревью другим агентом. Пока результат (на проекте в ~30K строк кода) меня устраивает.Что любопытно, при таком подходе количество строк документации в
.md(49K) превышает количество строк собственно кода.
kotafey Автор
11.07.2026 04:33То, что вы описываете, — это по сути SDD, Spec-Driven Development: спецификация становится первичным артефактом, а код — её производной. Spec Kit у GitHub, Kiro у Amazon — та же идея. И ваши 49K строк .md против 30K кода — характерный признак подхода: центр тяжести проекта переехал из кода в спеки.
Это рассуждение в другую сторону от моей статьи, и мне кажется, они не конкурируют, а дополняют друг друга. Общий диагноз один: ревьюить глазами каждую строчку невозможно, контроль надо куда-то смещать. SDD смещает его вверх — в намерение: вы ревьюите спеку и план, что в разы дешевле чтения дифа. Статья — про смещение вниз, в факт: что бы агент ни задумал, deptrac и ревьювер с чистым контекстом проверят результат.
Одно другое не отменяет, потому что план агент нарушает так же непринуждённо, как и CLAUDE.md: на этапе «пишу код по спеке» его тянет в ту же копипасту и те же дырки между слоями. Согласованный план — не гарантия согласованного кода. Поэтому в идеале это два конца одного конвейера: спека/план (намерение) → генерация → машинная проверка факта. Судя по тому, что у вас в конце стоит обязательное ревью другим агентом — вы к этой же схеме и пришли, просто с другого конца.
Использовать ИИ можно очень по-разному, и любопытно, что разные пути сходятся к одному и тому же выводу: человек уходит с ревью строк на ревью намерений и границ.

dkfbm
11.07.2026 04:33Использовать ИИ можно очень по-разному, и любопытно, что разные пути сходятся к одному и тому же выводу: человек уходит с ревью строк на ревью намерений и границ.
Именно так. ИИ генерирует слишком много кода в единицу времени, чтобы человек мог его хотя бы оценивать в том же темпе. Да и честно говоря, претензию именно к качеству кода свежим моделям предъявить трудно – чисто технически они пишут хорошо. При условии, что задача, ограничения и критерии успеха сформулированы точно и недвусмысленно. Со знаменитыми галлюцинациями практически не сталкиваюсь – ну может, пару раз что-то похожее было. Вероятно, в силу именно того, что каждый шаг проекта документирован, причины принятых решений понятны и особого простора для фантазий не оставляют.

Bardakan
11.07.2026 04:33KISS + YAGNI. А вот тут обратная беда. LLM обожает «на всякий случай»: фабрику для класса с одной реализацией, конфигурируемость, которую никто не просил, слой абстракции над слоем абстракции. Выглядит солидно, ревью проходит легко («ну, аккуратно же написано»), а через месяц эта преждевременная гибкость становится тем самым лишним контекстом, который мешает следующим правкам.
не понял. Какая еще фабрика в KISS, если принцип предполагает простоту кода - в том числе меньше кода там, где он не требуется?
а поиск по кодовой базе — дорогая операция, съедающая контекст
тоже ерунда - когда вместо spec-driven-development описываю только общие правила, а cursor без отдельного по стилю ориентируется на уже существующие классы как шаблоны, то получается неплохая экономия токенов (а значит и контекст засоряется меньше). Вплоть до того, что на работе удивлялись, как это мне хватает 20$ подписки. Но это конечно, если вы используете агентов именно как помощников, а не пытаетесь генерить вообще весь проект автоматически.
ken48
Недавно думал как раз об этом. Архитектурные практики, как способы борьбы со сложностью, были придуманы, чтобы дать возможность человеку понимать системы, выходящие по сложности за пределы его контекстного окна - кошелька Миллера - 3-5 (иногда 7) сущностей в кэше внимания. У ИИ контекстное сильно больше, но тоже ограниченное. Такая логика приводит к выводу: важность архитектуры есть функция отношения сложности проекта к размеру контекстного окна. Проще говоря, сложность проекта мы в любом случае должны компенсировать: там, где заканчивается контекстное окно, начинается архитектура. Иначе будет плохо.
kotafey Автор
Да, это ровно тот вывод, к которому я шёл всю статью, только сформулированный компактнее — «важность архитектуры есть функция отношения сложности проекта к размеру контекстного окна» я бы вынес в эпиграф. Добавлю два уточнения из практики. Первое: у LLM деградация начинается сильно раньше формальной границы окна — заявленные 200k токенов и окно, в котором модель реально удерживает все связи, это очень разные числа, так что «архитектура начинается» гораздо раньше, чем кажется по спецификации модели. Второе: даже гипотетическое бесконечное окно не отменяет архитектуру — пять чуть разных проверок баланса плохи не тем, что не влезают в контекст, а тем, что при правке нужно не забыть ни одну. Это уже не про объём внимания, а про количество мест, которые обязаны меняться согласованно.
Dhwtj
Против n² зависимостей между n компонентами никакое окно не поможет.
Если это правильные абстракции, то их хватает.