Тестовое задание
Polza Agency — Технический специалист / вайбкодер
Выгрузка → PostgreSQL
Из data_pack.zip (page_001.json … page_020.json, ~1000 компаний) все записи загружены в PostgreSQL. Реализована дедупликация, проставлены индексы.
- Скрипт загрузки: `scripts/load_data.ts` — читает JSON, дедуплицирует по названию, загружает в БД
- Схема: таблица `companies` с полями: id, name, category, city, address, rating, reviews_count, site, phone, created_at
- Индексы: на name (для поиска), city (для фильтрации), category (для группировок)
- Docker Compose: `docker-compose.yml` — PostgreSQL + приложение одной командой
- SQL-запросы в блоке ниже
Страница /companies
Next.js App Router страница с серверным рендерингом. Таблица компаний из PostgreSQL с поиском по названию и фильтром по городу.
- Server Component: `src/app/companies/page.tsx` — фетчит данные и города напрямую из БД
- Client Component: `CompaniesClient.tsx` — интерактивная таблица с поиском и фильтрацией
- Поиск через URL-параметры (?q=&city=) — работают навигация, рефреши и расшаривание результатов
- Адаптивный дизайн: колонки скрываются на мобильных устройствах
Данные с сюрпризом (review.csv)
Анализ review.csv на аномалии. Все несоответствия задокументированы в ANOMALIES.md — 31 инцидент.
- Найдены: невалидные рейтинги, битая кодировка (mojibake), дубликаты ID, сдвиг колонок, опечатки
- Полный отчёт с таблицами и цветовой кодировкой по severity
Вайбкод / LLM-стек
Процесс разработки, инструменты, методология и архитектурные решения.
- IDE: Claude Code + Cursor, агентские сценарии на живом репозитории
- Модели: Claude Sonnet 4.5 / DeepSeek V4 Flash — под задачу
- Подход: AI генерирует код, я проверяю каждый шаг, тестирую и коммичу
- Все изменения в git с осмысленными сообщениями, каждая фича в своей ветке
SEO-оптимизация
Полный набор SEO-базовых мета-тегов, структурированных данных и файлов для индексации.
- Open Graph / Twitter Cards на всех страницах с og:image 1200×630
- JSON-LD структурированные данные (Organization + BreadcrumbList)
- robots.txt + sitemap.xml + уникальные canonical URL
- Уникальные title/description для каждой страницы (главная, /companies, /privacy, /docs, /anomalies)
- favicon.svg + lang="ru"
Архитектурные решения
Почему Next.js + TypeScript, а не Python?
В ТЗ не было жёсткого требования по языку для ETL-скрипта, но вся платформа Polza Agency построена на Next.js + TypeScript + PostgreSQL. Выбор TypeScript для скрипта загрузки данных и всего приложения — это осознанное архитектурное решение:
- Единый стек — скрипты загрузки, сайт и API пишутся на одном языке. Не нужно переключать контекст между Python и TypeScript.
- Типизация — TypeScript даёт статическую проверку типов на всём протяжении: от парсинга JSON до UI-компонентов.
- Server Components — данные фетчатся напрямую в серверном компоненте, без отдельного API-слоя. Меньше кода, быстрее работа.
- Docker Compose — и БД, и приложение поднимаются одной командой. Не нужно ставить Python, Node.js или настраивать окружение вручную.
- Единый CI/CD — один набор зависимостей для всего проекта: npm install, npm run build, npm start.
Прочие важные моменты
- Безопасность — .env в .gitignore, секреты не попадают в репозиторий. Промпт-инъекции и SQL-инъекции проверены (не найдены).
- Воспроизводимость — docker-compose.yml поднимает PostgreSQL, скрипт load_data.ts загружает данные. README с командой запуска.
- Дедупликация — компании сопоставляются по названию, дубликаты исключаются на этапе загрузки.
- ETL-обработка аномалий — автоматическое выявление и исправление битой кодировки, нормализация городов, приведение типов.
SQL-запросы
1. Топ-5 категорий по числу компаний
SELECT category, COUNT(*) AS company_count FROM companies WHERE category IS NOT NULL GROUP BY category ORDER BY company_count DESC LIMIT 5;
2. Средний рейтинг по городам (компании с 10+ отзывами)
SELECT city,
ROUND(AVG(rating)::numeric, 2) AS avg_rating,
COUNT(*) AS company_count
FROM companies
WHERE rating IS NOT NULL
AND reviews_count >= 10
GROUP BY city
ORDER BY avg_rating DESC;3. Доля компаний с сайтом по категориям
SELECT category,
COUNT(*) AS total,
COUNT(*) FILTER (WHERE site IS NOT NULL) AS with_site,
ROUND(100.0 * COUNT(*) FILTER (WHERE site IS NOT NULL) / COUNT(*), 1) AS pct_with_site
FROM companies
WHERE category IS NOT NULL
GROUP BY category
ORDER BY pct_with_site DESC;Вопрос-ответ
IDE и LLM модели — выбор и эволюция
Q: Твой выбор IDE и LLM моделей сейчас и как он менялся последние полгода.
A: Сейчас основная IDE это VS Code. Для работы с кодом использую двух агентов: Claude Code (Sonnet 5) и OpenCode с бесплатными моделями DeepSeek V4 Flash и OpenCode Zen. Полгода назад в основном работал с бесплатными китайскими моделями DeepSeek, MiniMax, MiMo. Причина была не только в экономии. Бесплатные модели чаще требуют самостоятельно разбираться в архитектуре проекта, проверять предлагаемые решения и принимать инженерные решения самому. Для меня это было полезно, потому что позволяло лучше понимать код и технологии, а не просто принимать готовые ответы модели. Задачи стали серьезнее и я пришел к подписке Claude, но все ещё совмещаю.
Подписки на LLM сервисы
Q: Сколько и каких подписок в месяц тебе хватает для полноценной работы.
A: Подписка Claude pro покрывает все задачи + иногда использую бесплатные модели в OpenCode на несущественных задачах (рутина, документация), где не стоит вопрос безопасности или критичного искажения данных.
Методология сравнения моделей
Q: Как сравниваешь две новые модели или инструмента — по ощущениям или на одинаковом наборе задач?
A: На одинаковом наборе задач, например, багфикс в существующем коде, миграция, рефакторинг. В первую очередь интересует не столько скорость сколько качество. Так же проверяю модели на больших задачах, но тут мы уже переходим в область ощущений часто. На больших задачах мы видим как модель справляется разово. Насколько четко нужно прописывать алгоритм словами либо модель способна сама безошибочно простроить всю цепочку от пункта А к пункту Б, насколько точно модель следует требованиям, понимает ли она существующую архитектуру проекта, не ломает ли код.
Тестирование новых функций
Q: Какие тесты ты потребуешь для новой функции?
A: Unit-тесты, валидность данных, отработка исключений и сообщения об ошибках, ручная проверка чтобы убедиться, что функция действительно работает так, как ожидается. + Безопасность ручного ввода с фронта (инъекции). Если это вход, то авторизация, права доступа, роли.
Разрешения для coding-агента
Q: Какие разрешения ты дашь coding-агенту, имеющему доступ к терминалу и базе?
A: Агент должен быть максимально ограничен в доступе к командам терминала и базы данных. В идеале агент должен работать под отдельной учетной записью с ограниченным набором прав. Если речь идет о базе данных, то по умолчанию ему достаточно доступа на чтение. Любые операции, которые могут изменить или удалить данные — с подтверждением от человека. В начале разработки допускаю большую свободу действий для моделей. Работающий проект — однозначно ограничения прав агента.
Автоматические тесты и CI/CD
Q: Пишешь ли ты автоматические тесты и включаешь ли их в CI/CD? Какие проверки должны обязательно пройти перед слиянием и деплоем.
A: Сборка, линтинги, проверка зависимостей, аудиты безопасности.