Модуль 1 · Идея и спецификация · урок 3 из 13
Выбор стека и каркас проекта
Стек для вайбкод-проекта выбирают не по моде, а по двум вопросам: насколько уверенно агент пишет на нём код и сможете ли вы этот код прочитать. В этом уроке выберем стек для «Переговорок», запишем решение и соберём каркас, в котором уже работают проверки.
Критерии выбора
Вы умеете это читать. Главный критерий. Если в авторизации окажется дыра, искать её будете вы, а не агент. Стек, на котором вы не способны провести ревью, превращает проект в чистый вайбкодинг со всеми последствиями.
Скучные технологии. В статье OpenAI о harness engineering команда объясняет, почему предпочитала «скучные» зависимости: у них стабильный API, они хорошо представлены в обучающих данных, и агенту проще ими пользоваться. Свежий фреймворк, вышедший полгода назад, агент будет путать с предыдущей версией.
Типы и быстрые проверки. Компилятор и линтер — самая дешёвая обратная связь для агента. Ошибка типов ловится за секунды, а не в продакшене.
Батарейки в комплекте. Миграции, валидация, тестовый раннер — чем меньше агент собирает руками, тем меньше самодельного кода надо проверять.
Где будет жить. Если у компании есть стандартный способ деплоя (Docker-образ и GitLab CI), стек должен в него ложиться.
Попросите варианты, решите сами
Агент полезен как собеседник при выборе, но решение — ваше. Режим только чтения, файлы спецификации в контексте:
Прочитай docs/SPEC.md. Предложи 3 варианта стека для этого сервиса.
Для каждого: язык, фреймворк бэкенда, база, ORM или query builder,
фронтенд, тесты (unit, интеграция, e2e), как деплоить в Docker.
Сравни таблицей: зрелость, насколько хорошо ты на нём пишешь,
сколько своего кода понадобится для входа по почте, риски.
Учти: команда знает TypeScript и PHP, деплой через GitLab CI.
Не выбирай за меня.Решение и его причины запишите коротким документом — через полгода вы или новый разработчик спросите, почему так:
# 0001. Стек
Статус: принято
Решение: TypeScript везде. Node.js 22, Fastify (API), React + Vite (интерфейс),
PostgreSQL 16, Prisma (схема и миграции), Zod (валидация на границе),
Vitest (unit и интеграция), Playwright (e2e).
Почему: один язык на фронте и бэке, команда читает TypeScript,
все инструменты зрелые. PostgreSQL умеет запрещать пересечение
интервалов на уровне базы (правило R3).
Отвергли: Next.js — сложнее граница клиент/сервер для ревью;
Laravel — хороший вариант, но фронтенд-часть команды пишет на TS.Для PHP-команды разумно выбрать Laravel: он закрывает вход, миграции и тесты из коробки. Дальше в курсе примеры на TypeScript, но приёмы те же.
Каркас: промпт
Каркас агент собирает быстро, но в нём легко накопить мусор: две библиотеки для одного и того же, устаревшие версии, смесь модулей CommonJS и ESM. Поэтому ограничения пишем явно:
Создай каркас по docs/decisions/0001-stack.md.
Структура: npm workspaces — apps/api, apps/web, packages/shared.
Используй официальные генераторы, где они есть (npm create vite).
Только ESM, TypeScript strict. Версии — актуальные стабильные,
не выдумывай номера: ставь через npm install.
База: docker-compose.yml с PostgreSQL 16 и Mailpit для писем.
Скрипты в корневом package.json: dev, lint, typecheck, test, check
(lint + typecheck + test), db:migrate.
В apps/api — эндпоинт GET /api/health и один тест на него.
Не добавляй ничего сверх списка. В конце перечисли все зависимости
с одной строкой «зачем» на каждую.
Готово, когда: npm run check зелёный, npm run dev поднимает API и web.В режиме по умолчанию песочница не пускает команды в сеть, поэтому на npm install агент попросит разрешения. Это нормально — но читайте, что именно одобряете: имя пакета с опечаткой выглядит почти как настоящее.
Что получилось
rooms/
├── apps/
│ ├── api/ # Fastify, Prisma, тесты Vitest
│ └── web/ # React + Vite
├── packages/
│ └── shared/ # Zod-схемы, общие для API и web
├── docs/
│ ├── SPEC.md
│ ├── PLAN.md
│ └── decisions/0001-stack.md
├── docker-compose.yml
├── .env.example
└── package.jsonservices:
db:
image: postgres:16
environment:
POSTGRES_USER: rooms
POSTGRES_PASSWORD: rooms
POSTGRES_DB: rooms
ports:
- "5432:5432"
volumes:
- db-data:/var/lib/postgresql/data
mail:
image: axllent/mailpit
ports:
- "1025:1025"
- "8025:8025"
volumes:
db-data:Пароль rooms здесь допустим: это локальная база разработчика. В .env.example лежат такие же учебные значения, а настоящий .env попадает в .gitignore — проверьте это сразу.
Проверьте каркас руками
Не верьте отчёту «всё работает» — запустите сами:
npm ci
docker compose up -d
npm run check
npm run dev
curl -s localhost:3000/api/health
npm ls --depth=0 --workspacesЧто смотреть:
- Список зависимостей. Каждая должна быть объяснима. Две библиотеки дат, jest рядом с vitest, пакет, которого вы не знаете, — повод спросить агента «зачем».
- Lock-файл в git. Без него сборка на CI получит другие версии.
npm audit. Свежий каркас с критическими уязвимостями — повод разобраться сразу.- Конфиг TypeScript.
strict: trueдействительно включён, а не закомментирован.
После этого — первый коммит. Отметьте пункт 1 в docs/PLAN.md: агент сделает это сам, если попросить, но проверка — ваша.
Попробуйте сами
10–15 мин на рабочем месте- Попросите Codex сравнить три стека для вашего проекта промптом выше и запишите решение в
docs/decisions/0001-stack.md. - Соберите каркас с явными ограничениями и прогоните команды проверки из раздела выше. Найдите хотя бы одну зависимость, которую можно убрать.
- Убедитесь, что
.envв.gitignore, а.env.exampleсодержит только учебные значения.
Коротко
- Главный критерий стека — вы способны читать и ревьюить этот код.
- Скучные, зрелые технологии агенту даются лучше модных.
- Решение о стеке — отдельный документ в
docs/decisions/. - В промпте на каркас перечисляйте и что нужно, и чего не добавлять.
- Каркас считается готовым, когда вы сами прогнали проверки и разобрали зависимости.