Обработка ошибок в продакшене

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

Обработка ошибок в Inferno охватывает несколько уровней:

  • ошибки рендеринга компонентов;
  • ошибки в обработчиках событий;
  • ошибки асинхронных операций;
  • ошибки интеграции с внешними сервисами;
  • глобальные ошибки времени выполнения.

Каждый уровень требует отдельного подхода.


Ошибки рендеринга и жизненного цикла компонентов

Inferno использует синхронный рендеринг, близкий по модели к React, но с меньшим количеством абстракций. Ошибки, возникающие в render или методах жизненного цикла (componentWillMount, componentDidMount, componentWillUpdate и т.д.), приводят к прерыванию обновления виртуального DOM.

Пример типичной ошибки рендеринга:

render() {
  return <div>{this.props.user.name}</div>;
}

Если user равен null, приложение аварийно завершит рендер текущего дерева.

Стратегии защиты

Явная валидация данных

render() {
  const { user } = this.props;
  if (!user) {
    return <div class="placeholder" />;
  }
  return <div>{user.name}</div>;
}

Декомпозиция компонентов Мелкие компоненты с чёткими контрактами уменьшают вероятность распространения ошибки вверх по дереву.


Error Boundary в Inferno

Inferno поддерживает механизм error boundary, аналогичный React, но реализованный проще. Для этого используется метод componentDidCatch.

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error) {
    this.setState({ hasError: true });
    logError(error);
  }

  render() {
    if (this.state.hasError) {
      return <div class="error">Ошибка загрузки интерфейса</div>;
    }
    return this.props.children;
  }
}

Такой компонент перехватывает ошибки:

  • в render дочерних компонентов;
  • в их жизненном цикле.

Важно: ошибки в обработчиках событий не перехватываются error boundary и требуют отдельной обработки.


Ошибки в обработчиках событий

Inferno не оборачивает события в try/catch автоматически. Ошибка внутри обработчика приводит к выбросу исключения в глобальный контекст.

onClick() {
  throw new Error('Ошибка клика');
}

Рекомендованный паттерн

onCl ick = () => {
  try {
    this.doSomething();
  } catch (err) {
    reportEventError(err);
  }
};

Для сложных сценариев используется обёртка-утилита:

const safeHandler = fn => event => {
  try {
    fn(event);
  } catch (e) {
    logError(e);
  }
};

Асинхронные ошибки и Promise-цепочки

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

async componentDidMount() {
  try {
    const data = await api.load();
    this.setState({ data });
  } catch (e) {
    this.setState({ error: true });
    reportNetworkError(e);
  }
}

Антипаттерн

componentDidMount() {
  api.load().then(data => {
    this.setState({ data });
  });
}

Ошибка в api.load() или внутри then приведёт к необработанному отклонению промиса.


Глобальные обработчики ошибок

В продакшене обязательно используется глобальный перехват ошибок JavaScript.

window.oner ror = function(message, source, line, column, error) {
  sendToMonitoring(error || message);
};

Для промисов:

window.addEventListener('unhandledrejection', event => {
  sendToMonitoring(event.reason);
});

Эти механизмы:

  • не заменяют локальную обработку;
  • служат последней линией защиты;
  • критически важны для сбора статистики.

Логирование и мониторинг

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

Типичная архитектура:

  • локальный логгер (консоль, буфер);
  • транспорт (HTTP, Beacon API);
  • сервер агрегации (Sentry, Rollbar, собственный сервис).
function logError(error, context = {}) {
  fetch('/log', {
    method: 'POST',
    body: JSON.stringify({
      message: error.message,
      stack: error.stack,
      context
    })
  });
}

Контекст должен включать:

  • версию приложения;
  • маршрут;
  • состояние компонента;
  • данные окружения.

Поведение интерфейса при ошибках

Корректная обработка ошибки — это не только перехват исключения, но и правильная реакция интерфейса.

Основные подходы:

  • Fallback UI — упрощённый интерфейс вместо сломанного;
  • Частичное восстановление — рендер только рабочей части дерева;
  • Изоляция ошибок — error boundary на уровне виджетов, а не всего приложения.
<Layout>
  <Sidebar />
  <ErrorBoundary>
    <Dashboard />
  </ErrorBoundary>
</Layout>

Ошибка в Dashboard не влияет на Sidebar и общую навигацию.


Ошибки состояния и неконсистентные данные

Ошибки не всегда проявляются как исключения. Некорректное состояние может привести к логическим сбоям без падения приложения.

Примеры:

  • setState после размонтирования компонента;
  • гонки асинхронных запросов;
  • устаревшие данные в замыканиях.

Защита:

componentWillUnmount() {
  this._unmounted = true;
}

async load() {
  const data = await api.load();
  if (!this._unmounted) {
    this.setState({ data });
  }
}

Различие dev и production сборок

В продакшене:

  • стек вызовов может быть минимизирован;
  • сообщения об ошибках теряют информативность;
  • tree-shaking может удалить защитный код.

Поэтому:

  • обработка ошибок не должна зависеть от process.env.NODE_ENV;
  • логирование должно быть независимым от консоли;
  • проверка данных должна быть явной.

Архитектурные принципы устойчивости

Ключевые принципы:

  • Fail fast на уровне бизнес-логики;
  • Fail safe на уровне UI;
  • Изоляция ошибок по границам ответственности;
  • Наблюдаемость как обязательное свойство системы.

Inferno предоставляет минималистичную основу, и именно это делает грамотную обработку ошибок критически важной частью архитектуры продакшен-приложений.