Падаем с изяществом: руководство по культуре ошибок для тестировщика |
04.08.2025 00:00 |
Зачем тратить время на продумывание сообщений об ошибках?Перед моей последней поездкой я попытался зарегистрироваться онлайн. Пользовательский путь, который проходил через утомительную серию полей формы, внезапно завершился кратким сообщением об ошибке: HTTP_400_BAD_REQUEST. Для любого тестировщика обнаружение ошибки — это заманчивый опыт. В этом конкретном случае мне было бы неплохо обойтись без неё, но у меня было больше причин, чем обычно, оценить, как была обработана ошибка. В целом пользователи более довольны сервисом, который корректно реагирует на сбои, а не сервисом, который, кажется, никогда не выходит из строя. Это называется парадоксом восстановления сервиса, и он возникает потому, что пользователь ясно видит, что при возникновении сбоев система отвечает соответствующим образом. Это похоже на эффект IKEA, при котором пользователи более довольны продуктом или процессом, если они вложили в них собственные усилия, чем если усилий было мало или вовсе не было. Внезапное сообщение об ошибке, с которым я столкнулся в процессе онлайн-регистрации, побудило меня написать о том, почему усилия по обеспечению качественных сообщений об ошибках более оправданы, чем попытки полностью устранить ошибки. Особенно это важно на ранних этапах разработки, когда грамотная обработка ошибок оказывает наибольшее влияние. О чем на самом деле говорит сообщение об ошибке На первый взгляд сообщение об ошибке типа HTTP_400_BAD_REQUEST кажется абсолютно неудобоваримым для пользователя. Однако оно скрывает «секретное послание», которое я смог расшифровать благодаря опыту на предыдущей должности веб-разработчика: «Не повторяй попытку.» Протокол HTTP определяет коды ошибок в блоке 400, включая HTTP_400_BAD_REQUEST и NOT AUTHORIZED (403), как вызванные действиями, которые продолжат приводить к ошибкам. Это контрастирует с ошибками из блока 500, такими как TIMEOUT (504) – в этом случае можно попробовать еще раз. Раньше, не зная этого, я часами терпел неудачи, повторяя попытки, каждая из которых завершалась сообщением «Попробуйте позже.» На самом деле никакие попытки повторного запроса не имели смысла. Сколько бы времени я сэкономил, если бы разработчики сразу сказали мне правду о коде 400: «Не повторяй попытку.» Хорошая культура ошибок: сначала исправляйте сообщения, потом — саму ошибкуОчевидно, что код ошибки 400 будет понятен лишь немногим. При отсутствии адекватного понимания ошибок ошибка из блока 400 может даже оказаться некорректной. По моему опыту, владельцы продуктов слишком быстро сосредотачиваются на устранении предполагаемых дефектов кода, упуская из виду вредные долгосрочные последствия плохих сообщений об ошибках. В этой статье я хочу выступить за культуру ошибок, в которой сообщения об ошибках получают не меньше внимания, чем сами дефекты продукта, и вот почему:
Я осмелюсь утверждать, что программное обеспечение высочайшего качества проявляется именно в случае неожиданного сбоя. Тогда видно разницу между разработчиками, которые предвидят непредвиденное, и теми, кто просто пишет функции, «тестируемые на данный момент». Распространённые ошибки при обработке ошибокМногие руководства учат программистов правильно обрабатывать ошибки, но статей о том, как выявлять нарушения этих правил в готовом продукте, мало. Иными словами, многие программисты так и не понимают, почему грамотная обработка ошибок важна с точки зрения конечного пользователя. Поэтому я хочу дать вам, как тестировщику, список распространённых ошибок в обработке ошибок и советы, как их обнаружить. Потерянная первопричинаСамая частая ошибка в обработке ошибок — отсутствие отчёта о том, что привело к сбою. Первопричина скрыта, а сообщение об ошибке, которое видит пользователь, является расплывчатым, например, «что-то пошло не так». Сокрытие первопричины происходит не из-за отсутствия усилий по созданию описательного сообщения. Чаще всего более точное и конкретное сообщение просто опускается в пользу общего из-за опасений, что оно может быть слишком техническим для пользователя. Такие методы обработки ошибок могут иметь серьёзные последствия для культуры ошибок в вашей команде. Скрывая описания ошибок из соответствующих подмодулей, разработчики теряют мотивацию писать удобочитаемые сообщения об ошибках. Понимая, что их усилия игнорируются, они не видят своего вклада в опыт пользователя. Этот анти-паттерн в культуре ошибок быстро распространяется, подрывая чувство ответственности внутри команды. Программисты сталкиваются с экономическим компромиссом между тщательным, но затратным по времени подходом и быстрым, но поверхностным способом обработки потенциальных сбоев. Как в случае с потерянной первопричиной, меньше кода обычно даёт лучшие результаты, но для этого требуется дисциплина и последовательность на протяжении всего проекта. Поэтому это выходит за рамки ответственности одного разработчика и становится вопросом качества кода (и продукта). Программисты рассматривают ошибки как объекты с двумя частями: сообщение на понятном языке, описывающее проблему на высоком уровне, и ссылка на предыдущее исключение, которое было зафиксировано как причина текущего сбоя. Вторая часть раскрывает историю всех предыдущих ошибок вместе со ссылками на неработающий сегмент кода. Конечные пользователи, напротив, видят только сообщение на понятном языке без внутренних деталей. Пример кода ниже показывает, как может быть реализована такая обработка ошибок. Если конечные пользователи никогда не увидят результат работы этой функции, код никогда не станет проблемой. Однако проиллюстрированный ниже практический пример «глотает» более детальные причины, которые должны были бы проявиться из подфункций. В лучшем случае пользователи увидят сообщение «Регистрация не удалась», а в худшем — окружающий код может подавить это сообщение в пользу чего-то ещё более размытого. try { К счастью, в моём опыте с онлайн-регистрацией ситуация была не такой. Низкоуровневый код ошибки 400 действительно был напрямую показан конечному пользователю — мне. Хотя сообщение и не обеспечивало «оптимальный» пользовательский опыт, оно показало, что каждый в команде — от фронтенда до бэкенда — мог бы помочь и добавить больше деталей. Вводящее в заблуждение сообщение об ошибкеЧто может быть хуже, чем сообщение об ошибке «Что-то пошло не так»? Вводящее в заблуждение сообщение! Такой тип сообщения легко заставит вас тратить время на попытки исправить проблему, повторяя действия снова и снова. Например, система может побудить вас повторять действие, хотя ошибка неустранима. Она может приказать вам подождать, хотя обработка уже остановилась. Вращающийся индикатор загрузки может имитировать активность, когда ждать уже не нужно. Сообщение об ошибке может ссылаться на какую-то другую причину, которую разработчик имел в виду во время тестирования, не учитывая все возможные сценарии. Такие сообщения — это ложь, возможно, неумышленная, но в лучшем случае — халатность. Вот пример кода с вводящим в заблуждение сообщением об ошибке, который я чересчур часто видел в реальных проектах, за которые люди платят деньги: try { Если вы видите такое вводящее в заблуждение сообщение об ошибке, обязательно зафиксируйте это как баг и подробно объясните, почему это плохо. Часто такие сообщения наносят больше вреда, чем функциональный дефект в коде. Как только конечные пользователи начнут сомневаться в сообщениях, которые выводит ваш продукт, вернуть их доверие будет очень сложно. Я видел случаи, когда пользователи игнорировали любые детали об ошибках даже спустя месяцы после устранения первопричины. Потерянное доверие тяжело вернуть. Сообщать неверные данные хуже, чем не сообщать ничего вовсе. Последствия могут жить намного дольше момента сбоя вашего приложения. Отсутствующее сообщение об ошибкеХотя отсутствие сообщения об ошибке может показаться пользователю худшим вариантом, иногда меньше значит больше. Зачастую разработчики добавляют общие сообщения вроде «попробуйте позже», не задумываясь, действительно ли это полезно. Они пытаются помочь, но на самом деле — нет. Эта привычка часто основана на ошибочных предположениях о пользовательском опыте и о том, что пользователи поймут или не поймут. Пользователи часто понимают больше, чем ожидается, включая коды ошибок. Также они формируют собственные теории о том, как работает программа. Если сообщения об ошибках трудно понять, пользователи могут озадачиться. Но это всё равно лучше, чем быть введёнными в заблуждение. Программисты пользуются сторонними библиотеками, которые порой выдают странные сообщения, например HTTP_400_BAD_REQUEST. Такое сообщение может дойти до конечного пользователя, если ничего прочего не добавлено и ничего не скрыто. Хотя это не идеально, по крайней мере сообщение не будет неточным. Иногда исправить ошибку удаётся быстрее, когда низкоуровневые коды ошибок передаются службе поддержки по горячей линии. Слишком подробное сообщение об ошибкеПредставьте, что продукт, который вы тестируете, не предназначен напрямую для конечных пользователей, а встроен в что-то большее — например, в виде интегрированного виджета или просто в качестве источника данных. Вы можете не знать точно, кто ваши конечные пользователи. Ваш модуль может использоваться технически подкованными хакерами или быть глубоко спрятан внутри другого приложения. Поэтому сообщение об ошибке, которое генерирует код, может казаться релевантным для конечного пользователя, но при этом быть слишком детализированным. Вы не можете быть уверены наверняка. По этой причине идеальное сообщение об ошибке начинается с самого общего описания и постепенно добавляет детали по мере того, как пользователь читает дальше. Это означает, что вы добавляете новую информацию по мере её появления. Пример ниже иллюстрирует эту идею. При условии, что все владельцы модулей прилагают усилия, чтобы сообщать об ошибках понятным для пользователя образом, итоговое сообщение может выглядеть так: «Регистрация не удалась, потому что виза не могла быть подтверждена из-за отсутствия обязательного идентификатора визы в сервисе». Очевидно, что здесь есть возможность для улучшения, но по крайней мере конечные пользователи будут понимать, где именно произошла ошибка. try { Почему этот фрагмент кода в списке ошибок? Я считаю, он близок к совершенству, но подвох в том, что он не неотъемлемая часть вашего приложения. Как тестировщик, вы, конечно, не можете контролировать, как написан код, но вы можете продвигать идею улучшения сообщений об ошибках. Выводы для тестировщиковХочу поделиться рядом идей, как подходить к обработке различных ошибок с точки зрения обеспечения качества. Рекомендации основаны на моём опыте работы CTO в testup.io. Они могут потребовать первоначальных усилий на этапе тестирования и исправления, но я видел, как это окупается в долгосрочной перспективе. Новые ошибки становятся легче отслеживать, а конечные пользователи чувствуют себя более информированными и терпимыми к временным сбоям. Как, а не почемуКогда ваше приложение падает, естественно, что первой вашей реакцией будет поиск причины ошибки. Конечно, вы хотите понять, почему это произошло, и хотите, чтобы ошибка исчезла. В моменте это кажется самым прямым путём: найти причину — затем исправить. Однако посмотрим на вопрос шире - сама по себе эта ошибка вам не так важна. Для вас важнее, как быстро устраняются проблемы в целом. В долгосрочной перспективе скорость важнее одной конкретной ошибки. В терминах метрик DORA это называется средним временем восстановления (mean-time-to-recovery). Это проверенная метрика, которая сильно коррелирует с успехом проекта в целом. Она нагляднее предсказывает результат, чем количество дефектов в беклоге. Она измеряет, насколько быстро вы находите, объясняете и исправляете ошибки, а не сколько их всего. Чтобы это работало, нужно сфокусироваться на том, как падает код продукта и сколько времени уходит на поиск, описание, воспроизведение и устранение проблемы. В некотором смысле, эта метрика показывает, насколько гладко проходит ваша работа как тестировщика. ВосстанавливаемостьВаше приложение может выйти из строя на стороне конечного пользователя из-за множества причин. Куча ошибок находится вне вашего контроля. Может пропасть интернет-соединение, внешние сервисы могут не работать, или высокая нагрузка приведёт к исчерпанию доступных ресурсов. Поскольку ваше тестовое покрытие никогда не будет идеальным, вы не увидите все возможные ошибки до выхода в продакшен. Прежде чем пытаться исправлять ошибки, которые могут быть временными или даже невозможными для воспроизведения, я рекомендую улучшать общую восстанавливаемость программного обеспечения более универсальным способом:
Перезапуск процесса — неизбежная реальность веб-приложений. Быстрое восстановление может сделать больше для доверия пользователей, чем чуть меньшее количество ошибок. Покрытие тестамиПредположим на минуту, что вы обнаружили проблему до выхода в продакшен. Вы настояли на достаточно внятном сообщении об ошибке, которое позволяет конечным пользователям пройти процесс повторно с минимальными усилиями. Если проект намерен долго жить, то вы уже мостите путь к улучшенному восприятию ошибок и более быстрому восстановлению в будущем. Теперь пора исправлять сам дефект в коде продукта. Как сохранить высокий уровень качества? Вот несколько идей для хорошего покрытия тестами:
Чтобы создать культуру хороших сообщений об ошибках, важно ценить прогресс. Не просто выдавайте количество ошибок команде разработчиков и на этом заканчивайте. Показывайте им, как вы цените усилия по созданию ёмких и понятных сообщений. Даёте положительную обратную связь, если суть ошибки передана качественно. ЗаключениеКачество обработки ошибок часто упускается из виду под давлением времени. При том, что тщательное тестирование и так частая мишень для сокращения затрат, это не удивительно. Однако истинная устойчивость программного обеспечения часто проявляется в том, как оно справляется с ошибками. Удовлетворённость клиентов может даже увеличиваться, когда ошибки возникают, но при этом о них и возможностях для их обхода ясно сообщается. Это соответствует контринтуитивным психологическим концепциям, таким как «парадокс восстановления сервиса» и «эффект IKEA». Влияние плохой культуры обработки ошибок особенно сильно, когда приложения выдают вводящие в заблуждение указания, например, «Попробуйте снова», несмотря на то, что проблема постоянна, или «Пожалуйста, подождите», когда никакой обработки не происходит. Эти недостатки зачастую проистекают из глубинных проблем в инфраструктуре обработки ошибок и затрагивают не только частные случаи. Если такие ошибки продолжают возникать, это может свидетельствовать о необходимости изменений культуры обработки ошибок. Игнорирование описания неожиданных событий часто означает, что проблеме вовсе не уделялось внимания. Вдумчивое предвидение причин ошибок — это предпосылка, а не следствие, надёжного программного обеспечения. Мой совет: сначала исправьте культуру обработки ошибок, а потом — сами ошибки. Дополнительная информация
|