Читать книгу Вайб-кодер от нуля до мастера в эпоху AI (Михаил Тарасов) онлайн бесплатно на Bookz (4-ая страница книги)
Вайб-кодер от нуля до мастера в эпоху AI
Вайб-кодер от нуля до мастера в эпоху AI
Оценить:

5

Полная версия:

Вайб-кодер от нуля до мастера в эпоху AI

UnhandledPromiseRejectionWarning: Первое, что мы видим. «Необработанное отклонение Промиса». Это говорит нам, что внутри нашей async функции что-то пошло не так, и мы это падение не поймали. Наш Промис (а async функции всегда возвращают Промис) был «отклонён» (rejected), а у нас нет блока. catch () или try...catch, чтобы с этим разобраться.

TypeError: Cannot read properties of undefined (reading ’map’): А вот и суть проблемы. «Ошибка типа: не могу прочитать свойство ’map’ у undefined». Это значит, что мы пытались сделать так: undefined.map (…).

/path/to/your/project/index. js:6:23: Это точные координаты бага. Файл index. js, строка 6, символ 23.

Идём на 6-ю строку. А там у нас: const names = users.map (user => user.name);

Ага! Значит, в этот момент переменная users была равна undefined.

Теперь отматываем плёнку назад. Откуда берётся users? Со строки выше: const users = await response. json ();. Значит, именно здесь что-то пошло не так.

Почему response. json () мог вернуть undefined или что-то, что привело к ошибке? Давай добавим немного старого доброго дебага — console. log.

async function getUsersNames () { const response = await fetch (»https://jsonplaceholder.typicode.com/users-produsers'); console. log («Response Status:», response.status); // Что нам ответил сервер? console. log («Response OK?», response. ok); // Всё ли хорошо? const users = await response. json (); // Попробуем распарсить ответ console. log («Users:», users); // Что в итоге в переменной? const names = users.map (user => user.name); return names; }

Запускаем снова. Теперь картина становится яснее.

В консоли мы увидим что-то вроде:

Response Status: 404

Response OK? false

А потом снова крэш. Потому что когда мы пытаемся вызвать. json () на ответе с ошибкой 404 (Not Found), который содержит не JSON, а HTML-страницу с ошибкой, происходит сбой парсинга.

Вот он, корень зла! Наш «гениальный» AI написал код для идеального мира. Для «happy path», где сервер всегда отвечает 200 OK и возвращает правильный JSON. Он не подумал о том, что реальный мир полон боли и страданий (и 404-х ошибок).

Фиксим баг: добавляем броню

Теперь, когда мы поняли проблему, исправить её — дело техники. Нам нужно проверять ответ сервера, прежде чем пытаться его использовать.

async function getUsersNames () { try {// Оборачиваем всё в try...catch — это сеть для ловли ошибок const response = await fetch (»https://jsonplaceholder.typicode.com/users-produsers'); // ГЛАВНАЯ ПРОВЕРКА! if (!response. ok) { // Если статус не 2xx, выбрасываем ошибку сами throw new Error (`HTTP error! status: ${response.status} `); } const users = await response. json (); if (!users || users. length === 0) { // Проверяем, а есть ли вообще пользователи return []; // Возвращаем пустой массив, а не падаем } const names = users.map (user => user.name); return names; } catch (error) { console.error («Ой, всё сломалось при получении пользователей:», error); return []; // В случае любой ошибки возвращаем пустой массив, чтобы остальная часть программы не упала } }

Вот теперь наш код одет в броню. Он готов к суровой реальности. Он проверяет ответ сервера и ловит любые ошибки в блоке try...catch.

Учимся задавать правильные промпты: от джуна к сеньору

Мы починили код. Но вайб-кодер не просто чинит, он учится, чтобы не повторять ошибок. Проблема была не только в AI. Проблема была в нашем промпте. Он был слишком наивным.

Промпт уровня «Джуниор»:

// Напиши функцию, которая получает имена пользователей с API.

Промпт уровня «Миддл»:

// Напиши асинхронную функцию getUsersNames, которая делает GET-запрос на «…', получает массив пользователей и возвращает новый массив только с их именами. (Это то, что мы написали).

А вот как выглядит промпт уровня «Сеньор»:

// Напиши асинхронную функцию getUsersNames, которая делает GET-запрос на «…'. Функция должна быть надёжной. Добавь обработку сетевых ошибок с помощью try...catch. Если ответ сервера не успешный (статус не 200—299), функция должна выбрасывать ошибку с текстом статуса. Если в ответе пришёл пустой массив или вообще не массив, функция должна возвращать пустой массив, не падая. В случае любой другой ошибки, она также должна возвращать пустой массив и логировать ошибку в консоль.

Чувствуешь разницу? Сеньорский промпт не просто говорит, что делать. Он предвидит проблемы и говорит, как на них реагировать. Он описывает не только «happy path», но и все «sad paths».

Если ты дашь AI такой промпт, он сгенерит тебе тот самый, надёжный код, который мы написали руками.

Вывод: ты — навигатор, а не пассажир

Эта первая ошибка — твой обряд инициации. Ты понял, что AI — не волшебник. Он — невероятно мощный, но очень буквальный исполнитель. Он не будет думать за тебя об edge-кейсах.

Твоя новая работа — быть параноиком. Быть тем самым душным тимлидом для своего AI-стажёра. Прежде чем писать промпт, задай себе вопросы:

— А что, если данные придут пустые?

— А что, если придёт не тот тип данных (не массив, а объект)?

— А что, если сеть упадёт?

— А что, если сервер вернёт ошибку?

Думать о пограничных случаях, об ошибках, об архитектуре — вот твоя настоящая работа. А рутину по написанию кода можешь смело делегировать своему кремниевому помощнику.


Совет от гаражного кодера:

Сделай «чек-лист параноика». Прежде чем принять AI-код, который работает с внешними данными (API, файлы), прогони его по списку: «Обработан ли null/undefined? Есть ли проверка на пустой массив/объект? Обернуто ли в try...catch? Проверяется ли статус ответа?».

Используй чат для дебага. Когда получаешь ошибку, не спеши гуглить. Выдели код вместе с ошибкой, скопируй в AI-чат (Copilot Chat/Cursor) и спроси: «Я получаю вот такую ошибку. Объясни, что она значит и как её исправить в этом коде?». Он часто даёт очень точные и полезные объяснения.

Пиши промпты итеративно. Не пытайся сразу написать идеальный сеньорский промпт. Начни с простого, получи базовый код. А потом используй Ctrl+K (в Cursor) или чат, чтобы доработать его: «А теперь добавь сюда обработку ошибок», «А теперь сделай так, чтобы он возвращал пустой массив при сбое».

Создай коллекцию «хороших промптов». Если ты часто выполняешь однотипные задачи (например, создаёшь React-компонент с подключением к Redux), сохрани себе где-нибудь в заметках идеальный, подробный промпт для этой задачи. Это твой личный арсенал заготовок.

Часть 2. Основы под присмотром: учимся, а не просто копипастим

Глава 4. Логика и данные: когда AI твой штурман, а не пилот

— Переменные, типы, условия. Просим AI написать функцию и проверяем её на косяки (например, == вместо ===)

Представь, что мы пилим фичу для какого-нибудь сайта с платным контентом. Нам нужна функция, которая будет решать, можно ли показать пользователю секретную статью или нет. Классическая задача, которую ты будешь решать сотни раз в своей карьере.

Давай сформулируем для нашего AI-помощника промпт. Сделаем его достаточно чётким, но оставим немного пространства для «творчества».


// Напиши функцию canAccessContent, которая принимает два аргумента:

// 1. age (возраст пользователя, число)

// 2. subscription (статус подписки, строка: ’free’, ’pro’, ’admin’)

// Функция должна возвращать true, если:

// — у пользователя подписка ’admin’ (им можно всё, независимо от возраста)

// — ИЛИ у пользователя подписка ’pro’ И ему 18 или больше лет.

// Во всех остальных случаях функция должна возвращать false.


Отправляем этот приказ в нашу IDE и ждём. Copilot или Cursor, почесав свои кремниевые мозги, скорее всего, выдаст нам что-то очень похожее на это:

function canAccessContent (age, subscription) { if (subscription == ’admin’ || (subscription == ’pro’ && age> = 18)) { return true; } else { return false; } }

Первый взгляд: вроде всё чики-пуки

Давай посмотрим на этот код глазами джуна. Выглядит логично. Условие в if полностью повторяет наше ТЗ. || — это «ИЛИ», && — это «И». Кажется, всё работает.

Давай проведём быстрый тест-драйв, проверим основные сценарии («happy paths»):

console. log («Админ, 16 лет:», canAccessContent (16, ’admin’)); // Ожидаем: true console. log («Pro, 25 лет:», canAccessContent (25, ’pro’)); // Ожидаем: true console. log («Pro, 17 лет:», canAccessContent (17, ’pro’)); // Ожидаем: false console. log («Free, 30 лет:», canAccessContent (30, ’free’)); // Ожидаем: false Запускаем… и получаем: Админ, 16 лет: true Pro, 25 лет: true Pro, 17 лет: false Free, 30 лет: false

Идеально! Задача решена, таск можно закрывать. AI — гений, я — молодец.

А теперь, братан, стоп. Выдыхай. Это была ловушка. Ловушка, в которую попадаются 9 из 10 новичков. Мы проверили только идеальные случаи. Но реальный мир, как мы знаем, далёк от идеала. Данные могут приходить с фронтенда в виде строк, API может вернуть null, пользователь может вообще ничего не ввести.

Второй взгляд: достаём сеньорскую лупу

Давай посмотрим на одну маленькую, но дьявольски коварную деталь в сгенерированном коде.

if (subscription == ’admin’…

Видишь? ==. Два знака равенства.

В мире JavaScript это не просто оператор сравнения. Это «оператор нестрогого, или абстрактного, сравнения». И он — источник самых безумных и трудноуловимых багов.

Что делает ==?

Он пытается быть твоим другом. Слишком хорошим другом. Прежде чем сравнить две переменные, он пытается привести их к одному типу. Этот процесс называется приведение типов (type coercion). Иногда это удобно, но чаще всего это приводит к хаосу.

Например:

5 == «5» вернёт true. JS видит число и строку и думает: «Хм, наверное, они имели в виду числа», и сравнивает 5 с 5.

false == 0 вернёт true.

null == undefined вернёт true.

«« == false вернёт true.

Это безумие. Это как если бы твой GPS на команду «поехали в Питер» иногда вёз тебя в Павловск, потому что «ну, названия же похожи».

Атакуем нашу функцию: ищем уязвимости

Давай представим, что с фронтенда нам пришёл возраст не как число, а как строка. Или что-то пошло не так, и в subscription прилетело что-то странное.

Давай создадим другую, более простую функцию, чтобы наглядно увидеть этот ад. Попросим AI:

// Напиши функцию, которая проверяет, равно ли значение false

AI может сгенерить:

function isItFalse (value) { if (value == false) { return true; } return false; }

А теперь смотри фокус:

console. log (isItFalse (0)); // Выведет: true. Ноль — это не false! console. log (isItFalse (»»)); // Выведет: true. Пустая строка — это не false! console. log (isItFalse ([])); // Выведет: true (в некоторых движках). Пустой массив — это не false!

Вот она, мина, которую нам подложил наш услужливый AI. Он написал синтаксически верный, но логически хрупкий и опасный код.

Фикс: === — наш бронежилет

Вайб-кодеры знают правило, выжженное на сердце годами дебага: всегда используй === (оператор строгого сравнения).

Что делает ===?

Он — суровый, но справедливый судья. Он не пытается никого мирить. Он сначала проверяет, совпадают ли типы переменных. Если типы разные (например, число и строка) — он сразу возвращает false, даже не смотря на значения. И только если типы одинаковые, он сравнивает значения.

5 === «5» вернёт false. Типы разные. Точка.

false === 0 вернёт false.

null === undefined вернёт false.

Это предсказуемость. Это надёжность. Это твой главный инструмент против безумия JavaScript.

Давай починим наш код руками.

function canAccessContent (age, subscription) { // Заменяем все '==' на '===» if (subscription === ’admin’ || (subscription === ’pro’ && age> = 18)) { return true; } else { return false; } }

А теперь давай сделаем его ещё чище, как настоящий сеньор. Весь этот if-else для возврата true/false — избыточен. Ведь само условие уже возвращает boolean.

Вот. Коротко, чисто, надёжно. И это то, до чего мы дошли своим мозгом, проанализировав и улучшив то, что предложил AI. Мы не просто приняли код, мы его отрефакторили.

Учимся правильно командовать: сеньорский промпт 2.0

Как мы могли бы избежать этой ошибки с самого начала? Сформулировав более точный приказ.

Наш джуниорский промпт был хорош, но в нём не хватало одного ключевого требования.

Промпт уровня «Сеньор»:

// … (всё то же самое) … Используй операторы строгого сравнения (===) для всех проверок.

Если ты добавишь это уточнение, AI с гораздо большей вероятностью сгенерит тебе сразу правильный, надёжный код. Ты, как пилот, должен давать своему штурману не только пункт назначения, но и важные указания по маршруту («избегай гравийных дорог», «не превышай скорость»).

Вывод: AI — это ускоритель, а не мыслитель

Эта простая функция вскрыла самую суть нашей новой работы.

AI взял на себя рутину: написал синтаксически правильную конструкцию function, if-else, return. Он сэкономил нам 30 секунд печатания.

Но логическую ответственность он оставил на нас. Он не подумал о хрупкости ==. Он не подумал о чистоте и лаконичности кода. Он просто выдал самый статистически вероятный ответ на наш запрос.

Наша задача как пилотов — не просто нажимать Tab. Наша задача — быть главным ревьюером, главным QA-инженером для нашего AI-помощника. Мы должны смотреть на сгенерированный код и задавать вопросы:

— А он надёжный?

— А он безопасный?

— А он выдержит плохие данные?

— А можно ли его написать чище?

Переменные, типы и логика — это не то, что можно делегировать. Это фундамент. И за качество этого фундамента отвечаем только мы. AI может подвезти нам кирпичи, но кладём мы их сами.

Совет от гаражного кодера:

Сделай === своей религией. Используй его всегда и везде. Единственный случай, когда == может быть оправдан — это проверка variable == null, которая отлавливает и null, и undefined разом. Но даже это многие считают дурным тоном. Проще быть последовательным.

Настрой ESLint. Это твой автоматический страж порядка. Установи плагин ESLint для своей IDE и настрой в нём правило «eqeqeq»: [«error», «always»]. После этого IDE будет подсвечивать красным любую попытку использовать ==, заставляя тебя писать правильно.

Валидируй данные на входе. Прежде чем работать с данными, пришедшими извне (от пользователя, из API), убедись, что они того типа, который ты ожидаешь. Если ждёшь число age, преобразуй его явно: const numericAge = parseInt (age, 10);. Никогда не доверяй внешним данным.

Думай об «обратном» условии. Когда пишешь логику, всегда думай не только о том, когда она вернёт true, но и о том, когда она вернёт false. Это помогает находить слабые места. «А что, если subscription будет null? А если age будет отрицательным?». Это развивает защитное мышление.

— Рофлим над тем, как Copilot предлагает три разных способа написать цикл for, и все — неоптимальные

Давай поставим нашему AI простую, как три копейки, задачу. У нас есть массив чисел, и нам нужно посчитать их сумму. Это «Hello, World!» в мире циклов.

Формулируем промпт. Ничего заумного, просто и по делу.

// Напиши функцию sumArray, которая принимает массив чисел

// и возвращает их сумму.


Мы ожидаем увидеть что-то простое и элегантное. Но наш AI-штурман, видимо, решил блеснуть эрудицией. Он, как студент-отличник, хочет показать, что знает не один, а сразу несколько способов решения. И он начинает нам их предлагать.

Нажимаем Ctrl+Enter (или используем другую команду для просмотра альтернатив), и начинается парад… не самых лучших идей.

Вариант №1: «Классика жанра» от деда

Первое, что часто предлагает Copilot, — это самый древний, самый кондовый способ, который был с нами со времен динозавров и jQuery.

function sumArray (arr) { let sum = 0; for (let i = 0; i <arr. length; i++) { sum += arr [i]; } return sum; }

Что мы видим?

Это классический цикл for с итератором i. Он работает? Да. Он понятен? Вполне. Он хороший? Ну… как сказать.

Анализ от гаражного кодера:

Многословность: Мы создаём переменную i, задаём ей начальное значение, пишем условие i как итерировать, а не что мы хотим сделать.

Риск ошибки «off-by-one»: Новичок легко может ошибиться и написать i <= arr. length, что приведёт к выходу за пределы массива и получению undefined, который при сложении с числом даст NaN. Привет, часы дебага.

Производительность (микро-занудство): На каждой итерации происходит обращение к arr. length. В современных движках JS это оптимизировано, но в старых браузерах или на очень больших массивах это могло быть микро-проблемой. Раньше «тру-кодеры» выносили const len = arr. length; за пределы цикла. AI об этом, видимо, тоже читал.

Вердикт: Это как ехать на старой дедовской «Волге». Она довезёт тебя из пункта А в пункт Б. Но она жрёт много бензина (твоего внимания), громоздкая и не очень безопасная. AI предложил это, потому что таких циклов — миллиарды в коде, на котором он учился. Это самый статистически вероятный ответ.

Вариант №2: «Модный хипстер» for...in

Окей, мы просим AI показать что-то ещё. И он, решив быть более современным, предлагает нам for...in.

function sumArray (arr) { let sum = 0; for (const index in arr) { sum += arr [index]; } return sum; }

Что мы видим?

Выглядит чище! Нет этого уродливого i=0; i <…; i++. Кажется, это шаг вперёд.

bannerbanner