CV Hub
Резюме как код. Профессиональная идентичность как воспроизводимая версионируемая система.
Обзор
CV Hub — это не шаблон портфолио. Это система. Проект переосмысляет резюме как инженерный артефакт — версионируемый, воспроизводимый и развёртываемый. Вместо того чтобы поддерживать разрозненные представления в LinkedIn, PDF, Notion и на сайтах-портфолио, CV Hub вводит единый декларативный источник правды — YAML-файл, который управляет всем пайплайном вывода. Результат — полностью автоматизированная система, где одно изменение согласованно расходится по всем форматам и всем ролевым вариациям. Сама страница, которую вы читаете, — часть этой системы: этот кейс собран из блочного YAML-документа тем же движком, что строит резюме.
Проблема
С точки зрения систем традиционные резюме фундаментально сломаны. Они: - дублируются между платформами - синхронизируются вручную - зависят от контекста (DevOps vs Backend vs GameDev) - не находятся под версионным контролем - не воспроизводимы Это ведёт к рассинхрону, несогласованности и заметным накладным расходам на поддержку. С инженерной точки зрения это эквивалентно запуску нескольких несинхронизированных окружений без единого источника правды.
Решение
CV Hub применяет инфраструктурные принципы к персональным данным. Базовая идея проста: опиши один раз → сгенерируй всё → разверни автоматически. Один YAML-файл становится: - живым сайтом - артефактами PDF, DOCX и TXT - несколькими ролевыми версиями резюме - версионируемым профилем под контролем Git Система устраняет дублирование и обеспечивает согласованность by design.
Мультипрофильная система
Архитектура base + delta
Ролевые резюме генерируются по двухслойной модели: базовое CV плюс дельта-переопределения на каждый профиль. Это даёт точный контроль над позиционированием без дублирования данных.
Как работают профили
Каждый профиль объявляется один раз в `profiles.yml` — id, URL-slug и ключ spec. На этапе сборки `merge.mjs` проходит по каждой комбинации профиль × язык и создаёт нормализованный артефакт на пару. Слияние намеренно явное, а не магическое: - Скалярные и списочные поля дельты целиком заменяют базовое значение. - Записи опыта сопоставляются по названию компании — дельта-запись, в которой указана только компания, наследует базовую и переопределяет лишь те поля, что задала. - Список опыта профиля полностью определяет свой упорядоченный набор, так что каждая роль курирует ровно ту историю, которая ей релевантна. Вывод детерминирован: один и тот же источник всегда даёт одни и те же артефакты, а гард на этапе сборки следит, чтобы routing-slug профиля и его file-spec ключ не расходились — страница профиля никогда не разъедется с его CV и файлами для скачивания.
Интернационализация
Языки — это ось первого класса, а не довесок. Система рендерит матрицу N профилей × N языков из единой базы контента. - Реестр `languages.yml` задаёт доступные языки и язык по умолчанию; переключатель языка в шапке генерируется из него. - Каждая строка интерфейса живёт в `translations.yaml` и резолвится через небольшой хелпер `makeT()` с цепочкой фоллбэка: запрошенный язык → английский → сам ключ, так что отсутствующий перевод деградирует мягко, а не ломает страницу. - Маршрутизация полностью статическая. `[...slug].astro` выпускает по странице на каждую комбинацию профиль × язык, а главная на языке по умолчанию остаётся на чистом корневом пути. Китайские переводы уже лежат в слое контента и готовы к включению добавлением одной строки в реестр языков.
Что я построил
- Static-first архитектура с нулём клиентского JavaScript по умолчанию
- YAML как единый источник правды для всех данных резюме
- Мультипрофильная система (DevOps, GameDev и т.д.) через детерминированный пайплайн слияния
- Поддержка нескольких языков с мягкой цепочкой фоллбэка
- Автоматическая генерация документов (PDF через Playwright, DOCX/TXT через docx.js)
- Блочный движок кейсов, на котором работают эти Deep Dive страницы
- Четыре взаимозаменяемых анимированных фоновых компонента (CSS и Canvas)
- Переключение тем через URL с шарингом состояния
- Полностью автоматизированный CI/CD на GitHub Actions с алертами в Telegram
- Динамическая конфигурация по окружению (siteUrl из GITHUB_REPOSITORY)
Архитектура
Система построена как детерминированный пайплайн. Слой данных: - YAML-файлы (базовое CV + дельты профилей, проекты showcase, кейсы, переводы, changelog) Слой обработки: - merge.mjs → нормализованные артефакты на каждый профиль и язык - генераторы документов → создают PDF / DOCX / TXT из этих артефактов Слой представления: - статическая сборка Astro → предрендеренный HTML, контент валидируется типизированными схемами Развёртывание: - GitHub Actions → неизменяемая сборка → GitHub Pages Никакого runtime-сервера. Никакого скрытого состояния. Никакой базы данных. Каждый вывод воспроизводим из исходника, а форма контента проверяется Zod-схемами ещё до сборки страниц.
Динамические фоны
Атмосферу сайта создаёт небольшая библиотека взаимозаменяемых фоновых компонентов. Активен всегда один — они спроектированы как drop-in палитра, каждый самодостаточен и настраивается через ту же систему дизайн-токенов, что и остальная стилизация. Семейства два: CSS-слой, который почти ничего не стоит, и набор Canvas-рендереров, вдохновлённых интерфейсом PlayStation XMB. Каждый показан ниже.
AnimatedBackground — по умолчанию
Чистый CSS · ноль JavaScript
Четыре крупных орба из радиальных градиентов дрейфуют за контентом, поверх — тонкий слой шума. Это полностью CSS — без JavaScript, без canvas, без работы в основном потоке. Это и самый оптимизированный фон: keyframes анимируют только смещение (без scale), поэтому браузер кеширует каждый размытый орб как текстуру и просто двигает её, а не пересчитывает blur 60px каждый кадр. Весь слой отключается ниже 768px и уважает `prefers-reduced-motion`.
GalaxyBackground
Canvas · requestAnimationFrame
Звёздное поле на canvas с медленным параллакс-дрейфом, подкрашенное под акцент активной темы. Более тяжёлый, кинематографичный вариант — когда атмосфера важнее стоимости простоя.
PlayStationWaves
Canvas · заливные волны в духе XMB
Заливные синусоиды, наслоённые как меню PlayStation XMB, со сдвигом оттенка по времени суток и недетерминированной стартовой позицией (сид от таймстампа) — так что два запуска не выглядят одинаково. Около 18 пропсов открывают число волн, скорость, амплитуду, цвет и качество отрисовки.
WaveLines
Canvas · светящиеся линии в духе XMB
Штриховой родственник PlayStationWaves: светящиеся линии, симметрично расположенные вокруг центральной оси. Свечение каждой линии композитится на собственном offscreen-canvas — чтобы обойти тонкий баг, где общая маска `destination-in` съедала альфу фона по краям.
Инженерия производительности фонов
Красивые фоны легко сделать тормозящими, поэтому они рассматриваются как отдельная поверхность производительности. - CSS-орбы избегают пересчёта blur каждый кадр, анимируя только трансформации. - Полноэкранный градиент живёт на fixed-псевдоэлементе вместо `background-attachment: fixed`, поэтому не перерисовывается при скролле. - Полноэкранный проход `mix-blend-mode` был убран, как только подтвердилось: при своей прозрачности он невидим, но дорог каждый кадр. - Каждый анимированный фон отключается на маленьких экранах и уважает reduced-motion. Итог: насыщенное движение на мощных десктопах и спокойный, дешёвый рендер на всём остальном.
Темы
Темизация — это набор CSS-переменных поверх токен-базы. Тема выбирается параметром `?theme=` в URL и применяется крошечным инлайн-скриптом, что делает состояние шарящимся: отправь ссылку — и получатель увидит ровно ту палитру, что выбрал ты.
Пайплайн экспорта документов
Тот же смерженный YAML, что рендерит сайт, генерирует и файлы для скачивания — так веб- и офлайн-версии не могут разойтись. - PDF получается рендерингом print-вёрстки в headless Chromium через Playwright — пиксель-в-пиксель с сайтом. - DOCX собирается программно через docx.js — это настоящий редактируемый документ Word, а не скриншот. - TXT отдаётся как чистый текстовый фоллбэк для ATS-систем и копипаста. Каждая пара профиль × язык получает свой набор файлов, генерируемых в CI и подключённых прямо на соответствующей странице.
CI/CD и автоматизация
Релиз — это один `git push`. - GitHub Actions гоняет весь пайплайн на каждый push в main: merge → генерация документов → рендер PDF → статическая сборка → деплой на Pages. - Бинарь Playwright Chromium кешируется по хешу lockfile, экономя около двух минут на холодном деплое. - Telegram-бот сообщает результат — успех, провал сборки или провал деплоя — с веткой и коротким SHA коммита, молча деградируя, если секреты не настроены. - `siteUrl` и base-путь выводятся из `GITHUB_REPOSITORY`, так что форк собирается и деплоится корректно без всякой конфигурации. - Sitemap, покрывающий каждый маршрут профиль × язык, генерируется на сборке, с соответствующим robots.txt.
Инженерия производительности
Производительность измеряется, а не предполагается. - Статический предрендеренный HTML без стоимости гидратации по умолчанию. - Первая обложка Showcase с реальной картинкой грузится eager с высоким приоритетом, остальные остаются lazy — намеренная оптимизация LCP. - Композитинг при скролле был профилирован, дорогие слои (перерисовка fixed-градиента, полноэкранный blend-проход) убраны. - Итог: Lighthouse 100 Performance / 100 Accessibility / 100 SEO, с движением, которое остаётся плавным на слабом железе.
Движок Showcase
Портфолио-половина сайта — та же идея, применённая рекурсивно. Проекты объявляются YAML-списком, и каждый может нести полноценный Deep Dive кейс — как этот — собранный из небольшого набора контент-блоков: text, image, divider и links. Блоки валидируются типизированной схемой и рендерятся отдельными компонентами, а значит длинный редакторский кейс пишется целиком в данных, без какого-либо кастомного кода страницы. Ведёт себя как контролируемая статическая альтернатива странице Notion: портативно, версионируемо и быстро.
Инженерные решения
- Astro вместо SPA-фреймворков — чтобы убрать лишний рантайм и стоимость гидратации
- Статическая генерация — ради гарантии производительности и предсказуемости
- YAML вместо CMS — ради полного контроля и Git-нативного версионирования
- Состояние через URL вместо localStorage — ради шаринга и прозрачности
- Явные дельта-файлы вместо неявных переопределений — чтобы изменения были наблюдаемы
- Типизированные схемы контента — чтобы кривые данные роняли сборку, а не браузер
DevOps-подход
Проект относится к резюме как к build-артефакту. Ключевые принципы: - версионирование на Git - неизменяемые сборки - автоматизация CI/CD - развёртывание, независимое от окружения - нулевые runtime-зависимости Каждый коммит порождает полностью согласованное, развёртываемое состояние системы.
Компромиссы
- Нет CMS — намеренный компромисс ради контроля и прозрачности
- Нет бэкенда — чисто статическая архитектура
- Ручное создание дельт для профилей — явность вместо неявной сложности
- Canvas-фоны дороже CSS-дефолта — поэтому один по умолчанию, остальные по выбору
- Ограниченная гибкость в рантайме в пользу детерминизма
Результат
- Одна правка перегенерирует все форматы по всем профилям и языкам
- Полностью статический деплой с нулевой стоимостью инфраструктуры
- Готовая к форку система, не требующая конфигурации
- Lighthouse: 100 Performance / 100 Accessibility / 100 SEO
- Предсказуемый, воспроизводимый вывод между окружениями
Выводы
CV Hub показывает, что даже нетрадиционные домены вроде резюме выигрывают от инженерной строгости. Применяя принципы системного дизайна — единый источник правды, детерминированные пайплайны и автоматизацию — проект превращает статический документ в масштабируемую, поддерживаемую систему. Этот подход обобщается за пределы резюме на любой домен, где важны согласованность данных, воспроизводимость и вывод в несколько форматов.