Вступление: ловушка "Универсального языка"

Я в IT больше 10 лет. Начинал с верстки, вырос в фронтенд-разработчика, потом стал тимлидом, далее менеджером проектов и сейчас руковожу отделом разработки решений для бизнеса. Я знаю JavaScript. Я люблю JavaScript. И именно эта любовь сыграла со мной злую шутку.

В индустрии есть опасная иллюзия: «Если ты знаешь JavaScript, ты – фулстек». Node.js дал нам, фронтендерам, суперсилу. Мы можем писать серверный код на том же языке, что и клиентский. Мы можем использовать одни и те же типы, одни и те же библиотеки.

Нам кажется, что разница между фронтендом и бэкендом — только в том, где выполняется код.
В браузере нет fs (файловой системы), а на сервере нет window. Вот и вся разница, да?

Нет. Разница не в синтаксисе. Разница в мышлении. И я узнал это самым тяжелым способом – положив релиз.

Загрузка страницы на свежеиспеченном сайте
Загрузка страницы на свежеиспеченном сайте

"Я сам всё сделаю"

В то время я работал менеджером проектов в штате застройщика. Это было самое начало моего управленческого пути. Я допустил ошибку и не успел вовремя найти бэкенд-разработчика, а сроки запуска проекта внезапно сократили.

И тут во мне проснулся герой: — «Да что там писать? Это же просто API! У меня 5 лет опыта в JS. Я сам всё сделаю!»

Стек я выбрал не сразу. Сначала смотрел в сторону облачных Headless CMS вроде Prismic.io или Contentful. Но это не покрывало все задачи, даже если часть логики и контента оставить жить на клиенте.

Мне нужно было решить бизнес-задачу: дать маркетингу гибкий инструмент, причем быстро.

И тут я наткнулся на Strapi. Опенсорс, Node.js, ставится на свой сервер, полностью настраивается через админку. Это была любовь с первого взгляда. Я никогда раньше с ним не работал, но документация обещала, что всё будет легко.

В итоге стек выглядел так:

  • Бэкенд: Strapi (Headless CMS).

  • Фронтенд: Nuxt.js (Vue).

  • База: MongoDB. Причина выбора была до смешного банальной: я просто не смог за час поднять MySQL локально. Моя самоуверенность («да что там эти базы») разбилась о первый же конфиг, а Mongo заработала из коробки без лишних вопросов.

Архитектура "конструктора"

Как мыслит фронтендер? Он мыслит Компонентами. У нас есть шапка, слайдер, блок "Преимущества", форма заявки, футер.

Я решил, что бэкенд должен быть зеркалом фронтенда. Я хотел сделать идеально для маркетологов. Я решил сделать Конструктор.

В Strapi я создал эндпоинты для отдельных сущностей. А внутри них – «Динамические зоны» (Dynamic Zones). Логика была такая:

  1. Маркетолог заходит в админку.

  2. Выбирает сущность, например, жилой комплекс.

  3. Накидывает блоки: «Хочу здесь слайдер», «Хочу здесь текст», «Хочу здесь форму».

  4. Заполняет поля (картинки, заголовки).

Фронтенд (Nuxt) получал этот JSON, проходился по массиву компонентов и рендерил их, примерно так:

<component
  v-for="block in page.blocks"
  :is="block.componentName"
  :data="block.data"
/>

Это казалось гениальным. Полная гибкость! Маркетолог счастлив, он может собрать посадочную страницу без программиста. Фронтенд счастлив, компоненты переиспользуются. Локально, на моем макбуке с боевыми данными, всё летало.

Холодный душ

День релиза. Сайт уже был наполнен контентом под завязку: сотни квартир, десятки планировок, рендеры, акции и новости. Мы запустили сайт. Пошли реальные пользователи.

Это не был Highload в понимании Яндекса. У нас было порядка 30–50 уникальных посетителей на сайте одновременно — нормальная цифра для регионального застройщика. Но этого хватило. Открываю главную страницу.

Жду. Секунда. Две. Три. ... Шесть секунд.

Белый экран. Браузер крутит спиннер. Эти шесть секунд показались мне вечностью. Я в ужасе представлял, как генеральный директор открывает сайт с телефона в лифте, где интернет ловит через раз, и видит эту бесконечную пустоту.

Потом контент резко вываливается.

Я в панике открываю DevTools. Время ответа сервера (TTFB) — 6.8 секунды. Почти 7 секунд сервер просто «думал», прежде чем отдать хоть байт данных. Для E-commerce и сайтов недвижимости это смерть. Клиент уйдет к конкуренту ещё на третьей секунде.

Что пошло не так?

Я полез в логи и профилировщик базы данных. То, что я увидел, заставило меня поседеть.

Мой «гениальный конструктор» породил монстра. Чтобы собрать одну страницу, Strapi должен был вытащить запись сущности, а потом через populate (аналог JOIN в SQL) подтянуть данные для всех 20 блоков. А некоторые блоки были сложные.

  • Блок «Похожие квартиры» ссылался на сущность Apartment.

  • Сущность Apartment ссылалась на Building.

  • Building ссылался на District.

MongoDB — это документоориентированная база. Она не любит джойны. А я заставил её делать рекурсивные джойны вложенностью в 5-6 уровней для каждого запроса.

Я спроектировал базу данных так, как проектируют React/Vue компоненты — вложенными деревьями. Но база данных — это не DOM-дерево.

Мои ошибки:

  1. Отсутствие нормализации. Я хранил всё в одной куче.

  2. Over-fetching. Я тянул всю базу, чтобы показать заголовок.

  3. Непонимание стоимости операций. Для меня db.find() было магией. Я не думал, как именно база ищет эти данные на диске.

Синяя изолента (Redis)

Переписывать архитектуру было поздно. Срочно нужен был рабочий сайт. Я искал решение «здесь и сейчас» и вспомнил про кэширование. Если база тормозит — не трогай базу, отдавай готовое.

Я поставил Redis. Нашел плагин для Strapi (он был сырой, мне даже пришлось отправить Pull Request с фиксом, чтобы он заработал полноценно).

Логика:

  1. Первый юзер заходит — ждет 5 секунд (страдалец).

  2. Мы сохраняем готовый JSON в оперативную память (Redis).

  3. Второй юзер заходит — получает ответ за 50 миллисекунд.

Да, первый пользователь после каждого деплоя или обновления контента всё ещё страдал. Кэш нужно было прогревать. Но это было меньшее из зол, и бизнес это устроило.

Это спасло проект. Отдел маркетинга успокоился, сайт «летал» (для 99% пользователей). Но я знал правду. Под капотом этого «Феррари» стоял двигатель от мопеда, перемотанный синей изолентой.

Фронтенд ≠ Бэкенд

Этот опыт научил меня большему, чем все курсы и книги до этого.

Я понял, почему на собеседованиях сеньор-бэкендеров гоняют по «скучной теории»: нормальные формы БД, индексы, транзакции, CAP-теорема. Раньше я думал: «Зачем это? Мы же просто JSON перекладываем!».

Я ошибался.

1. Ментальная модель фронтенда — это "Состояние и Реактивность". Ты думаешь о том, как интерфейс реагирует на действия юзера. Компоненты, пропсы, ивенты.

2. Ментальная модель бэкенда — это "Данные и Надежность". Ты думаешь о том, как эффективно хранить данные, как их быстро достать, и что будет, если одновременно придут 1000 человек.

Знание синтаксиса async/await не дает тебе этой ментальной модели. Ты можешь выучить ноты, но это не сделает тебя композитором.

Эпилог

Node.js дал фронтендерам возможность заходить на территорию бэкенда. Но он не выдал нам визу на пмж.

Мы привыкли строить красивые фасады и декорации, которые можно легко перекрасить. Но бэкенд — это фундамент. Если залить его неправильно, перекрасить не получится — придется сносить дом.

И иногда, чтобы стать настоящим фулстеком, нужно перестать думать, как декоратор, и начать думать, как инженер.


P.S. Если эта история отозвалась болью – добро пожаловать в комментарии. Расскажите, как вы впервые положили прод. Это сближает.

Комментарии (24)


  1. max9
    15.02.2026 11:57

    я просто не смог за час поднять MySQL локально

    really? кажется это мог сделать любой эникейщик из времен LAMP


    1. MrTheFirst Автор
      15.02.2026 11:57

      Подробностей уже не вспомню, но была ошибка, которою сходу устранить не смог. Поэтому просто с 0 развернул strapi, поставив галочку напротив mongodb. Тогда для меня большой разницы между этими БД не было, главное чтобы работало.


      1. max9
        15.02.2026 11:57

        в 99% дистрибутивов это ставится как yum/apt install mysql/mysql-server. все что надо сделать. и 20 лет назад так же работало.


        1. MrTheFirst Автор
          15.02.2026 11:57

          С высоты опыта всё кажется простым. Но суть статьи именно в трансформации мышления.

          Когда ты всю жизнь мыслишь категориями npm install и import Component, необходимость конфигурировать базу данных вызывает ступор. Это сейчас я понимаю, что это базовые знания. А тогда это была магия.

          И да, эникейщик бы справился быстрее. Но я не эникейщик, я инженер, который учился на своих ошибках. И эта ошибка стоила мне нервов и бессонных ночей, зато теперь я знаю цену своим решениям.


      1. anshdo
        15.02.2026 11:57

        Просто вот именно в этот момент уже нужно было задуматься: "А достаточно ли у меня компетенций, чтобы сваять бэкенд? Если я даже СУБД развернуть не могу."


      1. iscareal
        15.02.2026 11:57

        Видимо это было давно? На сегодняшний день Strapi поддерживает только реляционные БД


        1. MrTheFirst Автор
          15.02.2026 11:57

          Примерно 2019 год, strapi v3


  1. i360u
    15.02.2026 11:57

    Хм, вполне нормальная история, "медленная база / быстрый кэш" - это самый обычный паттерн для бэкэнда. О деталях, конечно, можно подискутировать, но в целом все рамках средних по больнице. А так - все лажают по неопытности, не вижу причин посыпать голову пеплом или считать что бэк - это совсем отдельная сфера. Вы так говорите, будто война за производительность - это исключительно прерогатива бэка, но и там и там применим тот-же самый асимптотический анализ сложности, а это основа вообще для любого разработчика.


    1. MrTheFirst Автор
      15.02.2026 11:57

      Теория одна – согласен, но «бытовое» применение отличается. Фронтенд чаще бьется за FPS и перерисовки, а бэкенд – за IO и память.

      По моим наблюдениям, переключиться именно с фронта на бэк особенно сложно. Теряется привычная визуальная опора, меняется сама парадигма: от событийной модели UI к транзакциям и работе с данными.

      Инерция мышления тут часто играет злую шутку: разработчики по привычке пытаются перенести UI-паттерны в бэкенд-архитектуру «один в один».


      1. i360u
        15.02.2026 11:57

        Я бы поспорил. На мой скромный взгляд, лучшие инженерные решения рождаются из пересечения компетенций, и в данном случае это именно фронт и бэк. Переносить какие-то паттерны и ментальные модели с фронта на бэк - не такая уж плохая идея, если вы знаете что делаете. И использование общей кодовой базы как и инструментов окружения - это ОЧЕНЬ ценный момент, чтобы от него просто отмахнуться. Помимо этого, бэк - это такая же сегментированная область, как и фронт и там и там вы можете встретить транзакции (как минимум IndexedDB) и большие данные летающие по вебсокетам в дэшбордах реального времени... как и генерацию статических ассетов на сервере, для которой не так важна максимальная оптимизация по IO. Развитие разработчика в сторону фуллстека - это никакая не ошибка, а самый правильный, на мой взгляд, путь для любого, кто хочет стать специалистом выше среднего.


        1. MrTheFirst Автор
          15.02.2026 11:57

          Согласен про пересечение компетенций. Знать (а лучше уметь) всё – полезно.

          Но давайте честно: сколько таких специалистов «выше среднего», о которых вы говорите? 5%? Моя статья про остальные 95%, которые приходят на бэкенд с ментальностью npm install и «оно само соберется».

          Фронтендер привык думать категориями: «Как это выглядит? Быстро ли отрисовалось? Удобно ли пользователю?».
          Бэкендер должен думать: «Что будет при параллельных запросах? Как это мигрировать через год? Выдержит ли база?».

          Когда ты приходишь на бэк со старой «фронтендерской прошивкой», ты инстинктивно оптимизируешь не то. Ты делаешь красивый API, но забываешь про транзакции. Ты кешируешь всё подряд ради скорости, но получаешь неконсистентность данных.

          Фуллстек – это круто, когда ты переключаешь этот тумблер в голове. А я его переключать даже не умел, просто не было соответствующего опыта. Я попал в те 95%.


    1. Elendiar1
      15.02.2026 11:57

      Выглядит как история про неправильно спроектированные эндпоинты.

      Вываливать все данные без пагинации, да еще и со всем вложенными сущностями - это уже не ок. Да еще и без оптимизации кверь (в джанге это select/prefetch_related)


  1. cskeleto
    15.02.2026 11:57

    e кластером на обычном 10G Ethernet. Думал: "ну API же, что сложного". Забыл про то, что GPU батчи создают неравномерную нагрузку, и при спайках трафика начались таймауты.

    Redis спас вас, это правда. Но есть подводный камень с инвалидацией кэша. Если маркетолог обновляет контент, нужно сбросить кэш. У вас как это решено? Вручную или есть хуки на Strapi updates?


    1. MrTheFirst Автор
      15.02.2026 11:57

      «Ну API же, что сложного» – это вообще девиз, который надо писать на надгробиях проектов. Батчи и спайки нагрузки – это как раз то, о чем фронтендер обычно не думает (у нас же Event Loop, оно само разрулится).

      Про кэш:

      Сначала хотел сделать «умную» инвалидацию через хуки Strapi (onUpdate -> clear cache). Но быстро понял, что задача усложняется связанными сущностями (обновил новость -> надо сбросить кэш списка новостей -> и кэш главной).

      В итоге пришел к самому надежному (и стыдному) решению: кнопка «Сбросить» в админке. Маркетинг сам решал, когда им нужно обновить данные. И это сработало лучше любой автоматики)


  1. kellas
    15.02.2026 11:57

    Ой, да вы и фронтенд как попало делаете)


  1. muhachev
    15.02.2026 11:57

    И иногда, чтобы стать настоящим фулстеком, нужно перестать думать, как декоратор, и начать думать, как инженер.

    Достаточно просто начать думать, а не выдумывать всякую фантастически фэйковую поучительную нейрослопохрень.


  1. kneaded
    15.02.2026 11:57

    Тут кто-то говорил, что нельзя разделять back и front, мол это всё одно и то же. Я абсолютно не согласен. Я, начинавший с бэкенд, пощупавший front понимаю, что можно на одном языке для back (например Python Django) построить фронт, как и наоборот. Это, с одной стороны, удобно, но ограничивает в некоторых специфичных кейсах.

    Ну например, на языке фронта минимально что-то похожее на pytorch и других библиотеках ИИ (к примеру computer vision) вряд ли построить, а на языке бэкенд вряд ли можно построить что-то похожее на анимации и поведение в зависимости от действий пользователя, как это делает красивый классный Javascript в связке с CSS.

    В общем и целом - и там и там инженеры, база одна, ОС одна, но как только коснись нюансов - пошло поехало


  1. rakot
    15.02.2026 11:57

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


    1. MrTheFirst Автор
      15.02.2026 11:57

      Самое смешное, что мы сделали полный круг. Server Actions в Next.js – это буквально php, только с типизацией.


      1. rakot
        15.02.2026 11:57

        Меня чуть в монитор не стошнило когда я в шаблонах увидел SQL запросы к базе, я такого ужаса насмотрелся еще 20 лет назад =)


  1. Spyman
    15.02.2026 11:57

    Я заметил, что чертовски часто (на 5 коммерческих проектах из десятка на которых я работал за всё время) - люди начинают придумывать сописный конструктор, для сборки интерфейса из блоков. И что ещё интереснее - за все 5 проектов не было ни одного, где этот конструктор был бы оправдан бизнес целями и не обходился в обслуживании дороже, чем линейная разработка фичей.

    Вот эту закономерность интересно было бы изучить)


  1. marsden
    15.02.2026 11:57

    История отозвалась не болью, а улыбкой)) но потом вспомнил, как я приходил к пониманию разницы бэк/фронт. Не так трагично, не через поломанный релиз, а просто на интуиции, что не может нода нормально отрабатывать на множестве запросов, иначе уже весь интернет сидел бы на такой "вкусняхе" вместо lamp & variations. Ну и то, что в реактивность фронта входил уже после статики сыграло свою роль, фактически стек бэка уже был


  1. skeevy
    15.02.2026 11:57

    Вы придумали заново Backend Driven Ui, но слишком наивный. В этом нет ничего плохого, но это надо «докручивать» и странно, что вы сразу заразили (если я правильно понял) а не прогнали на тестовом стенде хотя-бы. Вот скорее всего проблема не в мышлении, а в том что это не проверено. Особенно на специально утрированных кейсах, которых у вас не будет, но вдруг…


  1. Akhmed_x051
    15.02.2026 11:57

    Ну чел боже, ты с самого начала задумал проект как один большой костыль.

    Я никогда бек не знал, взял за стек next.js (фронт + сео) + drizzle.orm (бд) + next.js server actions (для бека). С первого же раза написания Бека, написал ее практически идеальной, с нужными схемами и связями.

    Хотя эндпоинты сводились к тому что просто записать-отдать, проект я написал в максимально сжатые сроки. Да и сервис не предназначается для высоконагруженного сервиса. Это просто сервис для строительной компании узкой специализации. Зато скорость разработки, качество (для своей задачи) и проще в деплое