Все проекты
lavier.tech
Open Source
web

lavier.tech

Сайт, который вы сейчас читаете: Next.js 16 + Go 1.25, весь контент двуязычный в Postgres JSONB, за ним админка, push-ревалидация ISR и деплой с health-gate и авто-откатом

В цифрах

0

Языка контента

0

Разделов в админке

0 s

Окно health-gate

auto

Откат при падении

Проблема

Что я решала

Большинство портфолио разработчиков держат контент прямо в JSX. Поправить опечатку — это правка, коммит и деплой; добавить второй язык — удвоить каждый файл и через месяц гарантированно получить разъехавшиеся переводы. А личный сайт — как раз то место, где контент меняется постоянно: новый проект, новая цифра в кейсе, новая должность.
Мой подход

Как я делала

Контент живёт в Postgres, а не в кодовой базе. Каждое переводимое поле — JSONB-объект с ключами en и ru, так что проект или запись об опыте это одна строка, несущая оба языка. Go-бэкенд их отдаёт, Next.js рендерит статикой, админка пишет обратно и запускает ревалидацию ровно тех путей, которые изменились. Фронт разложен по Feature-Sliced Design, чтобы entities, features и widgets оставались разделимыми. Деплой повторяет клиентские проекты: GitHub Actions собирает оба образа, пушит в GHCR, снимает дамп БД перед каждым выкатом, прогоняет goose-миграции и помечает релиз хорошим только после прохождения health-gate.

Выбор технологий

  • Postgres JSONB for i18nОдна строка на сущность с парой {en, ru} лучше параллельных таблиц или параллельных JSON-файлов. Добавить язык — это ключ, а не миграция, а недопереведённое поле видно сразу, а не проваливается молча в фолбэк.
  • Next.js 16 + push-based ISRСтатические страницы с ревалидацией по требованию дают скорость статики и поведение CMS. Админка сама сообщает, какие пути пересобрать, так что не приходится подбирать интервал revalidate и надеяться, что он достаточно короткий.
  • Go 1.25 backendОдин маленький бинарь рядом с Postgres — быстрый старт, крошечный образ и никакого второго Node-рантайма. Заодно упрощается прокси аналитики: singleflight схлопывает параллельные запросы к Umami в один поход наверх.
  • Caddy + health-gated deployCaddy сам держит TLS и фронтит несколько приложений на одном VPS. Важна именно часть с health-gate: релиз считается хорошим только после того, как контейнеры ответили, поэтому откат — это чтение файла, а не ночь отладки.
Результат

Что получилось

Правки контента выкатываются за секунды и без деплоя, а оба языка не разъезжаются, потому что физически лежат рядом. Восемь разделов админки покрывают всё, что рендерит публичная часть. В пайплайне деплоя стоит health-gate на 120 секунд и авто-откат к .last-good-sha, поэтому плохой релиз не превращается в простой. Заодно на сайте собрана вся SEO-обвязка, которую просят клиенты: sitemap, robots, JSON-LD, Open Graph-картинки под каждый проект, hreflang-пары и пинг IndexNow при публикации.