Кейс: публикация статьи за 8 секунд вместо 40 минут пересборки | AI-Mrkt
AI
AI Marketing Community
Вступить бесплатно
Главная/Кейсы/Next.js · Архитектура · Claude Code
Next.js · Архитектура · Claude Code

Как отвязать контент от сборки: с 40 минут до 8 секунд на публикацию

Добавление одной статьи запускало полную пересборку сайта на 11 000+ страниц. Разбираю, как отвязать контент от сборки: файлы вместо бандла, on-demand ISR и точечная ревалидация вместо деплоя.

40 мин → 8 секвремя публикации
11 000+страниц без пересборки
ISRon-demand рендер

Проблема: одна статья пересобирала весь сайт

Есть большой образовательный портал на Next.js — каталог и блог, вместе больше 11 000 страниц. Всё это собрано и поддерживается вайбкодингом, в диалоге с Claude Code. Блог там — главный канал органического трафика, статьи выходят пачками, часть из них пишет и публикует контент-автопилот на планировщике.

И была боль, которую владелец сформулировал одной фразой: «почему ради одной статьи ты каждый раз собираешь весь сайт заново и заливаешь его целиком?» Вопрос абсолютно правильный. Добавление одной статьи запускало полный next build на все 11 000+ страниц и перезаливку собранного бандла на сервер. По факту — десятки минут на публикацию пары абзацев.

Если публикация текста запускает сборку кода — значит, контент стал частью программы. А он должен быть данными.

Диагноз: контент был вшит в бандл

Первым делом я нашёл корень, а не симптом. Все статьи блога лежали внутри исходника — большим массивом прямо в коде, который компилируется в бандл сайта. Для сборщика это не «данные», а часть приложения. Поэтому любая правка текста = изменение кода → полная пересборка и повторная генерация всех статических страниц.

Добивал деплой: каждая сборка меняет хеши файлов, а ссылки на них зашиты почти в каждую HTML-страницу. Из-за этого «инкрементальная» заливка на деле тащила почти весь сайт заново. Отсюда и ощущение владельца, что «перезаливается всё целиком» — оно было близко к правде.

Идея фикса: отвязать контент от сборки

Решение — разъединить две вещи, которые были склеены: контент и сборку. Причём это не эксперимент: на этом же проекте один раздел уже годами работал по нужной схеме, я просто распространил её на блог.

  1. Контент — на диск, а не в бандл. Каждая статья становится отдельным файлом content/blog/<slug>.json, который читается в рантайме через файловую систему, а не через import. Теперь добавить статью — это изменение данных, а не кода.
  2. Рендер — по требованию (ISR). На странице статьи я включил dynamicParams и revalidate. Новая статья, которой не было на момент сборки, рендерится при первом запросе и кешируется. Полная пересборка не нужна.

Как это устроено

Модуль доступа читает одну статью с диска ровно тогда, когда её запросили, — без базы и без CMS, просто файлы в репозитории. А конфиг маршрута — это буквально три строки, которые меняют всё поведение:

export const dynamicParams = true   // новый slug рендерится on-demand
export const revalidate = 86400      // ISR-кеш

// generateStaticParams при этом всё ещё читает content/ —
// значит полная сборка по-прежнему пре-рендерит все статьи,
// а появившееся МЕЖДУ сборками отдаётся на лету.
Инсайт

Список статей для индекса и sitemap нельзя читать один раз при старте модуля — иначе новый файл не появится без перезапуска процесса. Читать нужно внутри функции рендера, тогда ISR сам решает, когда перечитать. Это неочевидная разница между «работает на демо» и «работает в проде».

Мгновенная публикация вместо деплоя

Дальше — самое важное для владельца. Я добавил защищённый эндпоинт, который точечно сбрасывает кеш нужных страниц через revalidatePath, и собрал из этого скрипт публикации. Теперь выход новой статьи выглядит так:

# 1. записать content/blog/<slug>.json
# 2. git commit && git push        (уезжает ОДИН маленький файл)
# 3. git pull на сервере            (он забирает только его)
# 4. дёрнуть /api/revalidate/        (кеш обновился)
# → без next build, без перезаливки бандла

Никакой полной сборки. Файл контента доезжает до сервера обычным git pull — тем же каналом, что уже возил другие рантайм-данные проекта. Полный деплой теперь нужен только когда меняется код сайта, а не тексты статей.

Грабли, на которые я наступил за владельца

Такая миграция на живом проде — это не «поменять пару файлов». Вот что реально стреляет:

Результат

8 сек
публикация статьи вместо ~40 минут
11 000+
страниц больше не пересобираются
×300
быстрее выход новой статьи

Цифры — фактический замер на проде: тестовая статья опубликовалась за 8 секунд и сразу открылась по своему адресу, без единой пересборки. Контент-автопилот, который раньше упирался в те же десятки минут и в перегрузку сервера, теперь публикует статьи мгновенно.

Но главный итог даже не в секундах. Пока контент склеен со сборкой, каждая публикация — это событие для всего сайта: риск, нагрузка, ожидание. Как только контент становится данными, публикация превращается в то, чем и должна быть, — быстрое и скучное действие. А скучное действие можно смело отдавать на автопилот.

Дмитрий Коновалов

Дмитрий Коновалов

CMO · AI-маркетинг

10 лет в маркетинге. Строю комьюнити AI-маркетологов и вайбкодеров в России. Каждый кейс сначала проверяю на своих проектах и бюджетах, потом разбираю в Telegram @dima_konovalov_edtech.

Хочешь делать так же?

В канале @dima_konovalov_edtech — реальные кейсы, рабочие промпты и разборы того, как AI меняет маркетинг и продажи. Без воды, только практика.

Вступить бесплатно

Частые вопросы

Почему добавление одной статьи пересобирало весь сайт?

Потому что статьи были вшиты в исходник — большим массивом в коде, который компилируется в бандл. Для сборщика это часть программы, а не данные. Значит правка текста = изменение кода → полная пересборка всех страниц. Лечится выносом контента в отдельные файлы, читаемые в рантайме.

Чем on-demand ISR лучше полного пре-билда?

При dynamicParams + revalidate новая статья, которой не было на момент сборки, рендерится по первому запросу и кешируется. Существующие страницы пре-рендерятся как обычно, а свежие отдаются на лету — пересобирать весь сайт ради одной статьи больше не нужно.

Как публиковать статью без деплоя?

Записать файл контента, доставить его на сервер обычным git pull (уезжает один маленький файл) и дёрнуть защищённый эндпоинт, который через revalidatePath сбрасывает кеш нужных страниц. Полная сборка остаётся только для правок кода, а не текстов.

Читать дальше