Бюджеты производительности помогают сохранять веб‑приложения быстрыми: задают явные пределы времени загрузки, размера JS и Core Web Vitals, а также включают быстрые аудиты и правила «фиксируй сначала».

Бюджет производительности — это набор пределов, о которых договорились до начала работы. Это может быть предел по времени (насколько быстро страница воспринимается), по размеру (сколько кода вы отправляете) или простой лимит (количество запросов, изображений, сторонних скриптов). Если вы превышаете предел, это считается нарушением требования, а не «когда‑нибудь потом» задачей.
Скорость обычно ухудшается потому, что при доставке всё суммируется. Каждой новый виджет добавляет JavaScript, CSS, шрифты, изображения, API‑вызовы и дополнительную нагрузку на браузер. Даже маленькие изменения накапливаются, пока приложение не начнёт казаться тяжёлым, особенно на телефонах среднего класса и медленных сетях, где находятся большинство реальных пользователей.
Мнения здесь вас не спасут. Один говорит «на моём ноутбуке всё нормально», другой — «медленно», и команда спорит. Бюджет заканчивает спор, превращая производительность в продуктовое ограничение, которое можно измерить и обеспечить.
Здесь подходит подход Addy Osmani: относитесь к производительности как к ограничениям дизайна и правилам безопасности. Вы не «пытаетесь» оставаться безопасными и не «надеетесь», что верстка будет хорошей. Вы задаёте стандарты, проверяете их постоянно и блокируете изменения, которые их нарушают.
Бюджеты решают сразу несколько практических задач. Они делают компромиссы явными (добавление фичи означает плату где‑то ещё), ловят регрессии рано (когда исправлять дешевле) и дают всем одно и то же определение «достаточно быстро». Они также снижают панические правки прямо перед релизом.
Вот сценарий, для которого нужны бюджеты: вы добавляете библиотеку для сложных графиков для одного вида dashboard. Она попадает ко всем, растёт главный бандл и отодвигает отображение первого значимого экрана. Без бюджета это проскочит, потому что фича «работает». С бюджетом команда должна выбрать: лениво загружать график, заменить библиотеку или упростить вид.
Это особенно важно, когда команды могут быстро генерировать и итеративно менять приложения, в том числе с чат‑управляемыми рабочими процессами вроде Koder.ai. Скорость — это здорово, но она же позволяет незаметно отправлять дополнительные зависимости и UI‑украшения. Бюджеты не дадут быстрой итерации превратиться в медленный продукт.
Работа над производительностью терпит неудачу, если вы измеряете всё и ни за что не отвечаете. Выберите один путь страницы, важный для реальных пользователей, и сделайте его якорем для ваших бюджетов.
Хорошая отправная точка — основной путь, где скорость влияет на конверсию или повседневную работу, например «главная → регистрация», «первый загруз после входа в дашборд» или «оформление заказа и подтверждение оплаты». Выбирайте что‑то представительное и частое, а не крайний случай.
Ваше приложение не работает на вашем ноутбуке. Бюджет, который выглядит нормально на быстрой машине, может казаться медленным на телефоне среднего класса.
Определите один класс устройства и один профиль сети для начала. Сохраните это просто и запишите фразой, которую сможет повторить каждый.
Например: телефон Android среднего класса последних 2–3 лет, в 4G в движении (не офисный Wi‑Fi), измеряя холодную загрузку и затем одну ключевую навигацию, в том же регионе, где большинство пользователей.
Речь не о выборе худшего случая. Речь о выборе распространённого сценария, под который вы действительно можете оптимизировать.
Числа важны только если их можно сравнить. Если один прогон — это «Chrome с расширениями на MacBook», а следующий — «симулированный мобильный», ваша линия тренда — шум.
Выберите одно базовое окружение и придерживайтесь его для проверок бюджета: та же версия браузера, те же настройки троттлинга, тот же путь теста и то же состояние кэша (холодный или тёплый). Если вы используете реальные устройства — используйте ту же модель.
Теперь определите, что значит «достаточно быстро» в терминах поведения, а не идеальных демо. Например: «пользователи быстро могут начать читать контент» или «дашборд отзывчив после логина». Переведите это в одну‑две метрики для этого пути и задайте вокруг них бюджеты.
Бюджеты работают лучше, когда охватывают и то, что чувствуют пользователи, и то, что команды могут контролировать. Хороший набор сочетает метрики опыта (часть «понравилось ли пользователю?») с лимитами ресурсов и CPU (часть «почему стало медленно?»).
Они отслеживают, как страница ведёт себя для реальных людей. Наиболее полезные соотносятся с Core Web Vitals:
Временные бюджеты — ваш северный ориентир, потому что они соответствуют пользовательскому раздражению. Но они не всегда говорят, что именно исправлять, поэтому нужны и другие типы бюджетов ниже.
Их проще применить в сборке и при ревью, потому что они конкретны.
Бюджеты веса ограничивают общий JavaScript, общий CSS, вес изображений и шрифтов. Бюджеты по запросам ограничивают количество запросов и сторонних скриптов, уменьшая сетевую нагрузку и «сюрпризы» от тегов, виджетов и трекеров. Бюджеты времени выполнения ограничивают длинные таски, время на основной потоке и время гидратации (особенно для React), что часто объясняет, почему страница «кажется» медленной на телефонах среднего класса.
Практический пример на React: размер бандла может быть в порядке, но новый карусельный компонент добавляет тяжёлую клиентскую отрисовку. Страница загружается, но при нажатии фильтров интерфейс «залипает», потому что гидратация блокирует основной поток. Бюджет времени выполнения вроде «нет длинных задач свыше X мс в старте» или «гидратация завершается в течение Y секунд на устройстве среднего уровня» поймает это даже когда бюджеты веса молчат.
Сильный подход рассматривает всё это как систему: бюджеты опыта определяют успех, а бюджеты размера, запросов и времени выполнения держат релизы честными и упрощают ответ на вопрос «что изменилось?».
Если вы зададите слишком много лимитов, люди перестанут обращать внимание. Выберите 3–5 бюджетов, которые соответствуют тому, что чаще всего чувствуют пользователи, и которые вы можете измерять для каждого pull request или релиза.
Практический стартовый набор (позже подбирайте числа):
Две границы делают ситуацию управляемой. «Warn» говорит, что вы отклоняетесь. «Fail» блокирует релиз или требует явного одобрения. Так предел реальный, но не создаёт постоянных паник.
Запишите бюджет в одном общем месте, чтобы никто не спорил о нём в напряжённый выпуск. Коротко и конкретно укажите: какие страницы или потоки покрыты, где выполняются измерения (локальный аудит, CI, staged build), какое устройство и профиль сети используются, и как именно определяются метрики (field vs lab, gzip vs raw, route‑level vs весь app).
Начните с базовой точки, которую можно повторить. Выберите одну‑две ключевые страницы и тестируйте их на одинаковом профиле устройства и сети каждый раз. Запустите тест минимум три раза и запишите медиану, чтобы один странный прогон не задавал тренд.
Используйте простую базовую таблицу с одной пользовательской метрикой и одной метрикой сборки. Например: LCP и INP для страницы, плюс общий размер JavaScript и общий объём изображений для сборки. Так бюджеты становятся реальными, потому что видно, что приложение действительно отправило, а не только что предположил лабораторный тест.
Задавайте бюджеты чуть лучше текущего состояния, а не фантастические числа. Принято улучшать на 5–10 процентов от текущей медианы по каждой важной метрике. Если ваш LCP по базовой настройке 3.2 с, не прыгайте сразу до 2.0 с. Начните с 3.0 с, затем ужесточайте, когда сможете удерживать результат.
Добавьте быструю проверку в каждый релиз до того, как увидят пользователи. Держите её достаточно быстрой, чтобы люди не пропускали. Простая версия: выполните одностраничный аудит по согласованной странице, блокируйте сборку если JS или изображения превышают бюджет, сохраняйте результаты по коммитам чтобы видеть, когда что изменилось, и всегда тестируйте по тому же шаблону URL (без случайных данных).
Проверяйте нарушения еженедельно, а не только когда кто‑то пожаловался. Обращайтесь с нарушением как с багом: найдите изменение, которое его вызвало, решите, что исправить сейчас, а что запланировать. Ужесточайте постепенно, только после нескольких релизов удержания линии.
Когда меняется продуктовая область, обновляйте бюджеты осознанно. Если вы добавили новый аналитический инструмент или тяжёлую фичу, запишите что выросло (размер, запросы, время выполнения), что вы сделаете, чтобы компенсировать позже, и когда бюджет должен вернуться.
Бюджет полезен только если его можно быстро проверить. Цель 10‑минутного аудита — не доказать идеальное число, а заметить, что изменилось с последней рабочей сборки и решить, что починить в первую очередь.
Начните с одной страницы, представляющей реальную работу. Выполняйте одни и те же быстрые проверки каждый раз:
Два представления обычно дают ответы за считанные минуты: сетевой waterfall и таймлайн основного потока.
В waterfall ищите запрос, который доминирует в критическом пути: гигантский скрипт, блокирующий шрифт или изображение, которое начинает загружаться поздно. Если ресурс LCP не запрошен рано, страница не сможет уложиться в LCP‑бюджет, как бы быстро ни работал сервер.
В таймлайне ищите длинные задачи (50 мс и более). Кластер длинных задач в старте часто означает слишком много JavaScript на первом экране. Один большой кусок обычно — проблема маршрутизации или общий бандл, который вырос со временем.
Быстрые аудиты терпят неудачу, когда каждый прогон разный. Фиксируйте несколько базовых вещей, чтобы изменения были очевидны: URL страницы и версия сборки, тестовое устройство и профиль сети, описание LCP‑элемента, ключевые числа, которые вы отслеживаете (например LCP, общий JS‑вес, количество запросов), и короткая запись о главном нарушителе.
Десктоп‑тестирование подходит для быстрого фидбэка и проверок PR. Используйте реальное устройство, когда вы близки к бюджету, когда страница кажется «лагучей» или когда у вас в основном мобильная аудитория. Мобильные CPU делают длинные задачи заметными, и именно там многие «на моём ноутбуке выглядит нормально» релизы разваливаются.
Когда бюджет провален, худшее — «оптимизировать всё сразу». Используйте повторяемый порядок триажа, чтобы каждая правка имела явный эффект.
Начните с того, что пользователи замечают сильнее всего, затем переходите к более тонкой настройке:
Команда выпустила новый дашборд и внезапно не попадает в LCP‑бюджет. Вместо того чтобы сначала править заголовки кеширования, они находят LCP‑элемент — полноширинное изображение графика. Они уменьшают размер, отдают лёгкий формат и загружают только то, что нужно в начале. Далее они замечают, что большая библиотека графиков загружается на каждом маршруте. Они грузят её только на странице аналитики и откладывают сторонний виджет поддержки до первого взаимодействия. В течение дня дашборд снова в бюджете, и следующий релиз даёт ясные ответы «что изменилось».
Главный провал — воспринимать бюджеты как разовый документ. Бюджеты работают только если их легко проверять, трудно игнорировать и они привязаны к тому, как вы доставляете сборки.
Большинство команд попадают в несколько ловушек:
Обычная модель — «маленькая» фича подтягивает новую библиотеку. Бандл растёт, LCP замедляется на секунду в медленных сетях, и никто не замечает до появления тикетов в поддержку. Бюджеты призваны сделать такое изменение видимым на этапе ревью.
Начните просто и держите проверки стабильными. Выберите 2–4 бюджета, которые соответствуют пользовательскому опыту, и ужесточайте их постепенно. Зафиксируйте вашу тестовую настройку и запишите её. Отслеживайте хотя бы один реальный пользовательский сигнал, если можно, и используйте лабораторные тесты для объяснения «почему», а не для выигрыша в спорах. Когда зависимость добавляет существенный вес, требуйте короткой записи: сколько стоит, что она заменяет и почему стоит того. И главное — включите проверку бюджета в обычный путь релиза.
Если бюджеты кажутся постоянным препятствием, обычно это значит, что они нереалистичны для текущего состояния или не связаны с реальными решениями. Сначала исправьте эти два момента.
Маленькая команда за неделю выпустила React‑дашборд аналитики. Сначала он казался быстрым, но каждая пятничная сборка делала его чуть тяжелее. Через месяц пользователи начали жаловаться, что первый экран «подвисает», а фильтры работают медленно.
Они перестали спорить о «достаточно быстро» и записали бюджеты, связанные с тем, что замечают пользователи:
Первое проваливание показало две вещи. Начальный JS‑бандл вырос из‑за графиков, библиотек дат и UI‑кита. Одновременно header‑изображение дашборда заменили на большее «временно», что вывело LCP за предел. INP ухудшился, потому что каждое изменение фильтра вызывало тяжёлые повторные рендеры и дорогие вычисления в основном потоке.
Они исправили в порядке, дающем быстрые выигрыши и предотвращающем повторные регрессии:
Вернуть LCP под линию: изменить размер и сжать изображения, задать явные размеры изображений и избегать блокирующих загрузок шрифтов.
Обрезать начальный JS: убрать неиспользуемые библиотеки, разделить некритичные маршруты и лениво загружать графики.
Улучшить INP: мемоизировать тяжёлые компоненты, дебаунсить ввод в фильтрах и выносить тяжёлую работу с горячего пути.
Добавить проверку бюджета в каждый релиз, чтобы при нарушении релиз ждал.
Через два релиза LCP упал с 3.4 с до 2.3 с, а INP улучшился с ≈350 мс до менее 180 мс на том же тестовом устройстве.
Бюджет полезен только если люди следуют ему одинаково каждый раз. Держите его маленьким, запишите и встроите в процесс доставки.
Выберите несколько метрик под ваше приложение, задайте пороги «warn vs fail» и документируйте точный способ тестирования (устройство, браузер, сеть, страница/путь). Сохраните базовый отчёт от текущего лучшего релиза и явно его пометьте. Решите, что считается допустимым исключением и что — нет.
Перед каждым релизом выполняйте тот же аудит и сравнивайте с базой. Если что‑то регрессирует, логируйте это там, где вы ведёте баги, и обращайтесь с этим как с поломанным шагом оформления, а не как с задачей «потом». Если вы выпускаете с исключением — назначьте владельца и срок (обычно 1–2 спринта). Если исключение постоянно продлевается — бюджету нужен серьёзный разговор.
Перенесите бюджеты на этап планирования и оценки: «Этот экран добавляет библиотеку графиков, так что нам нужно убрать что‑то другое или лениво её загружать». Если вы строите с Koder.ai (Koder.ai, koder.ai), вы также можете прописать эти ограничения в Planning Mode, затем работать малыми итерациями и использовать снимки и откаты, когда изменение выводит приложение за пределы. Суть не в инструменте, а в привычке: каждая новая фича должна заплатить за свой вес или не попадёт в релиз.
A performance budget is a set of hard limits (time, size, requests, CPU work) that your team agrees to before building.
If a change exceeds the limit, treat it like a broken requirement: fix it, reduce scope, or explicitly approve an exception with an owner and an expiry date.
Because performance gets worse gradually. Each feature adds JavaScript, CSS, images, fonts, API calls, and third‑party tags.
Budgets stop the slow creep by forcing a tradeoff: if you add weight or work, you must pay it back (lazy-load, split a route, simplify UI, remove a dependency).
Pick one real user journey and one consistent test setup.
A good starter is something frequent and business-critical, like:
Avoid edge cases at first; you want a flow you can measure every release.
Start with one target that matches typical users, for example:
Write it down and keep it stable. If you change device, network, cache state, or the path you test, your trend becomes noise.
Use a small set that covers both what users feel and what teams can control:
A practical starter set is:
Use two thresholds:
This avoids constant fire drills while still making the limits real when you cross the line.
Do this in order:
Not always. Bundle size can be fine while the page still feels slow because the main thread is busy.
Common React causes:
Add a runtime budget (for example, limit long tasks during startup or set a hydration time cap) to catch this class of issues.
Fast generation and iteration can quietly add dependencies, UI flourishes, and third-party scripts that ship to everyone.
The fix is to make budgets part of the workflow:
This keeps fast iteration from turning into a slow product.
Timing metrics show the pain; size and runtime limits help you quickly find what caused it.
Pick 3–5 budgets first. Tune later based on your baseline and release history.
Treat the breach like a bug: identify the commit, fix or scope-reduce, and prevent repeats.