От 10 строк к полной E2E-автоматизации: как вайб-кодинг работает на практике

Я начал с 10 строк JavaScript, которые просто открывали браузер и кликали по кнопкам. Через 17 последовательных промптов к Claude Code это превратилось в 142-строчный скрипт с прокси-ротацией, перехватом clipboard и рекурсивной retry-логикой - и всё на одной зависимости.


Почему я не написал один большой промпт

Первый инстинкт был именно таким: описать всё сразу. “Мне нужен Playwright-скрипт, который открывает веб-сервис, регистрирует аккаунт через сервис временной почты, дожидается письма с кодом, копирует код из буфера обмена, использует прокси из файла и запускается 50 раз подряд”. Звучит как нормальное ТЗ. На практике AI генерирует что-то похожее на правду, но когда запускаешь - половина не работает, и непонятно с какого конца чинить.

Я попробовал по-другому. Первый промпт: “напиши простой Playwright-скрипт, открой 2 вкладки”. Результат - 15 строк, которые работали. С этого момента каждый следующий промпт добавлял одну конкретную вещь.

Это называется incremental prompting - подход, при котором задача разбивается на последовательные подзадачи. Каждая следующая опирается на результат предыдущей. Когда что-то ломается, сразу понятно что именно и в каком шаге. Контроль над генерацией принципиально другой: ты видишь каждое изменение в изоляции и можешь откатиться к последней рабочей точке.


Как скрипт эволюционировал шаг за шагом

Первые три шага были базовые: открыть страницу, добавить прокси, выставить локаль EN-US. 15 строк превратились в 30. Всё работало, можно двигаться дальше.

Четвёртый промпт принёс переход к сервису временной почты вместо Google-авторизации. Здесь же - снятие чекбоксов на форме регистрации. Пятая и шестая итерации добавили генерацию случайного email, заполнение формы, polling-цикл ожидания письма с кодом подтверждения.

Седьмой промпт стал первым серьёзным препятствием. Скрипт падал с ошибкой:

Error: strict mode violation: getByRole('button', { name: 'Continue' })
resolved to 2 elements

На форме было две кнопки с текстом “Continue”: обычная и “Continue with Google”. Playwright требует, чтобы локатор указывал ровно на один элемент - это intentional поведение, которое защищает от случайного клика не туда. Решилось одной опцией:

page.getByRole('button', { name: 'Continue', exact: true })

С exact: true Playwright ищет точное совпадение текста, а не подстроку. Кнопка “Continue with Google” перестала матчиться.

Промпты 8-9 - извлечение кода верификации из DOM письма. Следующая пара добавила обработку ситуации “email недоступен” - сервис временной почты иногда отказывал. Решение: рекурсивная retry-логика, где функция generateEmail() вызывает саму себя при ошибке. Не exponential backoff, а именно рекурсия - проще и достаточно для этой задачи.

Двенадцатый промпт оказался самым интересным технически.


Три технических момента, которые стоит запомнить

Перехват clipboard через monkey-patching. Веб-сервис генерировал API-ключ и клал его в буфер обмена через встроенную кнопку “Copy”. Задача: этот ключ нужно сохранить в файл. Проблема: Playwright не может напрямую читать clipboard после того, как страница сама туда записала.

Решение - подменить navigator.clipboard.writeText до загрузки страницы:

await page.addInitScript(() => {
  const original = navigator.clipboard.writeText.bind(navigator.clipboard);
  navigator.clipboard.writeText = async (text) => {
    window.__copiedText = text;
    return original(text);
  };
});

// После клика на кнопку "Copy":
const apiKey = await page.evaluate(() => window.__copiedText);

Скрипт встраивается до инициализации страницы, перехватывает запись в буфер и параллельно сохраняет значение в window.__copiedText. Никаких permissions, никаких дополнительных зависимостей.

Прокси-ротация из файла. После того как без прокси скрипт заблокировали уже на третьей попытке, добавили чтение прокси из proxies.txt и round-robin ротацию:

const proxies = fs.readFileSync('proxies.txt', 'utf8')
  .split('\n').filter(Boolean);

// В цикле на TOTAL_RUNS итераций:
const proxy = proxies[attempt % proxies.length];
const context = await browser.newContext({
  proxy: { server: proxy }
});

Каждый новый контекст получает следующий прокси по кругу. Прокси задаётся на уровне context, а не browser - это даёт изоляцию сессий между запусками.

Параметризация через env. Количество запусков задаётся переменной окружения:

const TOTAL_RUNS = parseInt(process.env.TOTAL_RUNS || '50');

Дефолт 50, но для отладки достаточно TOTAL_RUNS=3 node script.js.


Как я промптировал

Структура, которая сработала лучше всего: current state + desired state + constraint.

“У нас есть функция generateEmail(). Она иногда получает ошибку ’email unavailable’ от сервиса. Добавь рекурсивную retry-логику - при ошибке функция должна вызывать саму себя и генерировать новый email. Без дополнительных зависимостей.”

Три компонента: что есть, что хочу, что нельзя. Claude не переписывал всё с нуля, а точечно добавлял нужное. И это работает именно потому, что контекст сессии сохраняется - не нужно каждый раз прикладывать весь файл.

На тринадцатом шаге я добавил: “после успешного сохранения результата - вызывай browser.close()”. До этого скрипт не закрывал браузер при выходе, и после нескольких запусков в памяти висели zombie-процессы. Одно предложение в промпте - три строки в коде - проблема исчезла.

Пятнадцатый промпт добавил логику пропуска: если письмо с кодом не пришло за 10 попыток polling - пропускаем итерацию и переходим к следующей. Без этого скрипт мог зависнуть на одном аккаунте навсегда.


Что получилось в итоге

142 строки CommonJS. Одна зависимость - playwright. Скрипт запускается с TOTAL_RUNS=100 node script.js, читает прокси из файла, ротирует их по кругу, регистрирует аккаунты через сервис временной почты, перехватывает API-ключи из clipboard и сохраняет результаты в JSON. После финализации проработал автономно 4.5 часа.

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


Что я вынес из этих 17 промптов

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

Конкретность промпта важна. “Добавь прокси” и “добавь прокси-ротацию из файла proxies.txt с round-robin по модулю” - это принципиально разные промпты с принципиально разными результатами.

Strict mode violations - это не баги, это Playwright говорит “твой селектор неоднозначен”. Лучше поймать это здесь, чем когда скрипт тихо кликает не туда.

Рекурсия для retry бывает чище exponential backoff - зависит от задачи. Если нужно просто попробовать снова с другими данными (другой email), рекурсивный вызов читается понятнее.


Вайб-кодинг в 2026 - это не “написал один хороший промпт и готово”. Это диалог, где каждый следующий вопрос чуть сложнее предыдущего. AI хорошо держит контекст, хорошо добавляет конкретную фичу к работающему коду, хорошо объясняет почему что-то сломалось. Плохо справляется с “сделай всё сразу и чтобы работало”.

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