EN Прислать проект
Все статьи

Обработка ошибок: почему приложение ломается молча

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

· 5 минОсновыКод

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

В коде, написанном с ИИ, этот ответ часто отсутствует. Ассистент пишет то, что просили, — счастливый путь, где всё получается. Получается приложение, которое на демо работает, а у живого человека иногда просто ничего не делает. Кнопка нажата, спиннер крутится вечно, в базе пусто, и никто, включая тебя, об этом не знает.

Почему приложение ломается молча

Три частые причины тишины:

  • Ошибку поймали и выбросили. Пустой catch {} или catch (e) { console.log(e) } в браузере. Формально ошибка обработана, на деле — спрятана.
  • Ошибку не ждали вовсе. Запрос к API без проверки ответа: сервер вернул 500, а код читает данные так, будто всё в порядке, и показывает пустой список.
  • Ошибка случилась там, где никто не смотрит. Фоновая задача, вебхук, функция по расписанию. Упала — и всё, следов нет.

Первое, что стоит сделать в таком проекте, — поиск по коду: catch, .catch(, except. Каждое место, где ошибка молча проглатывается, — кандидат на исправление.

Два адресата: человек и лог

У каждой ошибки два читателя, и им нужно разное.

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

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

Хорошая практика — одно и то же событие отправить в оба места: короткое понятное сообщение человеку и подробную запись в лог, связанные общим идентификатором. Тогда на письмо в поддержку «у меня ошибка, код A7F3» можно найти конкретную запись.

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

На стороне браузера задача — не оставлять человека в неизвестности.

  • У каждого запроса три состояния: загрузка, успех и ошибка. Если в компоненте есть только первые два, третье случится всё равно — просто без интерфейса.
  • Проверяй ответ сервера. fetch не считает ответ 404 или 500 ошибкой: нужно смотреть на response.ok самостоятельно.
  • Не теряй введённое. Если отправка формы не удалась, данные должны остаться в полях. Заставить человека заново заполнять длинную форму — надёжный способ его потерять.
  • Ставь общую страховку. В React это граница ошибок — компонент, который вместо белого экрана показывает «что-то пошло не так» с кнопкой «обновить».
  • Отправляй ошибки браузера к себе. Без сервиса сбора ошибок ты не узнаешь, что у пользователей с определённым браузером всё падает.

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

На сервере задача — не врать о результате и не терять информацию.

  • Правильные коды ответа. Ошибка ввода — 400, нет доступа — 403, не найдено — 404, сломалось у нас — 500. Ответ 200 с текстом «ошибка» внутри ломает всю логику на клиенте.
  • Проверка на входе. Чем раньше отбракованы плохие данные, тем меньше странных ошибок глубже.
  • Внешние сервисы падают. Ставь таймауты на запросы к ним и решай заранее, что будет при отказе: повторить, поставить в очередь или честно вернуть ошибку.
  • Не делай половину операции. Если надо списать баланс и создать заказ, это одна транзакция: либо оба шага, либо ни одного.

Минимальный шаблон для обработчика выглядит примерно так:

try {
  const order = await createOrder(input);
  return res.status(201).json(order);
} catch (err) {
  const id = crypto.randomUUID();
  logger.error({ id, userId: user.id, err }, "createOrder failed");
  return res.status(500).json({ error: "Не удалось оформить заказ", id });
}

Что не надо делать

  • Показывать человеку текст исключения как есть.
  • Ловить все ошибки подряд и продолжать, как будто ничего не случилось.
  • Бесконечно повторять запрос, который падает каждый раз, — особенно к платному API.
  • Писать в лог всё тело запроса целиком, вместе с паролем.

Коротко

  • Обработка ошибок — заранее решённый ответ на «что, если не получилось».
  • Самая опасная ошибка — проглоченная: пустой catch хуже упавшего приложения.
  • Человеку — понятное сообщение и следующий шаг, логу — подробности без секретов, между ними — общий идентификатор.
  • На фронтенде у каждого запроса есть состояние ошибки, а введённые данные не теряются.
  • На бэкенде — честные коды ответа, таймауты на внешние сервисы и транзакции.

Если в проекте десятки пустых catch, SUBXCAT может разобрать их и настроить сбор ошибок, чтобы молчаливые поломки стали видимыми.

Хочешь, сделаем это за тебя?

Расскажи о проекте ассистенту — через пять минут будет примерная смета, через 48 часов разбор.

Созвон с командой 20 минут: инженер, дизайнер и маркетолог посмотрят на проект вместе с тобой.
Записаться на звонок