Главная

Обрабатывайте ошибки правильно.

Пишите отказоустойчивый код.

Поделиться:
 

13+

Программисты пишут программы, которые работают. Хорошие программисты пишут программы, которые работают практически в любых условиях.

Представьте, что вы потратили уйму времени на заполнение онлайн-формы на каком-то правительственном сайте, нажали кнопку “Далее” и тут браузер выдал ошибку “Соединение с сервером прервано”. Представьте, что вы регистрируетесь на рейс, используя терминал для самостоятельной регистрации, и, наконец, выполнив квест по использованию его интерфейса, видите перед собой надпись: «Извините, произошла ошибка». Представьте, что вы делаете денежный перевод с помощью мобильного приложения, а потом заходите в лифт, где нет сигнала и приложение зависает.

Плохая обработка ошибок может вызвать раздражение, разочарование и даже создать проблемы, на решение которых могут уйти часы. И это еще не все. Плохая обработка ошибок делает продукт незавершенным, сырым и неряшливым. Это подрывает доверие пользователей. Всегда нужно задавать себе вопрос: “А что может пойти не так?”. Нужно продумывать каждую ситуацию.

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

Идея № 0. Стиль кода

Разработка программного обеспечения — это комплексная и сложная вещь: даже простая веб-страница, которая отображает некоторую информацию из базы данных, использует технологии, разработанные тысячами людей в течение нескольких десятилетий.

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

  1. Вы тратите много времени на выяснение того, что делает тот или иной участок кода. А если же стиль кода одинаков, то вы понимаете, что MAX_DELAY в Java или kMaxDelay в C++, это некая константа.
  2. Если вы захотите сделать какое-либо изменение, это может иметь непредсказуемые последствия.

Именно по причине вышеописанного каждая уважающая себя компания имеет руководство по написанию кода. Преимущество стиля кода состоит в его стандартизации и унификации. «Руководство по стилю написания кода» Uber гласит:

“Вся суть стандарта заключается в том, чтобы избегать суеты. В Интернете много споров о табуляции против пробелов и т.д. Эти споры никогда не будут разрешены. Споры только отвлекают от выполнения реальных проблем.”

Весьма полезно разработать рекомендации и руководства по обработке ошибок и придерживаться их. Но руководства и рекомендации это только начало… Есть еще ряд принципов, которым нужно следовать:
Стандарт не должен вызывать трудностей: программисту должны быть предоставлены библиотеки со всеми служебными классами, а также вспомогательные инструменты для таких тривиальных вещей, как отступы и именование переменных.
Отсутствие возможности нарушения стандарта: должны существовать как технические (предварительные проверки, которые не допускают отправку в продакшн при наличии стилевых ошибок), так и организационные механизмы для обеспечения стиля.
Это два основополагающих принципа, которым надо следовать для того, чтобы поддерживать хороший стиль кода.

Идея № 1. Используйте как исключения, так и статус коды.

Рано или поздно случится такая ситуация, что вам надо будет возвращать либо что-то, либо код ошибки, которая, в свою очередь, не является исключением. Простейший пример — предоставленный пользователем JSON-файл. Мы не должны воспринимать пользовательский файл JSON в качестве исключения. Да, действительно, в этой ситуации можно создать два метода: метод «проверки» и метод «обработки». Это неплохое решение, но оно не особо практично, так как нужно создавать два метода вместо одного, а также класс данных для этого JSON. Короче говоря — дополнительная и ненужная работа.
Некоторые языки имеют встроенную поддержку данного шаблона (например, несколько возвращаемых значений в Scala Try или Go), а в остальных его следует создавать вручную: Google StatusOr.

Идея № 2. Используйте исключения только в исключительных случаях.

Если вы используете оба инструмента (статус и исключения), нужно определить четкие правила, когда использовать статус, а когда исключение. Я, в свою очередь, предлагаю вам такое правило: “использовать исключение тогда и только тогда, когда ничего уже нельзя сделать и процесс должен быть полностью прерван”.

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

В предыдущей статье я говорил, что прежде, чем обрабатывать ошибку, программист должен ответить на следующие вопросы:

Я настоятельно рекомендую вам использовать стандартные ошибки потому, что с пользовательскими ошибками могут возникнуть дополнительные проблемы:
❶ Ваши кастомные классы обработки ошибок будут находиться в utils или exceptions? Или, может, в errors? В текущем проекте или в какой-то общей библиотеке? Как будет называться общая библиотека: <имя-проекта> -common, common, или, может быть common error? Эти вопросы могут сбить вас с толку и запутать.
❷ Работа с аналогичными или даже полностью эквивалентными статусами или исключениями. В этом модуле мы используем mapper.errors.NotFoundError, но эта библиотека возвращает geo.statuses.NotFoundErrorStatus. Таким образом, мы, вероятно, должны проверить и преобразовать последнее в первое.

Вышеописанные проблемы полностью устраняются, если использовать набор стандартных ошибок/исключений. Можете использовать набор от Google. Отсутствие файла? Возвращаем NOT_FOUND. Предоставлены неверные данные — INVALID_ARGUMENT. Запрос на вывод подан до прибытия денег — FAILED_PRECONDITION. Эти 15 участков кода должны обрабатывать 90% случаев. В оставшихся 10% случаев нам придется создавать собственные коды ошибок/исключений.

Идея № 4. Относитесь к пользовательской ошибке так же, как к пользовательскому загрузчику классов.

Создать исключение очень легко — новый класс, наследуемый от Exception или std::exception и все, готово. Означает ли это, что кастомные исключения должны быть повсюду? Нет!

Исключения или коды ошибок должны обрабатываться аналогично пользовательским загрузчикам классов или аллокаторам памяти или пользовательским типам коллекций. Вы не будете создавать CustomerArrayList, поэтому не создавайте CustomerNotFoundException. Ошибки — часть инфраструктуры, а не бизнес-логики.

Все вышеописанные принципы должны помочь избежать искусственной сложности и сосредоточиться на действительно важных вещах, а не на разгадке загадок и деталей реализации.
Да, скорее всего, не все эти принципы будут работать в вашем случае. Идеального решения нет. Все условно и субъективно.
Вопрос обработки ошибок непосредственно связан с техническими деталями, и вам выбирать, что лучше всего подходит для вашего проекта и кодовой базы. Также огромную роль играют организационные вопросы: какой способ обработки ошибок будет наиболее удобен для вашей команды? В общем, подходите к вопросу обработки ошибок с умом. Надеюсь статья была для вас полезной. Спасибо!

Источник

13+