Эта статья знакомит с принципом «Быстро потерпеть неудачу!». Что это? Когда мы должны его использовать? Как это помогает нам писать лучший код?

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

  • Подход Игнорировать!: ошибка игнорируется, и приложение продолжает выполнение.
  • Подход Fail fast!: приложение немедленно останавливается и сообщает об ошибке.
  • Подход Fail safe!: приложение распознает ошибку и продолжает выполнение наилучшим образом.

Какой подход лучший?

Какой из них вы должны подать в своем приложении?

Прежде чем ответить на этот жизненно важный вопрос, давайте сначала рассмотрим простой пример.

Предположим, нам нужно написать (элементарное) веб-приложение, которое отображает предупреждающее сообщение рядом с фонтаном, чтобы предупредить людей о загрязнении воды.

Следующий HTML-код выполняет свою работу:

<html>
   <body>
      <h2 style="color:red;">Important!</h2>
      <p>Please <b>DO NOT</b> drink this water!</p>
   </body>
</html>

Результат, отображаемый в браузере, выглядит так:

Теперь давайте вставим небольшую ошибку. Вместо </b> мы пишем <b> после DO NOT, как показано ниже:

<p>Please <b>DO NOT<b> drink this water!</p>

Возникают два интересных вопроса:

  • Что должно произойти?
  • Что будет?

На второй вопрос легко ответить. Нам просто нужно наполнить наш браузер ошибочным кодом HTML. Это результат, отображаемый в Chrome, Edge, Firefox, Internet Explorer и Safari на момент написания:

Прежде чем читать дальше, спросите себя: «Какой подход был применен браузерами?» …

Очевидно, что подход Fail fast! не применялся, потому что приложение продолжило работу и не сообщило об ошибке. Единственное отличие, которое следует отметить, заключается в том, что больше текста теперь отображается жирным шрифтом. Но сообщение в целом по-прежнему отображается правильно, и люди предупреждаются. Не о чем беспокоиться!

Попробуем еще одну ошибку. Вместо <b> мы пишем <b перед DO NOT, как показано ниже:

<p>Please <b DO NOT</b> drink this water!</p>

Это результат - опять же, как показано в браузерах, упомянутых ранее:

Паника! Теперь программа делает прямо противоположное тому, что она должна делать. Последствия ужасны. Наше спасательное приложение превратилось в приложение-убийцу (но не в такое приложение-убийцу, которое мы все мечтаем когда-нибудь написать).

Важно осознавать тот факт, что приведенный выше пример - не просто теоретический, преувеличенный пример. Существует множество реальных случаев, когда маленькие ошибки имели катастрофические последствия, например, космический корабль Маринер-1, взорвавшийся вскоре после старта из-за пропущенного дефиса. Дополнительные примеры см .: Список ошибок программного обеспечения.

Как видно из приведенного выше примера, последствия неприменения подхода Fail fast! сильно различаются и могут варьироваться от совершенно безвредных до чрезвычайно вредных.

Итак, каков правильный ответ на важный вопрос «Что должно произойти?»

Что ж, это зависит от ситуации. Однако есть некоторые общие правила.

Первое правило:

Мы никогда не должны игнорировать! ошибка - если для этого нет действительно веской причины.

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

Помните правило 6 10 заповедей для программистов на C, красноречиво написанное на старом английском языке Гарри Спенсером:

  • «Если объявлено, что функция возвращает код ошибки в случае затруднений, вы должны проверять этот код, да, даже если проверки утраивают размер вашего кода и вызывают боль в ваших печатающих пальцах, потому что если вы ты думаешь, что «со мной этого не может случиться», боги непременно накажут тебя за твое высокомерие ».

Второе правило:

Во время разработки мы должны применять подход Fail fast!.

Обоснование этого правила легко понять:

  • Подход Fail fast! помогает при отладке.
    Как только что-то пойдет не так, приложение останавливается, и сообщение об ошибке помогает обнаружить, диагностировать и исправить ошибку. Таким образом, подход Fail fast! ведет к созданию более надежного программного обеспечения, снижает затраты на разработку и обслуживание и предотвращает сбои и катастрофы, которые в противном случае могли бы появиться в рабочем режиме.
    Даже если ошибка не возникает. привести к серьезному сбою, всегда лучше обнаружить его как можно скорее, потому что затраты на исправление ошибки возрастают экспоненциально со временем, прошедшим в цикле разработки (время компиляции, тестирования, производства).
  • Последствия появления ошибок в режиме разработки, как правило, не вредны.
    Клиент не жалуется, деньги не поступают не на тот счет, и ракеты не взрываются.

Быстрый отказ обычно считается хорошей практикой в ​​разработке программного обеспечения. Вот несколько подтверждающих цитат:

Однако ситуация может кардинально измениться, если приложение работает в производственном режиме. К сожалению, универсального правила не существует. Практика показывает, что по умолчанию лучше также применять подход Fail fast!. Окончательный ущерб, нанесенный приложением, которое игнорирует ошибку и продолжает произвольно, обычно хуже, чем ущерб, вызванный внезапной остановкой приложения. Например, если бухгалтерское приложение внезапно перестает работать, пользователь злится. Но если он молча игнорирует ошибку и продолжает работать, давая неверные результаты (например, несбалансированный баланс), пользователь очень рассердится. «Злой» лучше, чем «очень злой». Следовательно, в этом случае лучше использовать подход Fail fast!.

В нашем предыдущем примере HTML подход Fail fast! также был бы намного лучше. Предположим, что вместо продолжения выполнения браузеры отображают сообщение об ошибке. Тогда разработчики немедленно узнают о проблеме, и код можно будет быстро и легко исправить, не причинив никакого вреда. Но даже если бы ошибочный код попал в производство (по странным причинам), худший сценарий был бы менее ужасным. Отображение «Пожалуйста, выпей эту воду» может быть ужасным. С другой стороны, отсутствие отображения какого-либо сообщения или просто отображение (непонятного) сообщения об ошибке, вероятно, приведет к тому, что очень низкий процент людей осмелится попробовать небольшое количество воды.

На практике иногда приходится внимательно изучать каждый отдельный случай. Это особенно верно, если максимально возможный ущерб высок, например, в медицинских приложениях, приложениях для денежных переводов или приложениях космических захватчиков. Например, очевидно, что применение правила Fail fast! - правильный подход, если ракета не взлетела на Марс. Но как только ракета запустилась, остановить приложение (или, что еще хуже, игнорировать ошибку) уже не вариант. Теперь отказоустойчивый! подход должен применяться, чтобы делать все возможное.

Иногда хороший вариант - быстро выйти из строя, но минимизировать ущерб. Например, если в приложении текстового редактора возникает ошибка времени выполнения, приложение должно сначала автоматически сохранить текущий текст во временном файле, а затем отобразить значимое сообщение для пользователя («Извините, ... но ваш текущий текст сохранен. во временном файле abc.tmp »), при желании отправьте разработчикам отчет об ошибке, а затем остановитесь.

Отсюда третье правило:

В критически важных приложениях необходимо применять подход Fail safe!, чтобы минимизировать ущерб.

Обобщить:

  • В режиме разработки мы всегда должны применять подход Fail fast!.
  • В производственном режиме:
    Обычно мы должны отдавать предпочтение подходу Fail fast! по умолчанию.
    Критические приложения, которые могут привести к серьезным повреждениям в случае сбоя, должны быть настроены с учетом контекста. и поведение, устраняющее ущерб (или, по крайней мере, уменьшающее ущерб). В отказоустойчивых системах необходимо применять подходы отказоустойчивости и адекватного реагирования!.

Ту же идею выражает превосходное Правило ремонта в Искусство программирования Unix, написанное Эриком Стивеном Рэймондом:

  • «Отремонтируйте то, что вы можете, но когда вы должны терпеть неудачу, откажите шумно и как можно скорее».

Примечание. Дополнительная информация и примеры доступны в Википедии в разделах Отказоустойчивый, Отказоустойчивый и Отказоустойчивая компьютерная система.

В любом случае всегда полезно использовать среду разработки, поддерживающую принцип Fail fast!. Например, скомпилированный язык поддерживает правило Fail fast!, потому что компиляторы могут немедленно сообщить о большом количестве ошибок. Вот пример глупой ошибки, которая легко ускользает от человеческого глаза и может привести к «нежелательным сюрпризам», например зависанию системы из-за бесконечного цикла:

var row_index = 1
...
row_indx = row_index + 1

Подобные опечатки (например, написание row_indx вместо row_index) обычны и немедленно обнаруживаются любым достойным компилятором или (что еще лучше) интеллектуальной IDE.

К счастью, существует множество очень эффективных Fail fast! функции, которые могут быть встроены в язык программирования. Все они полагаются на следующее правило:

Желательно, чтобы ошибки выявлялись автоматически во время компиляции или как можно раньше во время выполнения.

Примеры мощных языковых функций Fail fast!: статическая и семантическая типизация, безопасность нуля во время компиляции (отсутствие ошибок нулевого указателя во время выполнения!), Проектирование по контракту, параметры универсального типа, интегрированный модуль тестирование и др.

Даже лучше, чем обнаруживать ошибки на раннем этапе, - не допускать их намеренно. Этого можно достичь, если язык программирования не поддерживает подверженные ошибкам методы программирования, такие как глобальные изменяемые данные, неявное преобразование типов, молчаливо игнорируемые ошибки арифметического переполнения, достоверность (например, "", 0 и null равны false) и т. Д.

Следовательно, мы всегда должны отдавать предпочтение среде программирования (= язык программирования + библиотеки + фреймворки + инструменты), которая поддерживает принцип Fail fast!. Мы будем меньше отлаживать и создавать более надежный и безопасный код за меньшее время.

Кристиан Нойманн
Разработчик программного обеспечения; Создатель PPL