Производительность — это базовое требование в разработке. Однако не всегда о базово высокой производительности говорят на этапе обсуждения проекта. Заказчик вряд ли скажет: «Сделайте так, чтобы сайт отвечал за Х мс», но он точно удивится и расстроится, если все будет грузиться медленно. В команде тоже мало кто рад: проект сдан, а теперь приходится выяснять, что тормозит и почему.

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

Уделять внимание вопросам производительности с самого начала

Производительность — не дополнительная штука, которую можно докрутить в самом конце. Это часть требований. Просто о производительности часто не договариваются в явном виде. Никто не говорит: «Сделайте так, чтобы навигация отрисовывалась за 10 миллисекунд». Но подразумевается, что всё должно работать нормально. Как в любой другой профессии, например, дорожного строителя, тебе не скажут: «Пожалуйста, положите асфальт без дыр». Это должно быть по умолчанию.

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

Можно воспринимать это как навык. Или даже привычку. Не оптимизировать заранее, а просто замечать, где может работать плохо. 

Кто отвечает за производительность сайта

На проектах этот вопрос возникает не сразу. Обычно — уже после запуска, когда начинаются проблемы. Кто виноват, если страницы долго открываются? Кто должен был предусмотреть, что в пиковые часы все встанет? Кто не проверил, что навигация запрашивает из CMS по сто элементов на каждой странице?

В команде быстро становится ясно: этим должен был заняться кто-то из разработчиков.

Бывает, что в начале проекта никто не уточняет, какой трафик планируется, сколько пользователей может быть одновременно на сайте, что именно должно оставаться быстрым при нагрузке. В брифах и юзер-стори таких цифр может не быть. Но это не освобождает от ответственности. Потому что даже если требования не прописали, ресурс всё равно должен работать стабильно. И когда он не справляется, первым делом идут не к менеджеру и не к дизайнеру.

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

У опытных разработчиков такая ответственность уже встроена в процесс. Они не ждут отдельного запроса. Они сами уточняют, под какой трафик должен работать сайт, и прикидывают, выдержит ли инфраструктура то, что они собираются развернуть.

Перфоманс ресурса — это такая же задача, как логика, работающая кнопка или правильный API-запрос. Если всё будет красиво, но медленно, никого не будет заботить, что «в тикете этого не было».

Что значит «быстрая загрузка», и кто это решает

Скорость — штука коварная. Для одних и две секунды загрузки нормально, другие начинают нервничать уже через полторы. Пользователь может не сформулировать, что именно его не устраивает, но если сайт медленно реагирует, негативное ощущение останется.

Чтобы не гадать, что считать «нормальной» скоростью, можно опираться на готовые исследования. Например, Google потратил немало усилий, чтобы разобраться, как пользователи воспринимают задержки. На основе этих данных строятся метрики вроде Time to Interactive и других показателей в PageSpeed и Lighthouse. Это понятная отправная точка, особенно если нужно говорить о производительности с клиентом или внутри команды.

Если говорить не о фронтенде, а о серверной части, у разработчиков часто возникает вопрос: за сколько должен отвечать сервер, чтобы считалось, что все в порядке? Ориентир простой: в 95% случаев HTML-страница должна отдаваться за 200 миллисекунд или быстрее. Это не значит «один раз померили локально — подходит». Это значит, что при реальной нагрузке — не только у вас, но и у десятков пользователей — сервер стабильно укладывается в эти рамки.

Почему именно 200 миллисекунд? Потому что всё, что выше, начинает отъедать общее время, за которое сайт должен не только отдать HTML, но и загрузить скрипты, стили, отрисовать интерфейс. Если сервер тормозит, фронтенду не из чего собирать быструю страницу.

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

Как понять, какую нагрузку сайт должен выдерживать

Если проект делается для бизнеса, у которого уже есть сайт, все просто. Достаточно посмотреть аналитику — Метрику или любой другой инструмент, которым пользуется клиент. Там обычно можно найти три ключевых значения:

  • самый загруженный месяц,

  • самый активный день,

  • пиковый час.

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

Если сайт разрабатывается с нуля и никаких цифр нет, придётся строить нагрузочную модель по косвенным признакам. Это несложно, но требует диалога с заказчиком. Вот что нужно узнать:

  • Кто будет пользоваться сайтом?

  • Сколько примерно людей — в день, в месяц, одновременно?

  • Сколько заявок или покупок он должен собирать?

  • Какую конверсию клиент считает нормальной?

  • Есть ли у бизнеса сезонность или периоды всплеска?

С этими цифрами можно построить простую цепочку: если сайт должен давать 10 000 заявок в месяц, а конверсия, по словам клиента, — 1%, значит, нужно около 1 000 000 сессий в месяц. Разделить это по дням и прикинуть среднюю продолжительность одной сессии — уже достаточно, чтобы примерно понять, сколько людей могут быть на сайте одновременно.

Например, если в день ~33 000 сессий, и каждая длится в среднем 1 минуту, то в любой момент времени на сайте может находиться примерно 20–30 человек одновременно. Это concurrent users — количество сессий, которые происходят параллельно. 

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

Бывает полезно смотреть на аналитику конкурентов, если она доступна. Или использовать данные с похожих проектов в своей практике. Любая опора — лучше, чем «а давайте потестим в проде».

И тут важный момент: все это нужно явно проговорить с клиентом, даже если расчеты пока довольно грубые. Во-первых, потому что часто у клиента такие ожидания есть, просто их никто не спросил. А во-вторых — чтобы потом не пришлось объяснять, почему система, рассчитанная на 5 человек, не справляется с 500.

Как проверить, не тормозит ли то, что уже написано

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

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

Если коротко: локального теста чаще всего хватает, чтобы заметить проблемы. Может, точной цифры по каждому запросу узнать не получится, но зато можно увидеть, что, например, одна и та же страница подгружается по 20 секунд. Или что компонент генерирует десятки запросов в базу без нужды. Или что отрисовка меню тормозит даже без данных. И этого уже достаточно, чтобы остановиться и поправить.

Если хочется чуть больше контроля, можно подключить инструменты. Например, K6 — лёгкий фреймворк, в котором можно задать поведение виртуальных пользователей и посмотреть, как сайт реагирует на нагрузку. Там не нужно быть инженером по тестированию — достаточно набросать простой скрипт, и он покажет, насколько быстро сервер отвечает, где возникают задержки, какие страницы выбиваются по времени. Все это можно запустить без сложной настройки. Даже если знаний по К6 у вас совсем нет, с чатом GPT или любой другой нейронкой вы справитесь с задачей за полчаса-час. Мы проверили ?

Ещё один плюс: K6 можно использовать даже для того, чтобы просто пробежаться по всем страницам сайта — например, по карте ссылок (sitemap). Это помогает проверить, как он ведёт себя на холодную, без прогрева кэша. Часто именно в этот момент всплывают скрытые проблемы — медленные компоненты, цепочки запросов, отсутствие кэширования, да и просто ошибки сервера. Это не нагрузочный тест, но он даёт понимание: если сайт тормозит при одном пользователе, под сотней он точно не справится.

Тестировать можно и через Postman, и через плагины, и даже с помощью обычных замеров в браузере. Но смысл не в инструментах. Смысл в том, чтобы не ждать, пока тестировщик скажет, что «что-то не так». Проверить можно самому — и лучше сделать это до того, как код уйдет дальше по цепочке.

Где чаще всего прячутся тормоза на сайте

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

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

Вот несколько примеров из реальной практики.

Навигация, хедер, футер

Если для этих блоков каждый раз запрашивать данные из CMS или базы, особенно с вложенностью, получится очень дорогая операция. А поскольку хедер, футер и навигация есть буквально на каждой странице, то чем больше сайт, тем больнее это будет. Хотя на самом деле навигация меняется довольно редко, и ее можно кэшировать или сгенерировать заранее, не боясь показывать «протухшие» данные.

Breadcrumbs

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

Метаданные

Canonical, hreflang, alternate-URL – всё, что нужно для SEO – иногда подтягивается в реальном времени и для каждого языка. Если языков на ресурсе 10–15, это уже десятки запросов к базе или API. При этом они редко кешируются, потому что считаются «мелочами».

Юзерские данные

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

Списки и фильтры

Листинг карточек, сортировка, поиск — все это часто пишут на прямых SQL-запросах к базе. На самом деле для таких задач есть более подходящие специализированные инструменты – поисковые индексы. Например, Solr, Elastic Search, Algolia, Azure Search и десятки других сервисов. Они специально предназначены для быстрого текстового поиска и фильтрации данных. 

Поисковые результаты

Найти результаты — одно дело. А вот если ты к каждой найденной странице делаешь еще дополнительные запросы за данными (например, чтобы в поисковой выдаче сразу показать еще и breadcrumbs или картинку для найденной страницы), то нагрузка на сервер резко возрастает. Даже один пользователь, просто нажимая «поиск», может создать десятки тяжёлых обращений к серверу. Тут рекомендация очень проста: все данные для отрисовки результатов поисковой выдачи должны быть добавлены в индекс заранее, еще на этапе индексации, в уже готовом для показа пользователю формате.

Картинки и редиректы

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

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

Как кэш спасает сайт, если им пользоваться

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

У кэширования есть репутация чего-то технического и необязательного. Иногда кажется, что это «второй этап», который можно доделать потом. Но по сути, это просто способ не делать одну и ту же работу многократно. А если работа тяжелая, ее лучше и не начинать заново.

Есть разные уровни кэширования. Самый понятный — CDN, который раздаёт статику и страницы без участия сервера. Дальше — output cache, когда сервер отдает уже собранный HTML, а не пересобирает его на каждый запрос. Это самый эффективный способ с точки зрения скорости: просто отдать готовую страницу, без вычислений, запросов и сборки.

Дальше идёт data-layer cache — когда ты хотя бы не запрашиваешь одни и те же данные из базы или API, а держишь их в памяти на время. Это помогает, но требует больше контроля: нужно следить за сроком жизни кэша, за актуальностью, за тем, как он сбрасывается.

Основные виды кэширования
Основные виды кэширования

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

Output cache часто не используют. Либо потому что не знают, как его внедрить, либо боятся, что будет сложно управлять инвалидацией. Но если фреймворк это поддерживает (а большинство современных — поддерживают), нужно хотя бы попробовать. Это сильно снижает нагрузку и решает массу проблем до того, как они станут критичными.

Когда проверять производительность, и кто это должен делать

Есть распространённое представление, что за перформанс на проекте отвечает тимлид. Он опытный, у него всё под контролем, он заметит, если что-то тормозит. Иногда так и бывает. Но чаще — нет.

Проблема в том, что многие вещи, которые влияют на скорость, видны только в момент, когда ты их пишешь. Когда ты решаешь, делать ли запрос к базе или взять данные из кэша. Когда думаешь, вызывать ли API в каждом компоненте или передать данные сверху. Когда собираешь сложную навигацию или breadcrumbs и решаешь подумать, как их упростить.

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

Это не значит, что теперь каждый разработчик должен стать специалистом по нагрузочному тестированию. Достаточно завести простую привычку: перед тем как закрыть задачу, проверить, как ведет себя компонент. Хотя бы локально. Хотя бы прикинуть: делает ли он что-то лишнее? Можно ли его кешировать? Нет ли там цепочки запросов, которые тормозят все приложение?

Если каждый в команде проверяет свой код хотя бы на базовом уровне, то тимлиду не приходится быть последней линией обороны. И проект в целом становится устойчивее. Потому что вместо того, чтобы в конце выяснять, что сайт не тянет, команда заранее устраняет то, что может его замедлить.

Что точно стоит проверять, прежде чем закрыть задачу

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

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

  • Запрашиваю ли я что-то, что почти не меняется? Можно ли это закэшировать?

  • Не делаю ли я одни и те же запросы в разных местах?

  • Не тяну ли я больше данных, чем нужно прямо сейчас?

  • Если у компонента есть динамика (пользователь, фильтры, поиск) — что будет при 10, 100, 1000 сессиях одновременно?

И ещё: если компонент уже работает, загляни в него глазами пользователя. Быстро ли он отвечает? Не чувствуется ли задержка? Это не всегда точный тест, но часто он первым подсказывает, что что-то не так.

Самое простое — провести быструю локальную проверку. Через K6, Postman, DevTools — неважно. Главное, чтобы ты сам понял, что написал. Тогда баги по перформансу не будут сюрпризом. А сайт не начнёт тормозить в тот самый момент, когда этого меньше всего ждёшь.

Вместо вывода

Производительность — это обычная часть разработки веб-сервиса. Да вообще любого сервиса. Просто о ней говорят меньше, чем о дизайне или логике. Но когда что-то начинает тормозить, именно она выходит на первый план.

Важно не знать всё о нагрузочном тестировании. Важно не забывать о вещах, которые действительно влияют на скорость. Замечать потенциально тяжёлые места. Проверять то, что можно проверить локально. Спрашивать, если не знаешь, какую нагрузку закладывать.

Большинство проблем с производительностью не придется решать, если не создавать их в самом начале. И это вполне посильная задача. Не для архитектора или тимлида, а для любого, кто пишет код. 

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