|
|
В журнал
Мысли20 мая 2026 г.4 мин

Calendly удалён. Заменили за вечер вайб-кодингом с Claude.

Четвёртый пункт из поста про SaaS крупным планом: страница бронирования на этом сайте. Date picker, доступность из Google Calendar, ссылки на Meet, письма. И как на практике выглядел «вайб-кодинг».


Я писал, что отписываюсь от мелких SaaS и заменяю их кодом. Четвёртым пунктом был Calendly: «страница Next.js с date picker». Вот этот пункт, раскрытый.

Если ты когда-нибудь бронировал со мной звонок, ты этим пользовался: страница записаться на звонок на этом сайте. Никакого Calendly, никакого Cal.com, никакого iframe, который стучится домой. Один роут в том же репозитории, что и всё остальное. Тот самый, который я мельком упомянул в посте про SaaS.

Что на самом деле делал SaaS

Разбери Calendly на части. Он делает четыре вещи:

  • Показывает посетителю календарь дней и свободные слоты.
  • Знает, когда я занят, и не предлагает занятое время.
  • Создаёт встречу в обоих календарях, со ссылкой на видеозвонок.
  • Шлёт письмо-подтверждение, которое оба участника могут добавить в календарь.

Всё. Те самые 5%, которыми я пользовался. Логика маршрутизации, оплата, round-robin на команду, брендированные страницы: ничего из этого. Я написал ровно четыре вещи.

1. Страница и date picker

Один роут, /book: сетка месяца на react-day-picker, колонка слотов рядом, форма с именем и почтой, экран подтверждения.

Я описал Claude флоу обычным текстом. Он выдал компонент. Пара итераций: «выбранный день инверсией, не обводкой», «слоты плотнее». Всё встало как надо.

2. Доступность: конфиг плюс запрос free/busy

Вот что отличает это от игрушки. Какие слоты существуют решают два входа.

Первое: конфиг-файл. Мой: таймзона Europe/Moscow, рабочие часы 07:00–23:00, слоты по 30 минут, буфер 15 минут между звонками, 4 часа лид-тайма до самого раннего доступного слота, горизонт 30 дней, список праздников.

Второе: мой настоящий календарь. Сервер дёргает freebusy API Google Calendar на видимое окно, получает занятые интервалы и вычитает их из сетки по конфигу. Что осталось, то и предлагается.

freebusy здесь правильный выбор, не полный список событий. Он возвращает только занятые интервалы: без названий, без участников. Страница бронирования узнаёт «занято 14:00–15:00» и больше ничего.

3. Бронирование: сначала валидация, потом событие

Сабмит уходит в один API-роут. zod проверяет payload: настоящая почта, имя в пределах длины, строка слота, которая парсится. Мусор отсекается до того, как запустится что-либо ещё.

Потом роут перепроверяет free/busy (слот могли занять, пока форма была открыта), создаёт событие в моём Google Calendar через googleapis и даёт Google прицепить ссылку на Meet. Посетитель добавляется участником: встреча попадает и в его календарь.

4. Подтверждение: письмо с настоящим .ics

Письмо уходит через Resend. В нём нормальное вложение .ics, собранное вручную: «Добавить в календарь» работает в Apple Mail, Outlook, где угодно. Плюс ссылка на Google Calendar в один клик для всех остальных.

Вот что упускают, когда говорят, что сделать страницу бронирования просто. Страница: просто. А совместимость с календарями (таймзоны, поля .ics, ссылка на Meet, список праздников) раньше съедала весь вечер. С Claude ушло минут двадцать.

Одна занудная часть: Google OAuth

Чтобы читать и писать мой календарь, серверу нужен refresh-токен Google OAuth. Для этого надо зарегистрировать приложение в Google Cloud Console, настроить consent screen, выбрать scopes, прогнать одноразовый обмен кода на токен. Claude написал роут /api/oauth/start для обмена. Кликанье по консоли Google на тебе: её интерфейс меняется достаточно часто, чтобы ни одна модель не помнила его точно.

Заложи полчаса занудства. Как только refresh-токен в переменных окружения, ты к нему больше не возвращаешься.

Что на самом деле значил «вайб-кодинг»

Не «Claude написал приложение, а я смотрел». Скорее так:

  • Я знал четыре вещи, которые система должна делать. Конфиг (буферы, лид-тайм, праздники) я продумал до первого промпта. Это мышление не аутсорсится.
  • Claude написал по сути весь код: математику слотов, слияние free/busy, сборщик .ics, React-компонент, шаблоны писем.
  • Я читал каждый дифф. Баг с таймзоной (слоты уезжали на час у границы перехода на летнее время) я поймал чтением, не запуском.
  • Продуктовые решения принимал я. Должен ли удержанный слот протухать? Класть ли в письмо ссылку на перенос? Это моё.

Модель: быстрый, начитанный напарник, который собрал сотню страниц бронирования. Не продакт-оунер.

SaaS продавал мне скучные 95%. Claude теперь делает их бесплатно, и я плачу только за те 5%, которые всегда были моими.

Стоило ли оно того

Командный тариф Calendly: $15–30 в месяц. Страница заняла вечер: часа три, большую часть из которых съели занудство с OAuth и доводка экрана подтверждения. Аргумент «сделай за вечер». Буквально.

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

Доказательство живое. Запишись на звонок на той самой странице, о которой весь этот пост.

Calendly удалён. Заменили за вечер вайб-кодингом с Claude. | Валерий Сацура