Производительность
Third-party scripts: тихий убийца бюджета
Команда тратит спринт, срезая app bundle с 800 КБ до 350 КБ. Lighthouse улучшается на 12 баллов. Через неделю data-команда добавляет Segment, Hotjar и новый Intercom widget — суммарно 450 КБ. Работа спринта стёрта. Никто не заметил, потому что third-party scripts не в репозитории приложения.
Third-party налог
Большинство production-веб приложений грузят 5-15 third-party scripts per page. Популярные и их примерные размеры:
- Segment analytics: ~150 КБ
- Hotjar recording: ~200 КБ
- Intercom chat widget: ~200 КБ
- Google Tag Manager (с несколькими tags): ~500 КБ+
- Full Optimizely A/B testing: ~400 КБ
- Один плохо оптимизированный ad tag: ~300 КБ
Десять средних скриптов × 100 КБ = 1 МБ third-party JS до твоего кода. Ничего из этого не появляется в bundle analyzer — файлы грузятся с внешних доменов во время выполнения. Ничего из этого нельзя tree-shaken — ты не контролируешь источник. И большинство работает на main thread, блокируя paint и взаимодействие.
Проблема усугубляется: бизнес-команды часто добавляют скрипты через Google Tag Manager без инженерного review. Каждое добавление через tag manager обходит code review полностью.
Паттерн 1: Interaction gating
Грузи скрипт только когда юзер делает действие, требующее его. Chat widget грузится при клике «Help». Analytics SDK грузится после первого значимого взаимодействия. Newsletter modal скрипт грузится когда юзер проскроллил до 80% страницы.
<!-- Defer до клика -->
<button id="chat-btn">Chat with us</button>
<script>
document.getElementById('chat-btn').addEventListener('click', () => {
const script = document.createElement('script');
script.src = 'https://widget.intercom.io/widget/APP_ID';
document.body.appendChild(script);
});
</script>Tradeoff: первое взаимодействие, триггерящее загрузку, имеет видимую задержку пока chunk скачивается и парсится. Смягчи через prefetch когда trigger-элемент становится видимым (IntersectionObserver на кнопке) или на mouseenter.
Паттерн 2: Web Worker изоляция через PartyTown
PartyTown — open-source библиотека (Builder.io), запускающая third-party скрипты в Web Worker через service-worker прокси. DOM-доступ из Worker bridged через synchronous postMessage. Main thread освобождается от analytics, ad и tracking скриптов полностью.
<script>
partytown = { forward: ['dataLayer.push', 'fbq', 'gtag'] };
</script>
<script src="/~partytown/partytown.js"></script>
<script type="text/partytown" src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXX"></script>Tradeoff: скрипты, зависящие от синхронного DOM-доступа, несовместимы (редко для analytics). postMessage round-trips добавляют ~5-15 мс per call. Некоторые скрипты ломаются при проксировании. Тестируй каждого vendor перед деплоем.
Паттерн 3: Server-side measurement
Перенеси analytics events на сервер. Вместо того чтобы шить 150 КБ Segment-клиент каждому юзеру, firing Segment events из твоего API-сервера на нужных действиях. Юзеры получают tracking; браузеры не получают ничего.
Похожие варианты: Google Analytics Measurement Protocol, Sentry server-side error capture для SSR ошибок, кастомный event pipeline через лёгкий beacon endpoint.
Tradeoff: теряешь browser-only контекст — user agent details, real-time client errors, click heatmaps. Подходит для core funnel analytics; комбинируй с лёгким client SDK для browser-специфических сигналов.
| Паттерн | Main-thread стоимость | Tradeoff |
|---|---|---|
| Interaction gating | Ноль до триггера | First-use latency; смягчить prefetch |
| PartyTown Web Worker | Почти ноль (проксировано в Worker) | DOM-sync несовместимые скрипты; postMessage overhead |
| Server-side measurement | Ноль (ничего на клиенте) | Теряется browser-специфический контекст |
Governance: отдельный third-party бюджет
Senior-команды ведут отдельный third-party бюджет наряду с app bundle бюджетом. Конкретно: файл third-party-budget.json в репо, перечисляющий каждый разрешённый third-party домен, его approved размер и команду-owner. Любой новый скрипт через GTM или <script src>, которого нет в файле, fail’ит CI.
Дополнительный enforcement: CSP (Content Security Policy) заголовок script-src перечисляет разрешённые origins. Скрипты с не перечисленных origins блокируются браузером. Ежеквартальный аудит просматривает каждую запись, удаляет unused vendors и проверяет track records.
Почему это работает
Почему third-party scripts непропорционально вредят? (1) Vendor’ы ставят features выше байтов — chat SDK поддерживает каждый use case, даже если app использует 5%. (2) Нет tree shaking — нельзя убрать unused vendor код. (3) Кумулятивный эффект — 10 скриптов по 100 КБ = 1 МБ до твоего кода. (4) Main-thread блокировка — большинство запускается синхронно при загрузке страницы если явно не деferred.
Маркетинг хочет 200 КБ chat widget на каждой странице и говорит: должен грузиться немедленно. Senior-компромисс?
Команда запускает пять analytics скриптов через GTM суммарно 600 КБ. Они мигрируют funnel events на server-side Measurement Protocol. Какой ожидаемый клиентский эффект?
Почему CSP заголовок script-src релевантен для third-party budget governance?
- 01Почему third-party scripts непропорционально раздувают bundle-стоимость по сравнению с first-party кодом?
- 02Опиши паттерн interaction gating и его tradeoff.
Third-party scripts регулярно добавляют 500 КБ-1 МБ на каждую страницу, вне app репо и вне code review. Три структурных countermeasure: interaction gating (грузить при триггере, ноль initial-стоимости), PartyTown Web Worker изоляция (main thread освобождён), server-side measurement (ничего на клиенте). Governance через third-party budget file и CSP enforce’ит allow-list в CI и на уровне браузера. Следующий урок рассматривает CI enforcement: как сделать бюджет жёстким constraint, а не руководством.
встречается в159
- Путь запроса: семь остановок от сокета до ответаjunior
- Accept и парсинг: от очереди ядра до типизированного запросаmiddle
- Маршрутизация и middleware: что выполняется и в каком порядкеmiddle
- Обработчик и ответ: от бизнес-логики до байтов на проводеmiddle
- Стриминг и backpressure: когда клиент читает медленнее, чем вы пишетеsenior
- Таймауты и хвостовая задержка: бюджеты, дедлайны и ловушка fan-outsenior
- Middleware и DI: два паттерна, формирующие любой backendjunior
- Пишем middleware: сигнатуры, next() и три модели фреймворковmiddle
- Инверсия управления: как зависимости добираются до классаmiddle
- Скоупы и время жизни DI: singleton, request, transientmiddle
- DI как шов для тестов: фейки, моки и граница, которая важнаsenior
- DI-контейнеры в продакшене: графы разрешения, циклы и когда не стоитsenior
- Блокирующий vs неблокирующий I/O: два способа ждатьjunior
- Event loop: один поток, упорядоченные фазыmiddle
- Что блокирует цикл: CPU-работа и синхронные вызовыmiddle
- Вынос CPU-работы: worker threads и пул libuvmiddle
- Backpressure и ограниченная конкурентностьsenior
- Пропускная способность под нагрузкой: хвостовая задержка и насыщениеsenior
- Зачем пул: цена создания соединенияjunior
- Размер пула: почему больше не значит быстрееmiddle
- Взятие и таймауты: очередь ожидания — настоящий дроссель задержкиmiddle
- Стратегии retry: backoff, jitter и thundering herdmiddle
- Наблюдаемость, production-инциденты и дизайн для глобального масштабаsenior
- Задачи, микрозадачи и scheduler.yield()middle
- Точность таймеров, троттлинг и фоновая работаmiddle
- Event loop Node.js: фазы, nextTick и задержка циклаsenior
- Стратегии рендеринга: SSG, SSR, ISR, streaming и гидратацияjunior
- SSG, SSR, ISR, streaming и RSC — как работает каждая стратегияmiddle
- Цена гидратации: selective, progressive, острова, resumabilitymiddle
- Core Web Vitals: что измеряют LCP, INP и CLSjunior
- LCP: четыре фазы, одна доминирующая стоимостьmiddle
- INP: input delay, processing, presentationmiddle
- Lab vs field: почему они расходятся и как использовать каждыйmiddle
- Трейдоффы метрик, RUM-атрибуция и цикл CI+полеsenior
- Общая картина: от URL до LCP до INP как эстафетаjunior
- Восемь слоёв трассировки: от service worker до второй навигацииmiddle
- Пять канонических поломок: где производство стабильно ломаетсяsenior
- Метод трёх треков: чтение трасс и построение системы мониторингаsenior
- Что такое индекс и как он ускоряет запросыjunior
- Leading-column rule: почему порядок столбцов в composite-индексе важенmiddle
- Partial, expression и covering-индексыmiddle
- Типы индексов: GIN, GiST, BRIN, Hash, Bloom и HOT-обновленияmiddle
- Index-only scan, Visibility Map и INCLUDEsenior
- Типичные сбои в продакшне и аудит индексовsenior
- Упражнение по проектированию индексов: стратегия полнотекстового поискаsenior
- EXPLAIN и планы выполнения: что решает планировщик и почемуjunior
- Типы сканирования: Seq, Index, Bitmap, Index-Onlymiddle
- Алгоритмы соединения и каскад ошибок оценки строкmiddle
- pg_statistic, ANALYZE и производственная наблюдаемостьmiddle
- Расширенная статистика: исправление ошибок оценки для коррелированных колонокsenior
- Кеш планов, настройка константных стоимостей и внутренности планировщикаsenior
- Производственные режимы отказа и стабильность плановsenior
- Connection pool: зачем амортизировать стоимость backend Postgresjunior
- Режимы PgBouncer: session, transaction и statementmiddle
- Размер пула: формула (ядра × 2) + шпинделей и двухуровневый стекmiddle
- Исчерпание пула и idle-in-transaction: сценарий отказа в 3 ночиmiddle
- Миграция на transaction mode: план развёртывания и prepared statements в PgBouncer 1.21middle
- Процессная модель Postgres и почему увеличение max_connections снижает производительностьsenior
- Ландшафт пулеров 2026, serverless connection storms и полная таксономия отказовsenior
- ADD COLUMN: мгновенно в PG 11+ против перезаписи в старом Postgresjunior
- Режим отказа очереди блокировок: почему мгновенный DDL может заморозить базуmiddle
- Безопасные DDL-паттерны: NOT VALID, CONCURRENTLY и исправления небезопасных операцийmiddle
- Таксономия сбоев миграций и дисциплина продакшнаsenior
- Выбор ключа шарда: стратегии hash, range, list и directorymiddle
- Ко-локация и Citus: инвариант, делающий шардирование пригодным к использованиюmiddle
- Режим отказа hot shard: обнаружение, изоляция и долгосрочная политикаmiddle
- Онлайн-решардинг, 2PC и операционная стоимость шардированияsenior
- Семь актов: от CREATE TABLE до Citusjunior
- Акты 1–3 в глубину: схема, индексы и статистика планировщикаmiddle
- Акты 4–6 в глубину: MVCC bloat, connection pooling и безопасные миграцииmiddle
- Акт 7 в глубину: шардинг, co-location и семиуровневый каскад трейдоффовmiddle
- Наблюдаемость, антипаттерны и производственный триажsenior
- Биты в проводеjunior
- Математика задержкиmiddle
- Bufferbloat и перегрузкаsenior
- Граница физического уровняsenior
- Номера последовательности и состояние соединенияmiddle
- Управление потоком и перегрузкойmiddle
- BBR, производственная наблюдаемость и за пределами TCPsenior
- CDN: контент по соседствуjunior
- Anycast и GeoDNS: маршрутизация к ближайшему edgemiddle
- Многоуровневый кеш и Cache-Controlmiddle
- Заголовок Vary и cache keysmiddle
- Stale-while-revalidate и cache stampedesenior
- Edge workers и edge-side compositionsenior
- CDN: операции и observabilitysenior
- WebSocket: HTTP-апгрейд до постоянного соединенияjunior
- WebSocket vs SSE vs long-polling: выбор правильного транспортаmiddle
- Backpressure в WebSocket: когда клиенты не успеваютmiddle
- Реконнект: jittered backoff, thundering herd, восстановление сообщенийsenior
- WebSocket в масштабе: HTTP/2 мультиплексирование, permessage-deflate, C10Msenior
- WebSocket в production: прокси, безопасность и распределённая архитектураsenior
- Что делают обратные проксиjunior
- Алгоритмы балансировки: от round-robin до power-of-two-choicesmiddle
- L4 vs L7 балансировка и сохранение IP клиентаmiddle
- Health checks, connection draining и slow startmiddle
- Retry-бури, circuit breakers и load sheddingsenior
- Устойчивая архитектура LB: anycast, zone-aware маршрутизация и observabilitysenior
- Почему QUIC, а не TCP+TLSjunior
- QUIC-потоки и head-of-line blockingjunior
- Объединённое рукопожатие и 1-RTTmiddle
- Connection ID и миграция сетиmiddle
- Обнаружение потерь и управление перегрузкойmiddle
- Возобновление 0-RTT и шифрование пакетовsenior
- Развёртывание и стоимость CPUsenior
- DDoS: что это и почему работаетjunior
- Атаки усиления и истощение состоянияmiddle
- Ограничение скорости: алгоритмы и архитектураmiddle
- WAF, межсетевые экраны, mTLS и HSTSmiddle
- Отравление DNS-кэша и BGP-перехватsenior
- Эшелонированная защита и экономика атакsenior
- Двенадцать слоёв: один URL, семь действующих лицjunior
- DNS, TCP, TLS по очереди: куда уходят миллисекундыmiddle
- Критический путь рендеринга и Core Web Vitalsmiddle
- Перехват прокси и шлюзы безопасности: rate limiter, WAF, mTLSmiddle
- Альтернативные пути: QUIC 0-RTT, WebSocket upgrade, миграция соединенияmiddle
- Наблюдаемость: распределённые трейсы, USE/RED и семплированиеsenior
- Устойчивость: каскадные повторы, circuit breakers и error budgetsenior
- Что такое три сигнала: метрики, логи, трейсыjunior
- Метрики и cardinality: cost-модель time-series databasemiddle
- Логи и объём: cost-модель структурного логированияmiddle
- Трейсы и сэмплирование: cost-модель distributed tracingmiddle
- Join-ключи и exemplar''''ы: как три сигнала становятся компонуемымиmiddle
- Observability 2.0: широкие события и сдвиг стоимостиsenior
- Режимы сбоя и инженерная практика: cardinality budget''''ы, PII и сэмплированиеsenior
- Зачем нужны структурные логи: дневник против таблицыjunior
- Схема продакшн-лога: поля, которые несёт каждая строкаmiddle
- Log levels и маршрутизация алертовmiddle
- Стратегии sampling и стоимость логовmiddle
- PII-редакция и log injectionsenior
- Propagation trace-контекста в логахsenior
- OTel Logs Data Model и audit-логи как подсистемаsenior
- Сигналы OTel, Semantic Conventions и проводной формат OTLPmiddle
- Авто-инструментирование и ручные спаны: правило 80/20 в OTelmiddle
- Collector OTel: receivers, processors, exporters и паттерны развёртыванияmiddle
- Стратегии сэмплирования: head, tail и parent-basedmiddle
- Vendor-нейтральность, eBPF-инструментирование, Operator и OTel в браузере и serverlesssenior
- Эксплуатация OTel Collector: надёжность, version skew, режимы отказа и управлениеsenior
- RED и USE: два чек-листа, одна дисциплина триажаjunior
- Инструментация RED в Prometheus: счётчики, гистограммы и дисциплина cardinalitymiddle
- USE на Linux: CPU, память, диск, сеть и PSImiddle
- Golden signals, структура дашборда и auto-RED в service meshmiddle
- Cardinality как драйвер затрат: label, PII, exemplars и семплированиеmiddle
- Native histograms, SLO и паттерны production-сбоевmiddle
- Выбор SLI и SLO-целей: отношения, не ощущенияmiddle
- Multi-window multi-burn-rate-алертинг: почему AND лучше ORmiddle
- Error budget policy, latency SLO и составные journeysmiddle
- Iceberg SLI, математика составного SLO и SLA vs SLOsenior
- Flame graph: читаем картинку, которая показывает, куда ушло времяjunior
- Sampling vs instrumentation profiling: почему 99 Гц побеждает в productionmiddle
- Типы профилей: CPU, память, off-CPU, mutex — какой когда братьmiddle
- Continuous profiling: always-on flame graphs с eBPF и корреляцией trace-idmiddle
- Как flame graph строится из сэмплов и как использовать его в productionmiddle
- Linux perf, внутренности eBPF, PGO и ограничения sampling''''аsenior
- Profiling в production: безопасность, war stories, OTel profiles и дизайн инфраструктурыsenior
- Debugging-воронка: SLO → RED → trace → profilejunior
- Архитектура OTel: один SDK, четыре сигнала, один wire-форматmiddle
- Экономия на observability: удерживаем затраты в пределах 5% inframiddle
- Масштаб, безопасность и ROI наблюдаемых системsenior