Говори, когда больно: как ошибки становятся частью UX
Когда в продукте появляется ошибка, пользователь не думает о том, что "не прошёл валидацию payload" или "сервер вернул 422". Он думает одно: "Что-то пошло не так. И, скорее всего, это я криворукий. А вообще это сервис кривой."
И тут есть развилка: либо мы оставляем его наедине с этим ощущением (читай — он уходит и не возвращается), либо мы объясняем, что произошло, что делать и почему это вообще случилось.
Ошибки — это не побочный продукт разработки. Это часть интерфейса, как кнопки, поля и текст. Только в отличие от кнопок, ошибки проявляют себя в худший момент. И именно в этот момент они показывают, насколько вы вообще думаете о пользователе. Сегодня прольем цифровой чай на вопрос о котором очень мало кто думает.
Почему ошибки — это важный UX-инструмент
В отчёте NN Group за 2024 год говорится: "Poor error messages are one of the top 3 causes of user frustration and task abandonment in digital products."
Само собой лучше когда ошибки не появляются на пути пользователя и все идет гладко. Но если ошибки имеют место быть, то ошибки оформленные правильно, дают продукту:
- Прозрачность — объясняют, что происходит и кто виноват.
- Контроль — возвращают ощущение, что "ситуация под контролем".
- Обратную связь — помогают быстрее достичь цели (даже если пользователь ошибся).
- Рост конверсии — в форме, где ошибки объяснены, пользователей меньше уходит.
- Меньше поддержки — когда всё понятно, писать в саппорт нет смысла.
Где ошибки живут и кто за них отвечает
В любой системе существуют два типа ошибок валидации:
1. Frontend-валидация
Это ошибки, которые происходят на клиенте — до отправки данных на сервер. Данный тип ошибок “зашивается“ в компонент. Они обеспечивают быстрый отклик и предотвращают очевидные ошибки:
- Пустое поле
- Неверный формат email
- Превышение допустимой длины и т.д.
2. Backend-валидация
Это ошибки, которые возвращает сервер после проверки данных. Данный тип ошибок косвенно связан с компонентом, но поставляется в него в виде переменной. Они учитывают бизнес-логику, доступы, связи между сущностями и пр.:
- Email уже зарегистрирован
- Пользователь не имеет права загружать файл
- Неверный код верификации
- Недопустимая комбинация данных
Есть еще третий тип, но я не буду намеренно его выделять в список. Ошибка сети/системы — пользователь без интернета, истёк токен, не отвечает база.
И все эти ошибки в итоге вываливаются пользователю. Не команде. Не QA. Не разработчику. А реальному человеку, который пытается оплатить, зарегистрироваться, нажать “отправить” и, вообще-то, хочет просто пользоваться продуктом.
Почему нужна систематизация
Ошибки — это как официанты в ресторане: не все их замечают, пока всё идёт хорошо, но стоит что-то пойти не так — и ты сразу понимаешь, насколько здесь вообще умеют работать с людьми.
Теперь представим типичный цифровой ресторан, то есть наш продукт. Пользователь заходит, хочет «заказать» — допустим, зарегистрироваться или оплатить. А ему в ответ: «Что-то пошло не так» «422: Validation failed» «Invalid email» «Такой email уже существует»
Каждое сообщение — как будто из другой эпохи, команды и вселенной. Одно на английском, другое на русском, третье — вообще из лога сервера, случайно вывалившегося в UI. Если бы продукт был человеком, он бы разговаривал голосами трёх незнакомцев с разными акцентами и уровнем вежливости.
Вот почему нужна систематизация. Потому что ошибки — это тоже интерфейс. И если они выглядят как мусорная свалка из случайных сообщений, пользователь будет чувствовать себя на этой самой свалке. Неуверенность, раздражение, ощущение, что никто не контролирует ситуацию — и ты уже не регистрируешься, не платишь, не продолжаешь.
Имхо, систематизация ошибок — это способ сказать: “Да, у нас тоже бывает фигово. Но мы хотя бы умеем об этом нормально поговорить.”
Когда у продукта есть чёткая система: категории ошибок, ключи сообщений, единый стиль текста, предсказуемое отображение — всё становится проще. Ошибки перестают быть катастрофой. Они становятся инструкцией: что случилось, где именно, и как это исправить. И главное — всё это написано языком, который отражает личность продукта, а не автогенератор документации из 2005 года.
Так что да: ошибки — это лицо продукта. Просто не в момент, когда всё хорошо, а в момент, когда правда важнее красоты. И если у тебя это лицо кривое, злое и говорит на латыни — подумай, что пользователь запомнит в итоге.
Что такое tone of voice в ошибках
Tone of voice (TOV) — это характер продукта. Он должен быть во всём: в подписях, в кнопках, и как ты понимаешь логично продолжать линию общения с клиентом в ошибках.
Плохой TOV:
- “Required field”
- “Minimum length 6 characters”
Хороший TOV:
- “Пожалуйста, заполните поле”
- “Пароль должен быть от 6 символов — без пробелов”
Вообще говоря, тут поле для экспериментов с tone of voice становится только шире. Представь формулировки ошибок заточенные под молодежный сленг или узкую специализацию профессиональную. Пользователь кайфанет не только от нормальной работы продукта, но и от того, как он решает сложные ситуации. Пользователь запомнит и полюбит тебя.
Как это делают в нормальных системах
Немного тошноты в студию. Думаю про формулировки и проработки ошибок уже все поняли. Сейчас о том, как делают одни из лучших:
Shopify Polaris: каждая ошибка — это key, например, errors.email.invalid; компонент InlineError сам достаёт локализованный текст.
Atlassian: ошибки приходят с кодом и мапятся в flag или inline message. Компонент сам решает, как показать.
Salesforce: маппинг сообщений идёт через специальную структуру JSON, по модели errors.models.USER.EMAIL.TAKEN.
Уже достаточно инфы, что бы немного погуглить, если не слышали о таком. Вот вам еще один пунктик на разобраться. Этакий Zod.
Проблема с Zod: он не знает, что у вас tone of voice
Для начала, Zod — это современная TypeScript-библиотека для описания схем валидации данных. Она позволяет декларативно описывать, какие данные ожидаются от пользователя (или от API), и автоматически проверяет их на соответствие. По сути, это как JSON Schema, только без боли, и с типами «из коробки».
Используется в React, Next.js, Node и везде, где хочется держать логику валидации рядом с кодом, а не на стороне сервера. Это то что распакует вам разраб и скажет у меня все готово про ошибки. По факту и да и нет.
Работает быстро, пишется просто. Но вот беда:
- По умолчанию ошибки у него машинные. Типа: “Ошибка поля”. Ты серьезно? Какое поле, о чем ты, почему?
- Если не задать сообщение вручную — будет выводить дефолт.
- Эти дефолты не локализуются, не стандартизируются, не брендируются.
Что делать (и как дизайнеру не превратиться в жертву Zod'а)
Хорошие ошибки не появляются сами. Увы, ни Zod, ни бэкенд, ни разработчики не обязаны думать о тоне, стиле и контексте. Они просто отдают то, что есть. А если продукт начинает говорить с пользователем как оператор терминала DOS, виноват будет не разработчик. А вы — дизайнер. Потому что интерфейс — это ваша территория.
Так что давайте разберёмся, что именно нужно сделать, чтобы ошибки выглядели как часть интерфейса, а не как сбой в Матрице. Погнали, записываем:
1. Создайте библиотеку ошибок с человеческим лицом
Начните с простого документа — например, в Figma, Notion или в виде таблицы. В нём:
- Придумайте ключи ошибок: validation.required, auth.emailTaken, network.timeout.
- Для каждого ключа напишите понятное, дружелюбное сообщение. Желательно в стиле продукта, а не Google Translate: "Invalid input" → "Пожалуйста, заполните поле", "Too short" → "Минимум 6 символов — без пробелов"
- Добавьте текст для перевода, если планируете локализацию.
Это будет вашим tone-of-voice-словарём ошибок — основа для всего, что происходит дальше.
2. Определите, где и как должны появляться ошибки
Одна и та же ошибка может выглядеть по-разному в зависимости от контекста. Пример:
- Поле не заполнено → inline-сообщение под полем.
- Не удалось оплатить → toast или баннер сверху.
- Сбой сервера → модальное окно с кнопкой "Повторить".
Дизайнер должен задать правила отображения ошибок:
- Какие компоненты для чего используются?
- Где они появляются?
- Как они выглядят: цвет, иконка, отступы, анимация.
Пропишите это в гайдлайне дизайн-системы. Компоненты ошибок — такие же важные кирпичи интерфейса, как поля и кнопки.
3. Работайте с разработчиками, а не вместо них
Моя любимая фраза: двигаем стул к разработчику. Обсудите с фронт-командой:
- Можно ли централизованно подключить тексты ошибок?
- Как связать код ошибки и ключ сообщения?
- Что делать, если сообщение приходит с бэка, но его нужно показать по-человечески?
Например, пусть фронт-форма не показывает "422: Validation failed", а обращается к ключу validation.required и берёт текст из вашей таблицы. Вы даёте стиль — разработчик даёт реализацию.
4. Прототипируйте ошибки как часть интерфейса
В макетах, wireframes и user flow всегда закладывайте состояния с ошибками. Покажите, как выглядит форма при:
- Пустом поле
- Некорректном вводе
- Системной ошибке
Если ошибок нет в макетах — их выдумает фронт. И чаще всего — не в лучшую сторону.
5. Создайте визуальный реестр ошибок
Ну и наконец, хороший ход — собрать все виды ошибок в одном месте:
- Как выглядят inline-ошибки под полем
- Как выглядит toast с ошибкой оплаты
- Какой стиль у баннеров и модалок
- Что видит пользователь при ошибке 500
Это можно сделать в Figma как страницу "Error system" или в Storybook. Тогда каждый, кто делает интерфейс, будет знать, как правильно “ломаться”. И да, тогда туда придут и возьмут, что-то что готово и проверено, а не как обычно на коленке по быстрому прикинуто лишь бы задачу закрыть.
Ошибки — ваша зона ответственности
Дизайнер не обязан писать регулярки и думать, что там у Zod внутри. Но дизайнер обязан сделать так, чтобы продукт не выглядел как терминал техподдержки. А для этого нужны:
- Таблица ошибок с ключами и текстами.
- Визуальные паттерны отображения.
- Гайд по компонентам ошибок.
- Совместная работа с фронтом.
Если вы это сделаете — ошибки перестанут быть “отвалами” и начнут работать на продукт. Потому что в момент, когда всё идёт не так — именно ошибка показывает, насколько вы вообще умеете общаться с пользователями.
Как всегда, добра 🦫