Gernar
Frontend DeveloperПроизводительность

Интервью-вопрос

Что такое оптимизация

Оптимизация это не набор трюков, а измеримое улучшение пользовательского сценария. В ответе важно показать диагностику, trade-off и проверку результата.

Добавлен
Редакция

Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.

🐰0
🥚0

Мини-квиз

Проверка перед разбором

Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.

Вопрос 1 из 50 правильно

Как лучше начать ответ на вопрос про оптимизацию?

Вы хотите показать системный подход, а не список случайных приемов.

Варианты ответа

Разбор

Разобраться, а не зазубрить

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

Базовая идея

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

Сразу добавьте важное ограничение. Оптимизация без измерений часто превращается в угадывание. Можно долго мемоизировать компоненты, хотя пользователь ждет из-за большого изображения, тяжелого шрифта, медленного API или стороннего скрипта.

Короткая формулировка для ответа:

Для меня оптимизация это не просто сделать быстрее. Это найти узкое место в конкретном сценарии, изменить его и подтвердить результат метриками. Во фронтенде это может быть загрузка, рендеринг, сеть, кеширование, изображения или работа main thread.

Как выбирать, что оптимизировать

Сильный ответ показывает порядок действий. Вы не просто знаете lazy loading, debounce и useMemo. Вы понимаете, когда каждый прием уместен и какую проблему он решает.

Как выбрать направление оптимизации

1Есть жалоба или просадка метрики?
Сформулируйте сценарий и измерьте текущее состояние.
2Проблема в первом экране?
Проверьте LCP, размер JS, CSS, изображения, шрифты и серверный ответ.
3Интерфейс тормозит после загрузки?
Смотрите long tasks, тяжелые вычисления, лишние рендеры и обработчики событий.
4Много сетевых запросов или повторов?
Проверьте кеш, дедупликацию, debounce, pagination и отмену устаревших запросов.
5Изменение внесено?
Повторите замер и убедитесь, что UX стал лучше, а код не стал опаснее.

Такой порядок защищает от типичной ошибки. Если вы сразу говорите "я применяю React.memo", интервьюер легко спросит: "А как вы поняли, что проблема была в рендерах?" Если вы сначала говорите про замеры, ответ выглядит надежнее.

Примеры оптимизаций во фронтенде

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

import { lazy, Suspense } from "react";

const ChartPanel = lazy(() => import("./ChartPanel"));

export function Dashboard() {
  return (
    <Suspense
      fallback={
        <div role="status" aria-live="polite">
          Загружаем график...
        </div>
      }
    >
      <ChartPanel />
    </Suspense>
  );
}

Это может уменьшить начальный JavaScript. Но есть trade-off: если график нужен сразу после первого клика, пользователь может ждать уже внутри интерфейса. Тогда стоит подумать о prefetch, skeleton и приоритете загрузки. Для доступности у fallback должно быть понятное состояние загрузки. Иначе пользователь скринридера может не понять, почему часть интерфейса пропала.

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

Плохой подход:

const ProductCard = React.memo(function ProductCard({ product, onClick }) {
  return <button onClick={() => onClick(product.id)}>{product.title}</button>;
});

Сам по себе React.memo может не помочь, если родитель на каждый рендер создает новый объект product или новую функцию onClick. Без профилирования вы только усложните код. Сначала найдите источник лишних обновлений, а мемоизацию применяйте точечно.

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

useEffect(() => {
  if (!query) {
    setResults([]);
    setStatus("idle");
    return;
  }

  const controller = new AbortController();
  setStatus("loading");

  const timerId = window.setTimeout(async () => {
    try {
      const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`, {
        signal: controller.signal,
      });

      if (!response.ok) {
        throw new Error("Search request failed");
      }

      const data = await response.json();
      setResults(data);
      setStatus("success");
    } catch (error) {
      if (error instanceof DOMException && error.name === "AbortError") return;
      setStatus("error");
    }
  }, 300);

  return () => {
    window.clearTimeout(timerId);
    controller.abort();
  };
}, [query]);

Здесь debounce снижает количество запросов, а AbortController помогает не обрабатывать устаревший запрос. Проверка response.ok и отдельные состояния loading, error и empty state защищают UX после сбоя. Без cleanup можно получить race condition, лишнюю нагрузку на API, обновление размонтированного компонента и неверные данные в интерфейсе.

Как звучит сильный процесс

Надежный подход
  1. 1Выбрать пользовательский сценарий
  2. 2Снять базовые метрики
  3. 3Найти узкое место
  4. 4Внести одно изменение
  5. 5Сравнить результат до и после
Опасный подход
  1. 1Оптимизировать на глаз
  2. 2Добавить мемоизацию везде
  3. 3Не проверить слабые устройства
  4. 4Не измерить эффект
  5. 5Оставить сложный код без пользы

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

Например, на странице каталога мы увидели, что первый экран медленно появляется на мобильных устройствах. В Performance panel и по RUM-данным было видно, что мешают большой JS-чанк и тяжелое изображение. Мы вынесли часть фильтров в lazy loading, оптимизировали изображение для LCP и проверили метрики после релиза. В итоге сценарий стал быстрее, а код остался понятным.

Даже без точных цифр можно сказать честно: "Я бы сравнил LCP, INP, размер бандла и количество запросов до и после". Это лучше, чем обещать абстрактное ускорение.

Практический вывод

На вопрос "что такое оптимизация" отвечайте в три шага. Сначала дайте определение через измеримое улучшение. Затем назовите основные направления во фронтенде: загрузка, рендеринг, сеть, кеш, изображения, main thread. В конце обязательно скажите про проверку результата и цену решения.

Хорошая финальная мысль:

Я стараюсь оптимизировать не ради самого приема, а ради пользовательского сценария. Если изменение не улучшило метрику или UX, а только усложнило код, это плохая оптимизация.

Частые ошибки

Где обычно ошибаются

Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.

  1. 1

    Оптимизировать без измерений

    Так легко потратить время на место, которое не влияет на пользователя. Например, вы уменьшили мелкое вычисление, а основная задержка была в большом бандле или медленном API. Сначала найдите узкое место через Lighthouse, Performance panel, профайлер React или продакшен-метрики.
  2. 2

    Ставить мемоизацию везде

    React.memo, useMemo и useCallback не бесплатны. Они добавляют сравнения, зависимости и риск stale values. Используйте их там, где есть дорогой рендер, стабильные входные данные и измеримый выигрыш.
  3. 3

    Считать локальный Lighthouse полным доказательством

    Lighthouse полезен, но один прогон на вашем компьютере не равен реальному опыту пользователей. В продакшене влияют сеть, устройство, регион, кеш, A/B-тесты и сторонние скрипты. Лучше сочетать lab-тесты с RUM-метриками и мониторингом.
  4. 4

    Ускорять загрузку ценой поломанного UX

    Агрессивный lazy loading может дать хороший первый экран, но пользователь увидит задержку при открытии важного блока. Если часть интерфейса нужна сразу после первого действия, продумайте prefetch, skeleton и приоритет загрузки.
  5. 5

    Кешировать данные без стратегии инвалидирования

    Кеш снижает запросы, но может показать устаревшие цены, статусы или права доступа. Для уверенного ответа упомяните TTL, ручную инвалидизацию, revalidation и обработку ошибок обновления.

Follow-up

Что могут спросить дальше

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

Живые ответы

Видео с похожим вопросом

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

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

Содержание