Как маркетолог собрал внутренний дашборд за 3 вечера без навыков программирования: реальный опыт с вайбкодингом
Что такое вайбкодинг и почему это не про программирование
Вайбкодинг выглядит так: открываешь Cursor или Lovable, пишешь "сделай мне лендинг с формой захвата и таймером обратного отсчёта", и через несколько секунд у тебя есть рабочий HTML с JavaScript. Ты не написал ни строчки кода. И не должен был.
Термин придумал Андрей Карпати в феврале 2025-го. Он описал процесс, когда полностью отдаёшь написание кода модели, а сам остаёшься в роли человека, который формулирует задачу, смотрит на результат и говорит "нет, кнопка должна быть красной и форма должна отправлять данные в Google Sheets". Это не программирование. Это постановка задачи.
Многие путают с no-code. Разница принципиальная. Bubble или Webflow дают тебе конструктор: перетаскиваешь блоки, настраиваешь логику кликами. Ты работаешь внутри чужой системы с чужими ограничениями. Вайбкодинг генерирует реальный исходный код под твою конкретную задачу. Хочешь нестандартную логику A/B-теста? Хочешь парсить данные из нескольких источников и складывать в свою таблицу? Просто описываешь. Код появляется именно под это.
В 2026 году порог входа стал смешным. Replit Agent разворачивает рабочее приложение за один промпт. Lovable в большинстве случаев понимает задачу со второй итерации. Люди без технического бэкграунда осваивают эти инструменты за вечер, я видел это на практике, не метафорически.
И вот почему маркетолог здесь оказывается в неожиданно выгодной позиции. Узкое место вайбкодинга никогда не было в коде. Оно в постановке задачи. Нужно точно знать, что ты хочешь получить, уметь описать это словами, понимать, какую метрику проверять, чтобы убедиться: оно работает. Маркетолог делает это каждый день. Пишет бриф агентству, ставит задачу дизайнеру, формулирует гипотезу для теста. Навык уже есть. Инструмент просто поменялся.

Весь процесс начинается с обычного текстового описания: что нужно показать, откуда брать данные и как это должно выглядеть.
Задача: какой дашборд нужен и зачем его строить самому
Конкретная ситуация: маркетолог каждую пятницу открывает три вкладки браузера, выгружает отчёты из Google Ads, заходит в Mailchimp за показателями email-рассылок, тащит цифры из Google Sheets с CRM-данными, и всё это сводит руками в Excel. Час работы, которая не создаёт никакой ценности. Просто перекладывание цифр из одного места в другое.
Казалось бы, есть готовые решения. Looker Studio бесплатный, Power BI популярен. Но дьявол в коннекторах: нужный источник либо требует платного плана, либо официальный коннектор тянет не те поля, либо нужно возиться с OAuth и правами доступа дольше, чем пишется свой скрипт. Power BI вообще заточен под экосистему Microsoft, и если данные живут в Google-инструментах, настройка превращается в отдельный проект.
Самописный вариант решает конкретно эту задачу: берёт данные из Google Sheets (куда уже стекается всё нужное), считает нужные метрики и показывает их на одном экране. Кнопка "Обновить" запускает подтягивание актуальных данных. Ничего лишнего.
Я говорю про дашборд с 4-6 виджетами: динамика по неделям, ключевые показатели в виде чисел сверху, пара графиков. Не BI-система, не аналитическая платформа. Рабочий инструмент под одну задачу.
Три вечера по 2-3 часа, это реалистичный срок, если не пытаться сразу сделать "как в Tableau". Первый вечер: настройка получения данных и базовая структура. Второй: визуализация. Третий: полировка и деплой. Переусложнение убивает такие проекты быстрее всего, я видел это много раз.

Три разрозненных источника, например CSV, API и база данных, сводятся в одну таблицу ещё до того, как Plotly рисует первый график.
Инструменты: Cursor, Lovable или Replit, что выбрать новичку
Три инструмента, которые сейчас реально используют для вайбкодинга. Все три разные по философии, и выбор зависит от одного вопроса: тебя пугает терминал или нет?
Cursor, это VS Code с встроенным ИИ. Если ты хоть раз открывал VS Code или хотя бы видел его на скриншотах, интерфейс будет знакомым. Cursor работает локально: скачиваешь, устанавливаешь, пишешь код прямо на своей машине. Бесплатный план есть, Pro стоит $20 в месяц. По моему опыту, в паре с Claude 3.7 он давал чистый, рабочий код с первой-второй попытки на задаче с дашбордом на Google Sheets, тогда как другие инструменты требовали больше итераций.
Но есть порог входа. Нужно установить Python или Node, разобраться с виртуальными окружениями, понять, как запустить сервер локально. Для кого-то это 20 минут. Для кого-то это стена.
Lovable работает в браузере. Заходишь, описываешь что хочешь, получаешь React-приложение. Ничего не надо устанавливать, ни терминала, ни пакетных менеджеров. Если ты впервые видишь редактор кода вообще, Lovable снижает тревогу почти до нуля. Генерирует фронтенд быстро, деплой встроен. Минус: меньше контроля над тем, что происходит под капотом, и сложнее делать что-то нестандартное.
Replit занимает среднюю позицию. Облачная среда, где и код, и выполнение, и деплой происходят в браузере, но при этом ты видишь файлы, видишь терминал, можешь что-то руками поправить. ИИ-агент Replit сам ставит зависимости и запускает проект. Хороший выбор, если хочешь чуть больше понимать, что происходит, но ещё не готов к полноценной локальной разработке.
Мой совет такой. Если задача конкретная и техническая, скажем, подключиться к Google Sheets, обработать данные, вывести графики, бери Cursor и Claude 3.7. Да, придётся один раз разобраться с установкой, но результат будет точнее. Если задача больше про UI, лендинг, простое веб-приложение, и терминал вызывает дискомфорт, Lovable закроет потребность без лишнего стресса.
Вечер первый: настройка среды и первый рабочий запрос
Cursor скачивается с cursor.com. Установка, вход через GitHub или Google, создание пустого проекта. Всё занимает минут десять, не больше.
Открываешь папку проекта, нажимаешь Cmd+K (или Ctrl+K на Windows) и видишь чат с ИИ прямо в редакторе. Теперь важная часть: что туда написать.
Первый промпт должен быть максимально скучным и конкретным. Не "помоги мне сделать дашборд", а что-то вроде:
"Создай HTML-страницу с одной таблицей, которая читает данные из Google Sheets по этому URL и показывает колонки: дата, канал, клики, расходы."
Чем конкретнее, тем меньше мусорного кода получишь в ответ.
Подготовить Google Sheets нужно заранее. Заходишь в таблицу, жмёшь "Поделиться", выбираешь "Все, у кого есть ссылка, могут просматривать". Это открывает доступ к CSV-экспорту без OAuth и API-ключей. URL для запроса выглядит так:
https://docs.google.com/spreadsheets/d/[ID]/export?format=csv
Cursor напишет примерно такой код:
// Промпт был: "Напиши fetch-запрос к Google Sheets CSV URL,
// распарси данные и выведи их в HTML-таблицу."
fetch('https://docs.google.com/spreadsheets/d/[ID]/export?format=csv')
.then(res => res.text())
.then(csv => {
const rows = csv.split('\n').map(r => r.split(','));
const table = document.querySelector('#data-table');
rows.forEach(row => {
const tr = document.createElement('tr');
row.forEach(cell => {
const td = document.createElement('td');
td.textContent = cell;
tr.appendChild(td);
});
table.appendChild(tr);
});
});
Открываешь HTML-файл в браузере, смотришь в консоль. С вероятностью процентов восемьдесят там будет ошибка CORS: браузер заблокировал запрос, потому что страница открыта как локальный файл. Не надо разбираться почему. Просто копируешь текст ошибки целиком и вставляешь в чат Cursor. Он предложит запустить через локальный сервер или добавить проксирование. Делаешь то, что написал.
После второй-третьей итерации таблица появится. Данные из Google Sheets, в браузере, без бэкенда и без единой строки, написанной вручную.
Это и есть итог первого вечера: работающая страница с реальными данными. Ничего впечатляющего визуально, но механика понятна. Дальше будет быстрее.

Cursor позволяет писать промпт прямо рядом с кодом, так что ИИ видит весь контекст файла, а не только текст вопроса.
Вечер второй: графики, фильтры и нормальный вид
Второй вечер я начал с того, что открыл вчерашний HTML и понял: таблица работает, данные грузятся, но смотреть на это невозможно. Голый Bootstrap, серый фон, ноль контекста. Надо добавить график и хоть какую-то жизнь.
Первый промпт был простым:
"Подключи Chart.js из CDN и нарисуй линейный график расходов по датам на основе уже загруженных данных"
Клод знает Chart.js хорошо. Через минуту у меня был рабочий canvas с осью X по датам и осью Y по расходам. Единственное, что пришлось поправить руками: порядок сортировки дат. Данные в CSV шли не хронологически, и график выглядел как кардиограмма паникующего человека. Добавил один промпт про sort() и стало нормально.
Дальше попробовал схитрить и запросить сразу три вещи: фильтр по каналу, второй график с разбивкой по каналам, и экспорт в PNG. Получил полурабочий код с конфликтующими обработчиками событий. Урок очевидный: один промпт, одна функция. Это не лень, это просто работает лучше.
Откатился и сделал фильтр отдельно:
"Добавь выпадающий список с уникальными значениями из колонки Channel. При выборе значения перестраивается и таблица, и график одновременно"
Здесь Клод справился хорошо. Список заполнялся динамически из данных, при смене значения вызывались обе функции рендеринга. Связка таблица+график обновлялась синхронно.
Потом добавил условное форматирование. Промпт:
"Если значение в колонке Расходы больше 5000, покрась строку красным"
Три строки кода в функции рендеринга таблицы. Работает. Я добавил ещё порог на 3000 (жёлтый) своим руками, потому что это проще написать самому, чем объяснять.
Последним шёл косметический промпт:
"Сделай страницу чище: белый фон, шрифт Inter, карточки с тенями, без таблицы по умолчанию Bootstrap"
Результат был 80% готов сразу. Клод подключил Inter через Google Fonts, обернул блоки в карточки с box-shadow, убрал стандартные Bootstrap-стили с таблицы. Пришлось подправить отступы и цвета вручную, но это заняло минут десять в DevTools.
К концу второго вечера на экране был дашборд с линейным графиком расходов по времени, фильтром по каналу, таблицей с цветовым выделением строк и хоть каким-то приличным видом. Второй график (по каналам) я добавил в самом конце, уже отдельным промптом, и он встал на место без проблем.
Итого два вечера, около четырёх часов суммарно, и что-то, что реально можно открыть на рабочем совещании без стыда.

Условное форматирование строк здесь работает через callback: цвет меняется в зависимости от значения в колонке, без перезагрузки страницы.
Вечер третий: подключение второго источника и деплой
К этому моменту у меня уже был дашборд с рекламными данными из одного листа. Задача вечера: добавить открываемость писем из Mailchimp и выложить всё по нормальной ссылке.
Mailchimp → Google Sheets. Прямой интеграции через бесплатный план нет, поэтому я пошёл простым путём: экспорт отчёта из Mailchimp в CSV, затем импорт в отдельный лист той же таблицы. Если хочешь автоматизировать, Nodul умеет забирать данные из Mailchimp по расписанию и писать их в Sheets без кода. Но для первого раза ручной экспорт раз в неделю вполне рабочий вариант.
Второй лист опубликовал так же, как первый: "Файл → Опубликовать в интернете → CSV". Получил второй URL.
Промпт для объединения двух источников:
Загрузи данные из двух CSV-URL, объедини по колонке Дата,
покажи рядом расходы на рекламу и открываемость писем.
URL 1: [ссылка на лист с рекламой]
URL 2: [ссылка на лист с Mailchimp]
Cursor написал функцию слияния примерно за минуту. Логика простая: два массива объектов, join по полю date, результат идёт в общий график. Где данные не совпали по датам, там пробелы. Это честно и не вводит в заблуждение.
Кнопка "Обновить данные". Перезагружать страницу каждый раз неудобно. Попросил добавить кнопку, которая делает повторный fetch без перехода:
// Промпт для Cursor:
// 'Добавь кнопку Обновить. При клике она заново
// загружает оба CSV и перерисовывает графики и таблицу.
// Пока данные грузятся, показывай спиннер.'
document.getElementById('refresh-btn').addEventListener('click', () => {
showSpinner();
Promise.all([fetchSheet1(), fetchSheet2()])
.then(([data1, data2]) => {
renderCharts(data1, data2);
hideSpinner();
});
});
Cursor написал этот код с первой попытки. Спиннер сделал через CSS-анимацию, которую тоже сгенерировал сам. Я только проверил, что кнопка видна на мобиле.
Деплой через Netlify Drop. Открываешь netlify.com/drop, перетаскиваешь папку с index.html, script.js и style.css в браузер. Через 30 секунд получаешь ссылку вида https://random-name-12345.netlify.app. Бесплатно, без регистрации, без настройки сервера.
Если на первом вечере создал репозиторий на GitHub, то альтернатива: включаешь GitHub Pages в настройках репо, и ссылка будет обновляться автоматически при каждом git push. Это удобнее в длинной перспективе, но требует лишних двух шагов при старте.
Я выбрал Netlify Drop, потому что хотел показать дашборд коллеге в тот же вечер. Скинул ссылку в Slack.
Что получилось за три вечера. Дашборд живёт по ссылке, открывается без логина, показывает рекламные расходы и открываемость рядом. Данные обновляются: заходишь в Google Sheets, правишь строки, нажимаешь "Обновить" на странице, видишь изменения. Команда может смотреть с телефона. Это не Tableau и не Looker Studio, но для еженедельного отчёта по двум метрикам хватает с запасом.
Весь проект занял около шести часов, включая эксперименты и перезапросы к Cursor, хотя ваши результаты могут отличаться в зависимости от сложности данных и числа итераций.
Как правильно писать промпты: три правила, которые реально работают
Плохой промпт съедает время быстрее, чем любая ошибка в коде. Я потратил несколько недель на то, чтобы понять простую вещь: проблема почти никогда не в модели. Проблема в том, как сформулирован запрос.
Одна задача за один промпт. Это самое важное. Запрос "добавь фильтр по каналу, поменяй цвета таблицы и сделай экспорт в PDF" звучит как нормальное техническое задание, но для ИИ это три разных задачи с потенциально конфликтующими изменениями. Модель начинает угадывать приоритеты и часто ломает то, что уже работало. Пиши "добавь фильтр по каналу" и дожидайся результата. Потом следующий промпт.
Давай контекст через @-упоминания. В Cursor это буквально встроено в интерфейс. Вместо "добавь колонку Конверсия в таблицу" пиши @index.html добавь в таблицу колонку Конверсия. Модель видит конкретный файл, конкретную структуру, и вероятность того, что она вставит код в правильное место, резко вырастает. Без @-упоминания она работает по памяти о том, что ты ей показывал раньше. Это ненадёжно.
Копируй stack trace целиком. Не пересказывай "там что-то сломалось с таблицей". Вставь весь текст ошибки: тип исключения, строку файла, полный trace. Разница в качестве ответа колоссальная. Пересказ ошибки заставляет модель строить гипотезы. Полный trace даёт ей точные координаты.
Есть ещё один приём, который я использую регулярно: после того как ИИ написал кусок кода, прошу его объяснить, что именно он сделал. Не ради педагогики. Это работает как проверка. Иногда в объяснении всплывает что-то вроде "я предположил, что у вас нет авторизации на этом роуте", и сразу понятно, что логика построена на ошибочном допущении.
Чего точно не делать: "сделай красиво" без примера. ИИ не знает твой вкус. Либо дай скриншот референса, либо назови конкретный источник: "сделай в стиле дашборда Linear" или "как таблицы в Notion". Конкретный референс даёт модели якорь. Абстрактное "красиво" даёт ей полную свободу, и результат будет... нейтральным.

Конкретный формат данных, ожидаемый результат и пример крайнего случая, вот три вещи, которые стоит прописать в любом промпте к ИИ.
Ограничения и честные предупреждения
Перед тем как запускать это в продакшн, нужно понимать несколько вещей. Не чтобы напугать, а потому что столкнётесь с ними раньше, чем ожидаете.
Google Sheets как источник данных работает только если лист публичный. CSV-экспорт через gviz/tq?tqx=out:csv не спрашивает авторизацию. Как только лист переходит в "только для тех, у кого есть доступ", fetch возвращает HTML-страницу с формой входа вместо данных. Для закрытых таблиц нужен либо OAuth 2.0, либо сервисный аккаунт Google Cloud с JSON-ключом. Это уже не вайбкодинг за вечер.
Дашборд показывает данные на момент последнего обновления страницы. Если менеджер обновил таблицу три минуты назад, а кто-то смотрит дашборд без перезагрузки, он видит старые цифры. "Реальное время" здесь не при чём. Можно настроить автообновление через setInterval каждые 30-60 секунд, но тогда растёт нагрузка на API Google. Для большинства внутренних дашбордов это нормально. Для трейдинга или мониторинга серверов, нет.
Cursor иногда ломает то, что работало. Особенно когда просишь "немного поправить стили" и получаешь переписанный блок логики. Если после правки что-то перестало работать, первый шаг: Cmd+Z несколько раз. Если не помогает, пишите в чат: "верни код к состоянию до последнего изменения" или "что именно ты изменил в этом блоке". Cursor помнит контекст сессии и обычно восстанавливает. Но если закрыли вкладку, контекст потерян. Поэтому коммитьте в git хотя бы раз в час.
Дальше честная граница применимости.
По мере роста кодовой базы поддерживать её в режиме вайбкодинга становится сложнее. Когда проект разрастается, появляются симптомы отсутствия архитектуры: функции дублируются, переменные называются похоже, логика размазана по файлу. Когда придёт задача "добавь фильтр по региону и экспорт в PDF", Cursor начнёт предлагать решения, которые конфликтуют с тем, что уже написано. Вы будете фиксить баг, который сами не понимаете. Именно эти ситуации разбирает разбор реальных сломов при AI-разработке в продакшене.
Если дашборд вырастает до чего-то серьёзного, вайбкодинг его не вытянет. Авторизация по логину и паролю, несколько источников данных с разными схемами, история изменений, роли пользователей, отдельная база данных. Это нормальная разработка с нормальным разработчиком. Что здесь описано хорошо работает для одного источника, публичного доступа и команды до 10 человек, которым нужно смотреть на цифры, а не управлять ими.
Что делать дальше: куда расти после первого дашборда
Первый дашборд работает. Теперь понятно, что хочется большего.
Самое очевидное узкое место: данные обновляются вручную. Это убивает весь смысл, если заходишь в дашборд раз в неделю и видишь цифры двухнедельной давности. Лечится через Apps Script прямо в Google Sheets: пишешь скрипт, который дёргает нужный источник и перезаписывает диапазон по расписанию. Cursor напишет этот скрипт за один запрос, если дашь ему структуру листа и объяснишь, откуда брать данные. Настраиваешь триггер на каждые 6 часов, и дашборд живёт сам.
Google Ads подключается двумя путями. Официальный коннектор прямо в Sheets, через "Дополнения", бесплатный и без кода. Если нужна более гибкая синхронизация по нескольким кампаниям или аккаунтам, Nodul справляется лучше: там визуальные сценарии, которые можно настроить за полчаса без написания запросов к API вручную.
Следующий серьёзный шаг: переехать с Google Sheets на Supabase. Sheets хорошо держит текущий срез, но плохо хранит историю. Если хочешь видеть
