Параноидальное защитное программирование

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

Enterprise Craft перечисляет несколько ключевых понятий Защитного программирования

  1. Проверка предварительных условий перед эксплуатацией
  2. Проверить наличие нулей
  3. Утверждайте состояние всякий раз, когда вы его меняете

Это отличная практика, но я утверждаю, что кое-чего не хватает.

  1. Защитное программирование фокусируется на том, что происходит внутри вашего приложения, и тратит меньше времени на беспокойство о том, что может происходить вне вашего кода.
  2. Защитное программирование предполагает, что ваша программа будет единственной вещью, изменяющей состояние.
  3. Защитное программирование работает лучше всего, когда API или SDK, с которыми вы работаете, предоставляют хорошие исключения и ошибки.

По этим причинам я начал использовать параноидальное защитное программирование в некоторых сценариях. Параноидальное защитное программирование добавляет к приведенному выше списку несколько концепций.

  1. Утверждение состояния до, во время и после внесения изменений
  2. Подробно запишите все результаты

Для достижения этих целей мы следуем нескольким рекомендациям

  1. Извлеките функцию проверки состояния, потому что вам нужно будет многократно проверять состояние.
  2. Результаты состояния журнала, которые могут быть безобидными в «информации»
  3. Результаты состояния журнала, которые означают, что в «ошибке» есть ошибка
  4. Результаты состояния журнала, которые неоднозначны при «предупреждении»

Давайте рассмотрим пример. Допустим, у нас есть функция resetUserPassword(), которая сбрасывает пароль пользователя на случайное значение, и другая функция getLastUserPasswordReset(), которая возвращает время последнего сброса пароля пользователя.

Наша цель — сбросить пароль пользователя, если он не изменился более 30 дней. Если мы знаем, что resetUserPassword() будет надежно генерировать исключения или возвращать ошибки, мы можем просто выполнять защитное программирование, как обычно.

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

Наш код параноидального защитного программирования должен выглядеть примерно так

original_reset_time = getLastUserPasswordReset("myUser")
log("Last reset time was: " + original_reset_time)
if(original_reset_time < date().days - 30) {
  log("Password is old, resetting")
  resetUserPassword("myUser")
  new_reset_time = getLastUserPasswordReset("myUser")
  if(new_reset_time < original_reset_time) {
    log("password reset correctly")
  } else {
    log("password failed reset!")
  }
} else {
  log("password is new, moving on")
}

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

Когда параноидальное защитное программирование является хорошей идеей?

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

Когда параноидальное защитное программирование не подходит?

  • Когда у вас есть надежный API, который возвращает постоянные ошибки
  • Когда у вас есть возможность сделать ваши исходные зависимости более надежными
  • Когда у вас есть другие фреймворки, которые будут обрабатывать идемпотентность за вас