Модуль 2 · Строим · урок 8 из 13
Данные, API и интеграции
Интерфейс можно переделать за час, данные — нет. Неудачная миграция на продакшене, перепутанные часовые пояса или интеграция, которая роняет бронь, когда мессенджер недоступен, — типичные ошибки вайбкод-проектов, и агент делает их уверенно. В этом уроке — как развивать данные и подключать внешние системы так, чтобы это можно было проверить.
Миграции: только вперёд
Правило из AGENTS.md «применённые миграции не менять» — самое важное в работе с данными. Агент, у которого не сходится схема, охотно правит старую миграцию: локально всё работает, а на стенде, где она уже применена, — нет.
Две команды Prisma, которые нужно различать:
npx prisma migrate dev— локально: создаёт и применяет новую миграцию;npx prisma migrate deploy— на стенде и в продакшене: только применяет готовые.
Каждую миграцию читайте построчно, особенно разрушительные операции. Когда офис-менеджер попросил переименовать поле hasScreen в equipment со списком, агент сгенерировал DROP COLUMN и ADD COLUMN — данные об экранах исчезли бы. Безопасный путь — расширить и сжать: добавить новую колонку, перенести данные, переключить код, а старую колонку удалить отдельным релизом.
Нужно заменить Room.hasScreen на Room.equipment (массив строк).
Сделай это в три шага, каждый — отдельной миграцией и коммитом:
1) добавить equipment, заполнить из hasScreen (true → ['screen']);
2) перевести API и интерфейс на equipment;
3) удалить hasScreen. Шаг 3 не делай, только опиши.
Покажи SQL каждой миграции до применения.Время — отдельная тема
Брони — это время, и здесь агент ошибается чаще всего. Правило уже записано в AGENTS.md: в базе timestamptz, в API ISO 8601 со смещением, в интерфейсе — зона офиса. Но правило не помогает, если границы дня считаются по зоне сервера. Сервер в контейнере живёт в UTC, и «брони на сегодня» после полуночи по Москве покажут вчерашний день.
Лекарство — тесты на границах, которые агент сам не придумает:
Добавь тесты для расчёта границ дня и недели в apps/api/src/services/time.ts:
- 23:30 и 00:30 по Europe/Moscow попадают в разные дни;
- процесс запущен с TZ=UTC и TZ=Asia/Vladivostok — результат одинаковый;
- неделя начинается в понедельник 00:00 по Москве.
Тесты должны падать на текущей реализации, если она зависит от TZ процесса.Один контракт на API и интерфейс
Zod-схемы в packages/shared — единственный источник типов запросов и ответов. Агент, которому нужно быстро, объявит тип Booking прямо в компоненте интерфейса — и через месяц у вас три разных Booking. Помогает правило в AGENTS.md и проверка в ревью: «новый тип ответа API, объявленный вне packages/shared, — замечание».
Бонус единого контракта: API валидирует вход той же схемой, по которой интерфейс строит форму, и расхождение ловит компилятор, а не пользователь.
Сиды и реалистичные данные
Пустая база скрывает половину ошибок. Попросите агента написать сид с реалистичными данными: четыре комнаты (одна архивная), три десятка броней на текущую неделю, длинные названия встреч, брони впритык друг к другу. Скрипт npm run db:seed должен быть идемпотентным — его будут запускать и тесты, и вы. Персональные данные — только выдуманные.
Актуальная документация библиотек
Агент знает библиотеки по обучающим данным, а API меняются. Если видите вызовы, которых нет в вашей версии пакета, дайте ему документацию. Два способа:
- MCP-сервер с документацией библиотек, например:
codex mcp add context7 -- npx -y @upstash/context7-mcp- справочные файлы в репозитории:
docs/references/с выжимкой нужной версии API. Команда OpenAI в статье о harness engineering описывает именно такой каталог.
Каждый MCP-сервер добавляет контекст в каждую сессию, поэтому подключайте только нужные.
Интеграция 1: подписка на календарь
Сотрудники хотят видеть расписание комнаты в своём календаре. Календарные клиенты не умеют входить по cookie, поэтому подписка работает по секретной ссылке:
Добавь GET /api/rooms/:id/calendar.ics — расписание комнаты на 30 дней
в формате iCalendar (RFC 5545). Доступ по персональному токену в query:
токен генерируется на странице профиля, в базе — хэш, токен можно перевыпустить.
Отменённые брони не включать. Тест: ответ разбирается парсером iCalendar,
время событий совпадает с бронями с учётом зоны.
После реализации я проверю подписку в реальном календаре.Последняя строка — не формальность. Формат iCalendar капризен, и проверка «календарь показал событие в правильное время» стоит больше юнит-теста.
Интеграция 2: уведомления в мессенджер
Когда бронь отменяют, в канал офиса уходит сообщение — комната освободилась. Типичная реализация агента: вызвать вебхук прямо в обработчике отмены. Если мессенджер тормозит, отмена висит; если он недоступен, отмена падает.
Уведомление об отмене брони в канал офиса через входящий вебхук.
Требования:
- отмена брони не зависит от доступности мессенджера;
- запись о событии сохраняется в таблицу notifications в той же транзакции,
что и отмена; отдельный воркер отправляет и ретраит с паузой, до 5 попыток;
- таймаут запроса 5 секунд; URL вебхука — из NOTIFY_WEBHOOK_URL, не логировать;
- в тестах внешний вызов идёт в локальную заглушку, настоящий вебхук не трогать.Это шаблон «outbox»: побочный эффект сначала записывается в базу, а отправляется потом. Он скучный и надёжный.
Агент и база данных
Агенту полезно смотреть в локальную базу: проверить, что миграция легла, посмотреть реальные данные. Для этого хватит psql с учётными данными из docker-compose.yml. Доступ к базе стенда и тем более продакшена агенту не давайте: одна неудачная команда — и восстанавливать придётся из бэкапа. Подключение баз через MCP разобрано в уроке «MCP на практике».
Попробуйте сами
10–15 мин на рабочем месте- Попросите агента показать SQL следующей миграции до применения и найдите в нём разрушительные операции.
- Добавьте тест, который запускает расчёт границ дня под двумя разными
TZ, и посмотрите, проходит ли он. - Найдите в своём проекте внешний вызов в обработчике запроса и спросите агента, что произойдёт, если сервис ответит через 30 секунд.
Коротко
- Миграции только вперёд; разрушительные изменения — через «расширить и сжать».
- Время:
timestamptzв базе, зона офиса для границ дня, тесты на полночь и разныеTZ. - Один контракт API в общих схемах, а не типы в каждом компоненте.
- Устаревшие API библиотек лечатся документацией: MCP или
docs/references/. - Интеграции не должны ронять основной сценарий: таймауты, ретраи, outbox.
- Агенту — локальная база, не продакшен.