Контракт ошибок
Как часто мы совершаем ошибки? Постоянно! Что мы делаем в случае неудачи? Стремимся её исправить, а если не получилось, решаем, как рассказать об этом заинтересованным лицам. Подбирая нужные слова, оцениваем размер ущерба и возможную реакцию на возникшую ситуацию. Вместе с этим исходный контекст может быть обобщён, изменён или расширен.

Допустим, вы так и не смогли выполнить задачу по интеграции с другой системой из-за недостатка информации. Но руководителю вместо голого факта говорите что-то в стиле: “Я запросил документацию у владельцев системы, они сегодня дадут ответ, и я уточню сроки реализации.”
В такой форме мы передаём:
- Контекст – “задача не выполнена”
- Текущее состояние – “запросил документацию, жду ответ”
- Масштаб последствий – “сроки реализации затягиваются”
- Дальнейшие действия – “сегодня уточню сроки”
И это не вежливость, это управление ожиданиями, которое постоянно используется в быту. А в программных системах это называется контрактом ошибок.
Но несмотря на то, что программы ошибаются чаще людей, контракту ошибок уделяется не так много внимания. Возможно, из-за того, что в момент реализации API не было чёткой структуры действий; а возможно, из-за излишней сосредоточенности только на функциональности.
При проектировании API внимание должно быть уделено трём составляющим:
- Набор операций
- Модель данных
- Контракт ошибок
Когда в приоритете функциональность, неудивительно, что во главе угла только набор операций и данные. Последствия таких недальновидных действий сказываются позже – на этапе эксплуатации, когда по реакции системы трудно судить о том, что с ней происходит.
К счастью, проектируя современные API, многие стали уделять больше внимания ошибкам и их мониторингу. Однако всё это делается без должного внимания к деталям, а те, что имеются, часто не несут ценности.
Контракт ошибок есть, мониторинг есть, но оба бесполезны.
Почему так происходит? Всё как в обычной жизни: не подумали о реакции пользователя на ошибки.
Если собираетесь сообщить об ошибке, подумайте, что с ней будет делать пользователь вашего API, у которого гораздо меньше контекстной информации, чем у вас сейчас. Как именно вы хотите повлиять на его поведение?
За каждой ошибкой в любом API должна быть предусмотрена какая-то история по её анализу и обработке. Если такой истории нет, это признак плохого дизайна. Проверить полезность ошибки очень легко:
Может и должен ли пользователь API действовать как-то иначе, чем просто try-catch-log или try-catch-retry?
Если может – отлично, API определяет поведение пользователя; если нет – ошибка ничем не отличается от безликой Internal Server Error. Последнее может быть сигналом, что вам не нужна излишняя детализация в ответе. И это нормально, если всё, что вы ожидаете – это log или retry.
Излишняя детализация ошибочных ситуаций – это ещё одна ловушка, в которую можно угодить, руководствуясь добрыми намерениями. И чаще всего в эту ловушку попадают аналитики и тестировщики, демонстрируя, что они предусмотрели все возможные варианты использования.
Рассмотрим конкретный пример. Предположим, нужно отменить заявку записи на приём ко врачу. Предполагается, что клиент передает только ID заявки. Операция может завершиться неудачей только по двум причинам: передали несуществующий ID или заявка уже отменена. Для каждого такого случая можно было бы предусмотреть свою ошибку. Но будет ли отличаться поведение пользователя в первом и втором случае? Нет, и более того, в обеих случаях не предполагается retry.
А что, если вероятность передачи несуществующего ID крайне низкая, конкурентность записи высокая, а пользователь работает с API по принципу fire-and-forget? Возможно, что в такой ситуации и ошибку возвращать бессмысленно.
Ошибки – это неотъемлемая часть проектирования контракта взаимодействия, поэтому в центре дизайна должно стоять поведение, а не попытки рассказать, что именно пошло не так.
Понравилась статья?
Посмею напомнить, что у меня есть Telegram-канал Архитектоника в ИТ, где я публикую материал на похожие темы примерно раз в неделю. Подписчики меня мотивируют, но ещё больше мотивируют живые дискуссии, ведь именно в них рождается истина. Поэтому подписывайтесь на канал и будем оставаться на связи! ;-)
Статьи из той же категории: