Введение в разработку с ИИ
Теория: Паттерны работы с coding agents
Когда человек давно работает в проекте, у него есть внутренняя карта: где лежит критичная логика, какие решения приняты исторически, какие части лучше не трогать без причины. У агента такой карты нет. Для него каждая новая сессия это почти новый заход в незнакомый репозиторий. Даже если задача человеку кажется очевидной, агенту часто сначала нужно понять базовые вещи: где находится нужный слой, как он связан с соседними модулями и что в этом проекте считается "нормальным" способом что-то менять.
Это важное отличие. Проблема редко в том, что агент "плохо пишет код". Гораздо чаще он ошибается потому, что видит проект фрагментами. Он не держит в голове весь репозиторий сразу, не помнит прошлые обсуждения команды и не знает, что у вас уже дважды не взлетала похожая идея. Все это ему приходится восстанавливать по текущему контексту.
Но контекст у агента ограничен: в него попадает разговор, прочитанные файлы, вывод команд, логи и все промежуточные шаги. Чем дольше и шумнее сессия, тем выше шанс, что важные детали размоются. В какой-то момент агент начинает путать старые и новые ограничения, повторно предлагать отвергнутые варианты или просто терять фокус.
Отсюда следует практический вывод: с агентом важно не только ставить задачу, но и помогать ему быстрее собрать правильную картину. Чем легче восстановить контекст, тем стабильнее результат. Об этом мы и поговорим в уроке.
Организация проекта
Агентам заметно легче работать там, где проект устроен предсказуемо. Если в репозитории понятная структура, устойчивые соглашения, типы, тесты и узнаваемые паттерны фреймворка, агент быстрее находит нужное место и реже делает лишние ходы. Ему не приходится каждый раз гадать, где искать роуты, где лежит бизнес-логика и в каком стиле здесь обычно пишут тесты.
Поэтому с агентами особенно хорошо работает подход convention over configuration. Если проект похож на типичный Rails-, Django-, Laravel- или React-код в рамках своей экосистемы, агент может опираться не только на локальные файлы, но и на общие ожидания от таких проектов. Это резко снижает долю случайных решений.

Но здесь есть нюанс: агент может помнить не самую свежую "типовую" картину. В быстро меняющихся экосистемах он иногда уверенно воспроизводит подход, который был хорошей практикой полгода назад, но уже устарел. Поэтому в задачах, связанных с новыми версиями библиотек, миграциями или свежими API, полезно явно просить его свериться с актуальной документацией.
Хорошая структура помогает не только навигации, но и проверке. Типы быстро подсказывают контракт между модулями. Тесты показывают ожидаемое поведение. Явные интерфейсы и внятные имена сокращают пространство для догадок. Чем лучше код сам объясняет себя, тем меньше агенту приходится импровизировать. С приходом агентов, языки со статической типизацией получили большое преимущество по сравнению с динамической типизацией. Агенты не только хорошо ориентируются благодаря типам, но и отлично помогают их писать.
Обратное тоже верно. В сильно кастомизированном проекте агент чаще идет по ложному следу: меняет не тот слой, дублирует существующую логику, пропускает важную проверку или приносит в код случайный паттерн из другого стека.
В общем дефолты наше все. Или как было сказано в ментальном программировании: единообразие важнее локальных оптимизаций.
Добавление кода
Работа с агентами обрастает своими подходами, которые постоянно добавляются и меняются. Все описать невозможно да и не нужно - быстро устареет. Но есть кое-какие вещи, которые остаются постоянными. Ниже мы разберем как раз такие, не побоимся этого слова, паттерны.
Решение на базе примера
Один из самых надежных способов направить агента - показать не только что нужно сделать, но и на что именно ориентироваться в текущем проекте. Если в кодовой базе уже есть похожий endpoint, тест, сервис или компонент, почти всегда выгоднее сказать: сделай по аналогии вот с этим местом.
Для агента пример работает лучше абстрактного описания. Фраза "добавь новый endpoint" оставляет слишком много свободы: как назвать файлы, где делать валидацию, как оформлять тесты, как возвращать ошибки. А фраза "добавь endpoint для teams по аналогии с users, включая тесты и обработку ошибок в том же стиле" сразу задает рамки. Агент видит образец и копирует локальную логику проекта. Хотя уровень повторения сильно зависит от модели и конкретного агента. Иногда, когда, казалось бы, можно просто копировать, модель все равно начинает изобретать.
Тот же прием работает и в формулировке задачи. Вместо "сделай красиво" лучше сказать "оформи этот экран в духе существующей страницы настроек". Вместо "напиши хорошие тесты" - "сделай тесты как в модуле billing, без моков, с акцентом на граничные случаи". Чем конкретнее ориентир, тем меньше агенту приходится угадывать.
Практически это означает очень простую привычку: перед тем как просить добавить новый код, сначала найти в проекте качественный образец, а если его нет, то написать и возможно даже самостоятельно -)
Например, разница между слабыми и сильными формулировками может выглядеть так:
Если есть какие-то отличия, то их можно дописать в конце. Например, не делай того или добавь вот это.
Сначала исследование, потом план, потом реализация
Одна из самых частых ошибок в работе с агентом - сразу просить его писать код. Для мелкой правки это нормально, но во всех неоднозначных задачах сначала полезнее попросить исследование. Идея простая: пусть агент сначала разберется, как устроен нужный участок системы, и только потом предлагает изменение.
Это может звучать как: "посмотри, как у нас устроена авторизация", "найди, где формируется сессия", "покажи, какие тесты покрывают этот сценарий", "перечисли файлы, которые придется менять". Такие запросы кажутся менее продуктивными, потому что пока не создают diff, но именно они часто спасают от изменений вслепую.
Хороший рабочий ритм здесь трехшаговый: сначала исследование, потом план, потом реализация.
Сначала агент собирает картину. Потом формулирует, что именно собирается менять и почему. И только после этого пишет код. Такой порядок помогает заметить неверное понимание до того, как в репозитории появится большая пачка сомнительных правок. Например, вместо "добавь вход через Google" полезнее начать так: "изучи текущий login flow, хранение сессий и работу с секретами; потом перечисли, какие файлы нужно менять для Google OAuth и какие риски ты видишь". После такого захода реализация получается заметно точнее, потому что агент уже понимает контекст, а не просто добавляет новую функциональность поверх непонятной системы.
Этот паттерн особенно важен в старом коде, незнакомом репозитории и задачах с несколькими связанными файлами. Чем выше шанс задеть лишнее, тем выгоднее сначала потратить время на разведку. Небольшая пауза перед кодом почти всегда дешевле, чем потом разбирать километровый diff.
Если задача завязана на новый фреймворк, свежую библиотеку или недавно изменившийся инструмент, исследование должно включать и внешнюю документацию. Иначе агент может уверенно реализовать вчерашний рецепт, который уже не соответствует текущей версии экосистемы.
Например, вместо прямой команды на реализацию лучше сначала дать такой запрос:
Здесь можно пойти еще дальше. Отвязаться от текущего состояния системы и поговорить с ИИ на тему того, а как вообще решается проблема в идеальном случае, какая обычно структура в базе, какие используются инструменты и библиотеки. Такую задачу можно делать вообще в пути (гуляя с собакой или стоя в пробке), в голосовом режиме с обычным чатом. И когда все станет понятно, то мы можем попросить в этом же чате сформировать концепцию решения и скопировать ее в наш агент, который это совместит с кодом проекта и предложит план реализации.
Сначала локальная правка, потом расширение
Когда агенту дают большую задачу, он нередко пытается решить ее слишком широко. Это естественно: модель видит возможность "сразу сделать хорошо" и начинает попутно перестраивать архитектуру, выравнивать нейминг, оптимизировать соседние модули и закрывать еще пару потенциальных проблем. Поэтому почти всегда надежнее идти от узкой версии решения. Сначала - минимальная правка, которая закрывает основной сценарий. Потом - расширение, если оно действительно нужно. Такой ритм удерживает задачу в границах и дает раннюю обратную связь.
Вместо "полностью переработай обработку платежей" лучше начать с чего-то вроде: "добавь поддержку нового статуса только в одном месте и обнови один целевой тест". Если агент попал в контекст, дальше можно попросить покрыть соседние сценарии, вынести повторяющуюся логику или сделать аккуратный рефакторинг. Но сначала важно получить маленький, проверяемый кусок результата.
Этот прием особенно полезен потому, что позволяет быстро увидеть, правильно ли агент понял задачу. Ошибка в локальной правке дешева: ее легко заметить и скорректировать. Ошибка в большой серии изменений дорога: приходится разбирать сразу и неверную логику, и лишние побочные улучшения.
На длинных задачах этот подход можно повторять сериями. Сначала один удачный пример, потом еще пара похожих случаев, и только после этого распространение на весь набор файлов или сценариев. Снаружи это выглядит менее эффектно, чем одна большая команда, но обычно дает намного более управляемый результат.
В промпте это часто выглядит так:
Потом можно попросить найти остальные места в коде и предложить план по их исправлению по аналогии с текущим изменением.
Проверять результат после каждого заметного шага
Агент может выглядеть уверенным даже тогда, когда движется не туда. Он последовательно читает файлы, пишет код, запускает команды и формирует логичный с виду план, но вся эта активность может опираться на неверное предположение. Поэтому одно из самых полезных правил в работе с агентами - не ждать самого конца, а проверять результат после каждого заметного шага.
Промежуточная проверка необязательно должна быть тяжелой. Иногда достаточно открыть diff и убедиться, что агент изменил именно тот слой, который нужно. Иногда полезно попросить его показать список затронутых файлов до реализации. Иногда - прогнать один конкретный тест, линтер или команду сборки.
Особенно хорошо работают задачи, в которых у агента есть способ проверить себя самостоятельно. Если вы пишете: "исправь баг и убедись, что падавший тест теперь проходит", качество обычно выше, чем при формулировке "попробуй починить". То же относится к UI: если есть скриншот, эталонное состояние или хотя бы понятный визуальный критерий, агенту проще понять, что значит "готово".
Из этого следует еще одно правило: если видно, что агент пошел не туда, лучше остановить его сразу. Не стоит надеяться, что еще через пару шагов он сам вырулит. Чем раньше оборван неверный ход, тем меньше потом разгребать лишний diff и тем проще вернуть задачу в правильные рамки.
Хорошая формулировка здесь такая:
Кроме запуска тестов полезно добавлять прогон линтеров и проверку типов. Все вместе дает хорошие гарантии и тормозит движение в неверную сторону, когда агент загоняет себя настолько сильно, что уже не может выбраться из возникших ошибок, что приходится его тормозить и откатывать изменения.
Исправление ошибок
Задачи на исправление ошибок формулировать проще всего, так как у нас, почти всегда, есть какой-то текст о проблеме. Это может быть упавший тест, stack trace, сообщение компилятора, ошибка из браузерной консоли, лог приложения или ссылка на неудачный CI-запуск (например в Github Actions). Для агента такие артефакты ценнее, чем пересказ проблемы в свободной форме.
Поэтому в отладке обычно лучше не интерпретировать слишком рано, а сначала показать первоисточник. Вместо "кажется, у нас что-то не так с авторизацией" намного полезнее дать текст ошибки, команду, на которой все падает, и ожидаемый результат. Агенту проще локализовать проблему, когда он видит настоящий симптом, а не уже переработанную человеком гипотезу.
Хороший запрос в таком случае выглядит примерно так: "после таймаута сессии логин ломается; <тут ссылка, текст ошибки или просьба посмотреть лог>". Здесь есть и входные данные, и критерий завершения, и ограничение на ширину решения. Еще лучше если ошибка воспроизводимая, тогда мы сможем попросить агента проделать шаги, которые позволяют эту ошибку увидеть, например, открыть браузер и самостоятельно попробовать залогиниться.
Рабочий цикл здесь обычно такой: дать агенту сырой артефакт сбоя, попросить локализовать источник, затем внести минимальную правку и проверить тот же самый симптом еще раз. Такой цикл хорошо сочетается с human-in-the-loop: человек приносит новые факты, агент быстро исследует и предлагает следующую итерацию.
Например, запрос на исправление можно сформулировать так:
Откровенно говоря, в режиме планирования часто бывает достаточно просто скинуть саму ошибку, например вывод тестов. Хорошие модели сами все поймут, сами спросят и сами предложат решение.
Чего не стоит ждать от агента
Несмотря на то, что агенты и модели постоянно улучшаются, есть области, в которых они постоянно лажают и пока непонятно смогут ли они это победить или нет. Вот список проблем, на которые постоянно натыкаются разработчики:
Обобщение
Агент не очень похож на сильного инженера, который быстро схватывает общий принцип и поднимается на уровень абстракции. Чаще он работает от локального примера к локальному примеру. Если он увидел три почти одинаковых фрагмента кода, это еще не значит, что сам предложит вынести общий helper, общий компонент или общий слой логики. Гораздо вероятнее, что он поправит один фрагмент, потом второй, потом третий - и на этом остановится.
Поэтому не стоит ждать от агента надежного обобщения "по умолчанию". Он умеет продолжать найденный паттерн, но хуже видит, когда из повторения уже пора делать абстракцию. Для человека повторяющаяся структура часто сама подсказывает, что здесь просится обобщение. Для агента это далеко не всегда очевидный следующий шаг. Если вы хотите не просто серию одинаковых правок, а выделение общего решения, это полезно формулировать явно.
Использование готовых решений
Еще одна типичная особенность: агент охотно идет в рукопашную реализацию даже там, где в проекте уже есть готовый механизм. Он может написать новый helper вместо существующего, продублировать валидацию, собрать временный парсер, хотя рядом уже есть библиотека или внутренний util для той же задачи.
Из-за этого полезно явно напоминать: сначала проверь, нет ли у нас уже готового решения; переиспользуй существующий сервис; не дублируй логику, если в проекте уже есть abstraction для этого. Такие ограничения особенно важны в зрелых кодовых базах, где главная ценность не в том, чтобы "как-нибудь заработать", а в том, чтобы не размножать почти одинаковые способы делать одно и то же.
Исправление причины вместо следствия
Еще не стоит ждать, что агент сам надежно различит причину и следствие. Очень часто он чинит не источник проблемы, а ближайшее место, где ошибка стала видимой. Не сходится тип - он легко добавит cast в нужной точке вместо того, чтобы поправить исходные типы. Пришел неожиданный null - поставит дополнительную защиту именно здесь. Локально это может убрать симптом, но не обязательно исправляет логику, из-за которой система вообще пришла в неправильное состояние.
Поэтому в задачах на исправление особенно полезно явно задавать рамку: найди первопричину, не маскируй проблему, не чини ее точечно без объяснения, почему она возникла. Иначе агент часто выбирает ad-hoc-решение: короткое, рабочее в моменте, но плохо переносимое на соседние случаи. Он хорошо умеет закрывать дыру там, где сейчас течет, но хуже сам понимает, где именно сломалось.
Актуальность информации
Не стоит ждать и того, что агент сам всегда пойдет за свежими фактами. Если задача касается документации, UI в браузере, CI, внешнего API или состояния конкретной страницы, он может начать рассуждать по памяти и звучать при этом вполне уверенно. Обычно это заметно по формулировкам: агент пишет так, будто уже знает ответ, хотя на самом деле еще ничего не открывал и не проверял.
Поэтому его часто полезно направлять буквально: открой документацию этой версии; зайди на страницу и посмотри, что реально отображается; прочитай текущую ошибку из CI; посмотри implementation вот этого метода; сравни с существующим модулем. Для человека такие указания могут звучать избыточно, но для агента это способ заменить догадку наблюдением.
Избегайте этих ошибок
Даже если в целом выстроили хороший процесс, есть несколько повторяющихся сценариев, в которых агент начинает резко деградировать. Обычно проблема не в одной неудачной команде, а в том, как устроена сама сессия.
Одна сессия на все подряд
Одна из самых частых ошибок - превращать сессию в свалку разнородных задач. Сначала вы чините тест, потом между делом спрашиваете про устройство очереди, потом просите обновить README, а затем возвращаетесь к первому багу. Для человека это еще терпимо: он умеет держать в голове, что относится к чему. Для агента это просто шум, который съедает контекст.
В результате в разговоре остается масса нерелевантных файлов, команд и промежуточных рассуждений. Агент начинает опираться не только на нужную часть истории, но и на случайные куски соседних задач.
Практическое правило простое: разные задачи - разные сессии. Если переключились на другую тему, полезно очистить контекст или начать заново. Это особенно важно, когда новая задача вообще не связана с предыдущей.
Слишком долго исправлять один и тот же промах
Еще один плохой паттерн - много раз подряд корректировать агента внутри одной и той же неудачной траектории. Он пошел не туда, вы его поправили. Потом еще раз. Потом еще. Кажется, что еще немного и он "допоймет" задачу, но на практике часто происходит обратное: контекст заполняется неудачными попытками, отвергнутыми подходами и частично верными правками.
После этого агенту уже труднее понять, что из сказанного является текущим направлением, а что было промежуточной ошибкой. Если после пары коррекций движение все еще неправильное, обычно выгоднее остановиться, очистить контекст и переформулировать задачу заново - уже с учетом того, что вы выяснили.
То есть вместо длинной серии "нет, не так" лучше сделать новый аккуратный заход: коротко описать цель, ограничения, правильный ориентир и критерий проверки.
Доверять правдоподобному результату без проверки
Одна из самых дорогих ошибок - принять правдоподобный ответ за правильный. Агент очень хорошо умеет производить убедительно выглядящий результат: код компилируется, diff аккуратный, объяснение звучит разумно. Но без проверки все это может оказаться просто хорошо оформленной догадкой.
Поэтому опаснее всего режим, в котором агент что-то сделал, а проверить это нечем. Нет теста, нет понятной команды, нет скриншота, нет воспроизводимого симптома. В такой ситуации человек сам становится единственным механизмом валидации, а значит почти наверняка будет пропускать ошибки.
Отсюда жесткое, но полезное правило: если результат нельзя внятно проверить, ему нельзя по-настоящему доверять. Для UI нужны скриншоты или визуальные критерии. Для бага - воспроизводящий тест или сценарий. Для инфраструктурной правки - команда, которая подтверждает, что проблема ушла.
Бесконечное исследование без рамок
Исследование полезно, но у него есть и обратная сторона: если попросить агента просто "разобраться, как тут все устроено", он может начать читать слишком много. Десятки файлов, длинные цепочки зависимостей, соседние модули "на всякий случай", исторические артефакты - и вот значительная часть контекста уже занята разведкой, которая лишь косвенно относится к задаче.
Такой режим особенно коварен тем, что внешне выглядит разумно: агент действительно старается понять систему. Но без границ исследование быстро превращается в бесконтрольное потребление контекста.
Поэтому исследовательские запросы стоит сужать. Лучше сказать: "посмотри только login flow и связанные тесты" или "найди, где создается сессия и где обновляется токен", чем просить изучить весь auth-модуль целиком. А если разведка потенциально широкая, выгодно выносить ее в отдельную сессию или отдельного агента, чтобы основная реализация не тонула в собранных по пути подробностях.
Секретный соус
Чем дальше вы будете работать с агентами, тем сильнее от уважительного "пожалуйста не могли бы вы", захочется перевести общение на "ты козел". Если у вас не возникает такого желания, то мы завидуем вашей выдержке -) Наблюдения в сети показывают, то, что постепенно разработчики начинают лениться, давать меньше контекста (а модели умнеют и лучше их понимают) и когда агент лажает, начинают с ним ругаться и материться. Нужно ли переживать по этому поводу? Материтесь, это успокаивает

