AI-разработка: от вайбкода до своих рук
Почему сломалось
Ошибки — это адреса, а не приговоры: как читать сообщения об ошибках и как отдавать их агенту, чтобы чинилось с первого раза.
Почему сломалось
Вчера вечером всё работало. Агент собрал вам страницу, вы открыли её, показали друзьям. Сегодня вы попросили добавить одну кнопку — и вместо приложения браузер показывает красный экран, залитый английским текстом. Первое желание — закрыть ноутбук. Второе — написать агенту «ВСЁ СЛОМАЛОСЬ, ПОЧИНИ!!!».
Не спешите ни с тем, ни с другим. В этом уроке — навык, который отличает человека, застрявшего на неделю, от человека, починившего за пять минут: умение прочитать сообщение об ошибке и правильно отдать его агенту.
Ошибка — это адрес, а не приговор
Красный экран воспринимается как «программа умерла». На деле произошло другое: программа дошла до места, где не смогла продолжить, остановилась и оставила записку — что случилось, в каком файле, в какой строке. Записка с обратным адресом.
Сравните с настоящей бедой. Кнопка «Сохранить» молча перестала сохранять: экран спокойный, ошибок нет, вы неделю вносите записи — а они исчезают. Вот это страшно: поломка есть, а адреса нет, и вы даже не знаете, что нужно что-то искать. Громкая ошибка честнее. Она случается сразу, останавливает работу до того, как испорчены данные, и сама говорит, где смотреть.
Опытные разработчики поэтому радуются, когда после их правки программа падает с внятной ошибкой, и напрягаются, когда «вроде работает». Привыкайте к той же реакции: красный экран — это программа сотрудничает с вами.
Осталась одна помеха: записка написана на языке, которому вас не учили. Разберём её по строчкам — полезного там три строки, остальное шум.
Что написано в ошибке
Возьмём игрушечный пример. Приложение должно показывать «Привет, Игорь!», а вместо этого — красный экран с таким текстом:
TypeError: Cannot read properties of undefined (reading 'name')
at renderGreeting (app.js:12:31)
at main (app.js:27:3)
Читаем сверху вниз.
Тип и суть — первая строка. TypeError — «ошибка типа»: программа ожидала одно, а получила другое. Дальше уточнение: попытались прочитать свойство name у undefined — у пустоты. То есть код спросил имя у пользователя, которого нет.
Файл и строка — app.js:12:31. Самая ценная часть сообщения: не «где-то в проекте», а файл app.js, строка 12, символ 31. Точный адрес.
Stack trace (след вызовов) — все строки, начинающиеся с at. Это цепочка «кто кого позвал»: функция main вызвала функцию renderGreeting, и вот в ней-то программа упала. Верхняя строка — место падения, строки ниже — путь, которым программа туда пришла. Практическое правило простое: ищите сверху первую строку с вашим файлом, а не с чем-то из папки node_modules (там лежит чужой библиотечный код) — она и ведёт к проблеме.
Теперь три ошибки, которые вы встретите чаще всего.
SyntaxError: опечатка
SyntaxError: Unexpected token '}'
Программа даже не начала работать: в тексте кода лишняя скобка, потерянная запятая, незакрытая кавычка. Как предложение, в котором закрыли скобку, которую никто не открывал, — читатель спотыкается и дальше не идёт. Такая ошибка чаще всего появляется после ручных правок: вы «чуть-чуть подправили» файл, который писал агент.
Cannot find module: файла нет по адресу
Error: Cannot find module './utils/format'
Кто-то в коде говорит «возьми код из файла ./utils/format», а по этому пути пусто. Файл переименовали или перенесли, агент сослался на файл, который забыл создать, или не установлена зависимость — чужая библиотека, которую проект скачивает отдельно.
undefined: здесь пусто
Наш TypeError из примера выше. Программа ждала данные, а пришла пустота. Типичная причина: данные ещё не успели загрузиться, а код уже пытается их показать. Или поле называется username, а код читает name — тоже пустота, только по невнимательности.
Как одна опечатка превращается в красный экран и как текст ошибки ведёт обратно к нужной строке — посмотрите на схеме:
В плеере над схемой переключите сценарий «Одна опечатка»: сбой в одном файле проходит через всю цепочку до браузера, а stack trace разматывает её в обратную сторону — до файла и строки. Потом включите «Ошибка → агент → зелёно» — это полный цикл ремонта: текст ошибки уходит агенту, тот находит строку, правит, экран снова живой.
Читать ошибку вы теперь умеете. Но чинить её будет агент — и от того, в каком виде вы её передадите, зависит, попадёт он в цель с первого раза или будет стрелять наугад.
Как отдать ошибку агенту
Правило первое: целиком, а не пересказом. «Выдаёт какую-то ошибку про модуль» — из такого описания стёрто всё, ради чего ошибка существует: тип, файл, строка, след вызовов. Агент получает загадку вместо адреса и начинает угадывать. Копируйте весь текст, от первой строки до последней строки следа. Выглядит длинно — и пусть: агент прочитает его быстрее, чем вы выделите мышкой.
Правило второе: расскажите, что вы делали перед этим. Ошибка описывает следствие, ваш рассказ — вероятную причину. «Попросил добавить форму входа, после этого главная страница перестала открываться» — две фразы сужают поиск со всего проекта до последних правок.
Правило третье: просите объяснить причину, а не только починить. Собранное вместе сообщение выглядит так:
Попросил добавить форму входа — теперь главная
страница падает. Вот ошибка целиком:
TypeError: Cannot read properties of undefined (reading 'name')
at renderGreeting (app.js:12:31)
at main (app.js:27:3)
Объясни, в чём причина, и только потом почини.
«Объясни» здесь — не вежливость. Без этой просьбы агент может убрать симптом вместо причины: обернуть падающую строку проверкой «если пусто — ничего не показывай». Красный экран исчезнет, но вопрос «почему там пусто?» останется без ответа и вылезет в другом месте, где такой проверки нет. А из объяснений постепенно складывается ваш собственный опыт: после пяти разобранных ошибок шестую вы узнаете ещё до того, как отправите её агенту.
Даже при идеальной подаче с первого раза чинится не всегда. И вот на втором-третьем заходе новички теряют больше всего времени — потому что делают одно и то же движение, которое ухудшает положение.
«Всё ещё не работает» — дорога по кругу
Знакомый сценарий. Агент починил — не помогло. Вы пишете «всё ещё не работает». Он правит снова — теперь другая ошибка. «Опять не работает!» Ещё правки, ещё ошибка, пятый круг.
Каждая такая попытка — новые правки поверх предыдущих. Агент не отменяет свои неудачные ходы, он наслаивает следующие. После пяти кругов проект дальше от рабочего состояния, чем был при первой ошибке, а в переписке уже не разобрать, какая из ошибок текущая и какие правки живы. Хуже того: «всё ещё не работает» не сообщает агенту ничего нового — он пробует следующую догадку с теми же данными, что и в прошлый раз.
Отсюда правило двух попыток. Агент дважды не попал — стоп, дальше два шага:
- Откат к последнему рабочему состоянию. Прямо попросите: «верни проект к состоянию до этих правок». В следующем уроке появится инструмент, который делает откат одной командой и без доверия на слово, — пока хватит просьбы.
- Новая формулировка с чистого листа: свежий текст ошибки целиком, что вы делали, что уже пробовали и что не помогло. Последняя часть обязательна — без неё агент с чистой совестью предложит тот же неудачный ход в третий раз.
Откат кажется поражением: «столько правок — и всё выбросить?» Но правки, которые не починили, — не прогресс, а мусор на месте раскопок. Убрать его — самый быстрый путь вперёд.
Остался случай, который не лечится ни правильной подачей, ни откатом: когда код вообще ни при чём.
Когда виноват не код
Иногда «сломалось» выглядит иначе: не красный экран, а «Не удаётся получить доступ к сайту» — или страница открывается, но упрямо старая. Прежде чем нести это агенту, потратьте минуту на три проверки: они снимают половину ложных тревог.
Запущен ли dev server. Dev server (сервер разработки) — программа, которая держит приложение запущенным на вашем компьютере; обычно это команда вида npm run dev, которую агент оставил работать в терминале. Закрыли терминал, перезагрузили компьютер — сервер погас. «Сайт недоступен» в этом случае означает не «код сломан», а «на том конце никого нет». Лечится не правкой кода, а повторным запуском.
Тот ли адрес и порт. Вкладка открыта на localhost:3000, а сервер после перезапуска поднялся на localhost:3001, потому что старый порт оказался занят, — и написал об этом в терминале, но кто же читает терминал. Старая вкладка смотрит в пустоту. Сверьте адрес в браузере с тем, что сервер напечатал при старте.
Кэш браузера. Браузер экономит время и показывает сохранённую копию страницы. Агент код поменял, вы обновили — а видите вчерашнюю версию. Жёсткая перезагрузка — Ctrl+Shift+R, на Mac Cmd+Shift+R — заставляет браузер забрать страницу заново, минуя копию.
Итоговый порядок при любом «сломалось»: взгляд в терминал (жив ли сервер, нет ли ошибки прямо там) → адрес и порт → перезагрузка без кэша → и только потом текст ошибки агенту. Так агент получает настоящие поломки, а не задачу «почини незапущенный сервер правкой кода» — с ней, кстати, он может увлечённо провозиться полчаса.
Что дальше
Теперь у вас есть цикл ремонта: прочитать ошибку, отдать её целиком с контекстом, после двух неудачных попыток — откат и новая формулировка. С таким циклом красный экран перестаёт быть событием — это рабочий момент на пять минут.
Агент в этом цикле чинит быстро. Иногда слишком быстро: вы отправили одну ошибку про одну строку, а он «заодно» переписал четыре файла. Экран зелёный, приложение работает — но что именно теперь лежит в вашем проекте, вы не знаете. Сегодняшний откат «по просьбе» тоже держался на честном слове агента: проверить, что он вернул всё как было, вам пока нечем.
Этим займёмся в следующем уроке — «Код, который ты не читал»: дифф (построчный список изменений) и git покажут каждую строку, которую агент тронул, — и превратят «он что-то переписал» в «он изменил вот это, и я понимаю зачем».