Vibe coding MenuGen
Андрей Карпати рассказывает о своём опыте «вайб-кодинга» — создания веб-приложения MenuGen с нуля без навыков веб-разработки, используя только Cursor и Claude. MenuGen позволяет сфотографировать меню ресторана и сгенерировать изображения для каждого блюда, чтобы понять, что именно вам подадут. Первый локальный прототип заработал быстро, но доведение до продакшена — подключение API OpenAI и Replicate, деплой на Vercel, настройка аутентификации через Clerk, платежей через Stripe — оказалось мучительным из-за устаревших знаний LLM, рейт-лимитов и бесконечной настройки сервисов. Карпати отмечает, что большую часть времени провёл не в редакторе кода, а в браузере, переключаясь между вкладками и настройками различных платформ. В заключение он размышляет о будущем: нужны платформы «всё включено» для простых приложений, сервисы должны стать дружелюбнее к LLM, а порог создания приложений должен снизиться почти до нуля.
Вайб-кодинг MenuGen
27 апреля 2025
Очень часто я сажусь в ресторане, листаю меню и чувствую себя... в тупике. Что такое паштет? Что такое тажин? Каватаппи... это же паста, верно? Sweetbread звучит восхитительно (я ужасный сладкоежка). Иногда всё это заходит совсем далеко. «Конфи из клубней, смешанное с выдержанным творожным сыром и заправленное инфузией из beurre noisette.» Ладно, и... что это вообще такое? Я столько времени в жизни провёл, гугля фотографии блюд, что когда подвернулась возможность поучаствовать в хакатоне по вайб-кодингу, я понял: это идеальный шанс наконец создать приложение, которое я всегда хотел, но нигде не мог найти. И вот оно, во плоти. Я называю его... 🥁🥁🥁 ... MenuGen:
MenuGen устроен предельно просто. Вы фотографируете меню, и приложение генерирует изображения для всех позиций. Оно визуализирует меню. Разумеется, это не в точности то, что вам подадут именно в этом ресторане, но даёт общее представление: вот это — салаты, это — рыба, это — суп и так далее. Мне это оказалось настолько полезным, что после хакатона (где я заставил первую версию работать на localhost) я продолжил вайб-кодить — задеплоил приложение, добавил аутентификацию, платежи и в целом довёл его до ума. Так что вот, попробуйте в следующий раз, когда пойдёте куда-нибудь поужинать :): menugen.app!
MenuGen — моё первое от и до вайб-закоженное приложение: я (человек, который поковыривается в коде, но практически не имеет реального опыта веб-разработки) прошёл путь с нуля до настоящего продукта, на который люди могут зарегистрироваться, заплатить, получить пользу, а я — положить в карман честные 10% наценки. Это впечатляет. Но помимо практической ценности приложения, MenuGen был для меня интересен как исследование вайб-кодинга и его реальной осуществимости сегодня. Поэтому я не писал код напрямую; 100% кода написано Cursor+Claude, и я, по сути, не знаю, как MenuGen работает в том привычном смысле, к которому привык. Теперь, когда проект «готов» (то есть первая версия вроде бы работает), я хотел написать этот короткий пост о своём опыте — как сегодня выглядит вайб-кодинг веб-приложения для человека без опыта в веб-разработке.
Сначала — локальная версия. Как это часто бывает при вайб-кодинге, самый первый прототип приложения на локальной машине занял совсем немного времени. Я взял Cursor + Claude 3.7, дал описание приложения, и он очень быстро написал все React-компоненты фронтенда, собрав красивую веб-страницу с плавными разноцветными шрифтами, маленькими CSS-анимациями, адаптивной вёрсткой и всем прочим — за исключением собственно бэкенд-функциональности. Видеть, как новый сайт материализуется так быстро — мощный крючок. Мне казалось, что я сделал 80%, но (спойлер...) на самом деле это было ближе к 20%.
OpenAI API. Примерно здесь начались первые проблемы. Мне нужно было вызывать OpenAI API для OCR-распознавания позиций меню из изображения. Пришлось получить ключи OpenAI API. Пришлось разбираться в слегка запутанных меню с «проектами» и детальными правами доступа. Claude постоянно галлюцинировал устаревшие API, названия моделей и форматы ввода/вывода, которые недавно изменились, — это сбивало с толку, но в итоге всё разрешилось после того, как я какое-то время перекидывал туда-сюда куски документации. Когда отдельные вызовы API наконец заработали, я тут же упёрся в жёсткий рейт-лимитинг — мне разрешалось отправлять лишь несколько запросов раз в 10 минут.
Replicate API. Далее мне нужно было генерировать изображения по описаниям. Я зарегистрировался, получил новый ключ Replicate API и довольно быстро столкнулся с похожими проблемами. Мои запросы не работали, потому что знания LLM были устаревшими, но вдобавок на этот раз даже официальная документация немного отставала из-за недавних изменений в API, который теперь возвращал не JSON напрямую, а некий Streaming-объект, который ни я, ни Claude не понимали. Затем я упёрся в рейт-лимитинг API, из-за чего отладка приложения стала крайне затруднительной. Мне потом объяснили, что это стандартные меры защиты от мошенничества, но они же усложняют старт для новых легитимных аккаунтов. Мне говорят, что Replicate переходит на другую модель с предоплатой кредитов, что может помочь в будущем.
Деплой на Vercel. К этому моменту приложение хотя бы работало локально, и я был доволен. Пришло время задеплоить базовую первую версию. Регистрация на Vercel, добавление проекта, настройка, привязка к моему GitHub-репозиторию, пуш в master, наблюдение за новым деплоем и... ОШИБКА. В логах были какие-то ошибки линтера из-за неиспользуемых переменных и тому подобных мелочей, но разобраться было сложно, потому что локально всё работало, а ломалось только при сборке на Vercel. Так что я дебажил, пуша фейковые отладочные коммиты в master, чтобы вызвать передеплой. Когда я исправил эти проблемы, сайт всё равно отказывался работать. Я спрашивал Claude. Спрашивал ChatGPT. Читал документацию. Гуглил. Через час я наконец осознал свою глупую ошибку — мой файл .env.local хранил API-ключи OpenAI и Replicate, но этот файл (и правильно!) включён в .gitignore и не попадает в git, поэтому нужно вручную зайти в настройки проекта на Vercel, найти нужное место и добавить переменные окружения руками. Я-то понял проблему относительно быстро, но могу представить, как начинающий вайб-кодер застрял бы на этом надолго. Когда деплой наконец удался, Vercel радостно предложил URL. Это меня снова удивило, потому что мой проект был приватным git-репозиторием, который не был готов увидеть свет. Я не ожидал, что Vercel возьмёт ваш приватный! репозиторий с незаконченным проектом и автоматически задеплоит его на совершенно публичный и легко угадываемый URL — вот так просто, ха.
Аутентификация через Clerk. Claude предложил использовать Clerk для аутентификации, и я согласился. Зарегистрировался в Clerk, настроил проект, получил API-ключи. На этом этапе Claude нагаллюцинировал около 1000 строк кода, которые, по всей видимости, использовали устаревшие API Clerk. Мне пришлось долго перекидывать туда-сюда куски документации, чтобы постепенно сдвинуться с мёртвой точки. Далее — до сих пор Clerk работал в режиме «Development». Чтобы перейти на «Production», нужно было пройти ещё ряд квестов. Clerk требует, чтобы приложение было размещено на собственном домене. menugen.vercel.com не подходит. Так что пришлось купить доменное имя menugen.app. Потом привязать домен к моему проекту на Vercel. Потом изменить DNS-записи. Потом выбрать OAuth-провайдера — я выбрал Google. Но это само по себе оказалось отдельным конфигурационным приключением. Нужно было включить «SSO connection». Зайти в Google Cloud Console и создать новый проект, добавить новый OAuth Credential. Подождать какое-то время, пока пройдёт процесс одобрения. Потом долго переключаться между вложенными настройками Vercel, Clerk и Google, чтобы всё правильно связать. Примерно здесь я подумал бросить проект, но на следующее утро мне стало легче.
Платежи через Stripe. Затем я хотел добавить платежи, чтобы люди могли покупать кредиты. А значит — ещё один сайт, ещё один аккаунт, ещё документация, ещё ключи. Выбираю «Next.js» в качестве бэкенда, копирую самый первый фрагмент кода из документации «getting started» в своё приложение и... ОШИБКА. Позже я понял, что Stripe выдаёт JavaScript-код, когда вы выбираете Next.js, а моё приложение написано на TypeScript, поэтому каждый раз, когда я вставлял фрагмент кода, Cursor ругался ошибками линтера. Но Claude со временем всё подлатал после того, как я пару раз сказал ему «исправь ошибки» и пригрозил перейти на ChatGPT. Потом обратно в панель Stripe — создаём Product, создаём Price, находим ключ цены (не ключ продукта!), копируем все ключи туда-сюда. Примерно здесь я поймал Claude на использовании очень плохого подхода к сопоставлению успешного платежа Stripe с кредитами пользователя (он пытался сопоставить по email-адресам, но email, который пользователь указывает при оплате через Stripe, может не совпадать с email Google-аккаунта, через который он зарегистрировался, — и тогда пользователь просто не получит купленные кредиты). Я указал на это Claude, и он немедленно извинился и переписал всё правильно, передавая уникальные идентификаторы пользователей в метаданных запроса. Он поблагодарил меня за замечание и пообещал, что в будущем будет делать правильно, — что, я знаю, просто газлайтинг. Но поскольку наш быстрый тест сработал, оставалось лишь несколько кликов, чтобы перевести деплой из Development в Production, заново создать Product, заново создать Price, заново скопировать все ключи и идентификаторы — локально и в настройках Vercel... и тогда всё заработало :)
База данных? Очереди задач? До сих пор вся обработка происходила «на лету» — просто запросы и результаты здесь и сейчас, ничего не кешируется, не сохраняется и т. д. Результаты эфемерны: если ответ занимает слишком много времени (например, потому что меню слишком длинное с кучей позиций, или API слишком тормозят), запрос может упасть по таймауту. Если обновить страницу — всё пропадает. Правильный подход — иметь базу данных, где регистрируются и отслеживаются задачи, а клиент просто отображает последнее состояние по мере готовности. Я понял, что мне придётся подключить базу данных из маркетплейса — что-нибудь вроде Supabase PostgreSQL (притом что Claude предлагал мне Vercel KV, который, я знаю, на самом деле deprecated). А ещё нужен какой-то сервис очередей типа Upstash для собственно обработки. Это означало бы ещё больше сервисов. Ещё больше логинов. Ещё больше API-ключей. Ещё больше конфигураций. Ещё больше документации. Ещё больше страданий. Слишком много для одного медведя. Оставлено на будущее.
Итого. Вайб-кодинг MenuGen был захватывающим и весёлым приключением в качестве локальной демки, но довольно мучительной рутиной в качестве задеплоенного, настоящего приложения. Создание современного приложения — это что-то вроде сборки мебели из IKEA из будущего. Куча сервисов, документации, API-ключей, конфигураций, dev/prod-деплоев, командных и security-фич, рейт-лимитов, тарифных планов... При этом у LLM слегка устаревшие знания обо всём, они допускают тонкие, но критичные ошибки проектирования, если внимательно за ними следить, а иногда галлюцинируют или газлайтят вас по поводу решений. Но самое интересное для меня — я потратил не так уж много времени в самом редакторе кода. Большую часть времени я провёл в браузере, переключаясь между вкладками, настройками и склеивая монстра. Вся эта работа и состояние даже не доступны и не манипулируемы для LLM — как мы собираемся автоматизировать общество к 2027 году при таком раскладе?
Что дальше. Как исследование того, каково сегодня вайб-кодить приложение, если у тебя мало или совсем нет опыта веб-разработки, я остался с одинаковой смесью восхищения (это действительно возможно и намного проще/быстрее, чем раньше!) и доли разочарования от того, что могло бы быть. Часть боли, конечно, в том, что вся эта инфраструктура не была рассчитана на такое использование. Целевая аудитория — команды профессиональных веб-разработчиков, живущие в мире до LLM. А не вайб-кодеры-одиночки, прототипирующие приложения. Несколько мыслей о решениях, которые могли бы сделать создание сверхпростых приложений вроде MenuGen намного проще:
Какая-нибудь платформа для разработки приложений могла бы поставляться со всем необходимым «из коробки». Что-то, что выглядит как противоположность Vercel Marketplace. Что-то предвзятое, конкретное, предварительно настроенное со всеми базовыми вещами, которые нужны каждому: домен, хостинг, аутентификация, платежи, база данных, серверные функции. Если бы какой-то сервис сделал всё это легко и «просто работающим» из коробки — это было бы потрясающе. Все эти сервисы могли бы стать дружелюбнее к LLM. Всё, что вы говорите пользователю, по сути, сразу же будет скопировано и вставлено в LLM, так что можно сразу обращаться напрямую к LLM. У вашего сервиса мог бы быть CLI-инструмент. Бэкенд можно было бы настраивать curl-командами. Документация могла бы быть в Markdown. Всё это эргономически намного более удобные поверхности и абстракции для LLM. Не разговаривайте с разработчиком. Не просите разработчика зайти, посмотреть или кликнуть. Инструктируйте и наделяйте возможностями его LLM. Для своего следующего приложения я подумываю использовать простой HTML/CSS/JS + Python-бэкенд (FastAPI + Fly.io или что-то подобное?), нечто гораздо более простое, чем серверлесс-мультивселенная «современной веб-разработки». Возможно, простое приложение вроде MenuGen (или подобные ему) было бы значительно проще в такой парадигме. Наконец, вполне вероятно, что MenuGen вообще не должен быть полноценным приложением. «Приложение» — это просто один вызов GPT для OCR меню, а затем цикл for по результатам для генерации изображений каждого блюда и красивого отображения пользователю. Это почти звучит как простой custom GPT (в терминологии оригинального «магазина приложений» GPT, который OpenAI выпустил ранее). Мог бы MenuGen быть просто промптом? Мог бы LLM отвечать не текстом, а простой веб-страницей для представления результатов — по аналогии с Artifacts? Могли бы и другие приложения выглядеть так же? Мог бы я опубликовать это как приложение в магазине и зарабатывать наценку точно так же?
А пока я вполне доволен тем, что довёл до финиша свой первый суперкастомный вайб-закоженный проект — реальный продукт, который решает проблему, мучившую меня давно, и которым можно поделиться с друзьями. Спасибо всем вышеперечисленным сервисам, которые я использовал для его создания. В принципе, он может приносить какие-то $, если понравится и другим, — полностью пассивно, мечта @levelsio. В конечном счёте вайб-кодинг полноценных веб-приложений сегодня — штука довольно хаотичная и не лучшая идея для чего-то по-настоящему важного. Но есть явные проблески величия, и я думаю, индустрии просто нужно немного времени, чтобы адаптироваться к новому миру LLM. Лично я с нетерпением жду, когда барьер для создания приложений упадёт практически до нуля, когда любой сможет создать и опубликовать приложение так же легко, как снять TikTok. Такие гиперкастомные автоматизации могут стать прекрасным новым холстом для человеческого творчества.
Сопутствующий твит (и «раздел комментариев») — в моём X @karpathy.