Организованное программирование

Третья часть разбора “Чистого кода” Роберта Мартина.
На этот раз — глава “Обработка ошибок”, где всё снова звучит красиво, но работает не так, как написано.

Разбираю, почему подход “всё через исключения” на практике создаёт больше хаоса, чем порядка. Объясняю, где Мартин путает исключительные ситуации с обычной логикой программы, и как это превращает чистый код в непредсказуемый.

Показываю, почему исключения — не всегда “чисто”, а часто просто удобно спрятанная ошибка. Разбираем примеры с try-catch, коды возврата, идемпотентность и атомарность, говорим о реальных паттернах работы с ошибками в Go, Haskell и TypeScript.
★ Support this podcast ★

What is Организованное программирование?

Пишем код, за который не стыдно. Разбираем базу, даем рекомендации и встречаемся с умными людьми

Привет, друзья. Это подкаст Организованное программирование. Я его ведущий Кирилл Макевнин. Сегодня я один. А мы продолжаем разбирать книжку Чистый код. Сегодня будет видео посвящено главе исключения ошибки. И, в принципе, на этом мы остановимся. Она достаточно небольшая, но там есть о чём поговорить. Соответственно, посмотрим, как оно получится. Поехали. Первое, с чего начинает Мартин - это с Используйте исключение вместо кодов ошибок. И я немного зачитаю, что он здесь рассказывает. В далёком прошлом многие языки программирования поддерживали механизмы обработки исключений. В таких языках возможности обработки и получения информации об ошибках были ограничены. Программа либо устанавливала флаг, либо возвращали код, который проверялся вызывающей стороной. Оба способа продемонстрированы. Дальше он показывает код, который сложно понять без контекста, потому что он зависит от того с чем там конкретно идёт работа, если особенно люди, которые с этим никогда не работали, да, там что-то с устройствами связанное. Но здесь показана общая идея о том, что вот есть возврат. В основном он тут связан с Налом, но не только. Тут проверка ещё статуса есть. Ну и таким образом идёт проверка, проверка, проверка. А и как будто вот получилась лесенка с большим количеством ифов. И дальше, соответственно, идёт описание о том, что вот это плохо, так писать нельзя и надо его, соответственно, менять. И вот исключение - это хорошо. Здесь сразу хочется сказать, что довольно забавно изменилось отношение к этим вещам, потому что когда появился ГО, понятно, что Го не вчера был придуман, то есть не помню, говорили об этом или нет, а это часть системы P 9, которая была довольно давно создана. И поэтому это не то, что что-то новое, как это в Go обрабатывается, но популярно стало именно, когда это превратили в язык именно GO. И, соответственно, там обработка ошибок снова вернулась к формату, что у нас не исключения, а возвраты. И, как ни странно, огромное количество программистов радостно сказало, что класс, наконец-то всё понятно, всё видно и так далее. При этом, конечно, там многие всё равно при этом недовольны и часто показывают какие-то вложенные лесенки из ошибок, но лесенки очень часто можно убрать. То есть это не всегда проблема логики программы именно с точки зрения того, что прямо вот так надо написать, иначе не заработает. Это часто проблема с точки зрения того, как код организует сам программист. Но даже если всё нормально организовано, то да, действительно, проверок будет много. И многих это устраивает, потому что видно всё, что происходит на всех уровнях. Поэтому, когда Мартин говорит о том, что раньше было только так, это было плохо, у нас не было каких-то возможностей, а, ну, это не так, возможности были. Другой вопрос, что отношение действительно с появлением исключения было такое, что вау, смотрите, насколько проще, быстрее, эффективней, меньше. Значит, у нас, ну, эффективнее, это, конечно, я зря, наверное, сказал, но, по крайней мере, код становится более компактным и вроде как бы смотрите, как классно. А на практике, конечно, это не так всё просто, потому что количество ошибок, которое из-за этого возникает в реальном, а, продакшене, в рантайме, оно большое. А большинство исключений мы никаким образом не проверяем, не знаем, что они могут возникнуть. То есть, если мы сами этого не подумали об этом, не написали какую-то обработку, то, как правило, программа падает с ошибкой. И тут помогают тесты, ну, либо есть языки, в которых есть определённые механизмы, которые как-то помогают с этим справляться. Но в целом исключение такой довольно рандомный механизм, который, с одной стороны позволяет писать код компактней и не думать о большом количестве вещей. Ну а цена за это, конечно, возможность в любой момент грохнуться, потому что ой, забыли обработать, не знали, что там вообще какие-то есть исключения. Чем больше взаимодействует, особенно с внешним миром, тем больше, соответственно, этого добра происходит. Вот это если в двух словах. Но теперь опять же, как показывает практика, отношение к тому, что исключение - это хорошо, всё остальное плохо, это не так. А тем более существуют и другие ещё подходы, да, там, а, связанные с Монадо и Авер, когда у вас есть возможность управлять процессом передачи ошибок снаружи. А, тоже классная, интересная концепция. Кто пишет на Хаскеле, естественно, всё это прекрасно знает. Это иногда пытаются тащить и в другие языки, но плохо это обычно заканчивается, потому что тогда как бы появляются две системы, которые начинают между собой миксовать. Но в любом случае говорить про то, что вот это плохо, это не совсем правильно, особенно в современном мире. Если всё-таки посмотреть в код, там не всё является, ну, проблемой, потому что одно дело, когда у вас действительно исключительная ситуация, а другое дело, когда это просто нормальная логика программы. Вот, например, у него здесь в коде есть проверка статуса, да, и вероятность того, что это прямо является исключительной ситуацией, а это ещё бабка надвое сказала. Проблема в том, что очень сложно этот код понять, когда, ну, он без контекста, то есть там какие-то девайсы, какой-то взаимодействие, просто типа методы, которые по названиям которых мы должны поверить, что это классно всё организовано. Но как бы учитывая предыдущий наш разбор, где я рассказывал про а точки и отношение Мартина к этому, что он может показать пример, который вообще в жизни так не пишут, и это сопровождается какими-то и проблемами, и вообще восприятием того, что происходит. Но при этом типа вот смотрите, какую он идею даёт. Вот здесь есть не очень хорошая история, что идея, конечно, важна, но она должна сопровождаться адекватными примерами. А является ли он адекватным или нет, вот здесь вот, честно говоря, сказать нельзя. То есть я могу пофилософствовать. Я уверен, что так же, как и в комментариях в прошлый раз, тоже многие люди могут прийти и сказать, что он на самом деле имел в виду и что здесь всё нормально. Но это всё, на самом деле, спекуляция, потому что нифига не понятно и контекста здесь никакого нет. А реальная жизнь может оказаться вообще совсем другая, не так, как мы себе представляем это в терминах абстракции, в которых кажется всё достаточно просто. Вот поэтому, к сожалению, этот код прямо обсуждать с точки зрения того, что здесь по-настоящему плохо, ну, довольно сложно. Но сама идея о том, что если у вас в языке есть исключение и есть исключительные ситуации, то есть мы тоже говорим про правильно использование исключений, то, конечно, логику всё-таки стоит строить на основе исключений, потому что ваша программа или ваша библиотека, то, что вы делаете, будет частью какого-то другого, более крупного кода, либо, наоборот, вы используете что-то другое, и есть определённые ожидание по тому, как этот код будет работать. Поэтому в целом тут, наверное, стоит сказать, что нельзя просто прийти и говорить: "Ой, вы не используете исключение, это плохо, у вас коды возврат - это плохо или вот это у вас плохо или хорошо". А всё очень сильно зависит, во-первых, от языка экосистемы, как там конкретно принято. И даже в рамках одного языка это может меняться иногда со временем. Плюс это ещё часто зависит от конкретного даже места, в котором вы работаете. То есть, например, если вы работаете с веб-фреймворками, то там очень часто есть системы midle war, в которых, вообще-то, ни в коем случае нельзя бросать исключения. Там, например, ошибка передаётся для того, от одной медвары к другой. Ну, там определённым образом это часто прокидывается для того, чтобы там управлять процессом, там приостановить работу, ещё что-то сделать. И поэтому, несмотря на то, что, например, в целом в приложении есть исключение, то конкретная подсистема работает чуть-чуть другим способом. Вот поэтому все эти разговоры, они слишком абстрактные и могут создать неправильное впечатление о том, как устроена настоящая история, связанная с обработкой ошибок в целом, да, когда мы говорим не только про исключение. Поехали дальше. Он говорит: "Смотрите, как стал, какой стал классный код, чистый". А и дальше показывает ниже пример кода, который тоже вызывает вопросы. А опять же, да, даже если не брать во внимание то, что здесь есть просто некие методы, которые вызываются, и мы как будто должны поверить, что, э, там с точки зрения логики всё хорошо только за счёт того, что имена а как будто бы а что-то обозначают. Мм, тут вот единственное, я поясню, чтобы было понятно. Дело не в том, что я там пытаюсь к нему придираться, ну, потому что так по-настоящему нельзя сказать. Это всегда такой абстрактный код. Ээ в духе остановили там вот девайс, как у него написано, очистили что-то ещё, ещё что-то. Может оказаться на самом деле абсолютно нерабочей фигнёй, потому что там есть транзакции, там есть какие-то неявные связи, там надо как-то вообще, может быть, вообще по-другому всё это устроено работает или асинхронно должно выполняться. Ну то есть миллиард всяких аспектов, которые могут сказать о том, что на самом деле этот код, ну, никакого отношения к реальной жизни не имеет и так вообще не было бы написано. Вот это очень сильно затрудняет попытку анализа. А представлять в стиле, что о'кей, без разницы, какой там код, главное, какую идею он там доносит, это плохая дорожка, потому что с точки зрение обучения, как правило, люди это связывают, то есть они никогда не воспринимают это так, что ой, есть идея, а вот тут пример - это фигня. Наоборот, они учатся на этих примерах, и эти примеры имеют огромное значение, потому что потом они транслируются, они про эти примеры рассказывают, и эти примеры показываются как эталон. То есть вот так на практике это устроено. И почему, например, я вижу, что здесь сразу множество вопросов возникает. Он говорит: "Смотрите, какой классный код". При этом он показывает код, в котором делается трайв, в рамках которого вызывается ровно один метод. Это уже само по себе странно. Так обычно не бывает. Ну, допустим, а вызывает страйв внутри которого один метод. Дальше перехватывается исключение, что девайс, а, смотрите, try to shutdown и девайс уже как бы остановлен. И после этого просто выводится сообщение об ошибке. Я могу много чего сказать по поводу этого кода. Давайте опять же не, короче, мы не включаем режим, что главное, смотрите, он показал, как исключения работают против возврата. Я буду разбирать всё-таки примеры конкретные тоже, потому что они всё-таки создают у людей некую картинку того, как Мартин мм показывает пример, как писать код. Значит, во-первых, в этом кэтче происходит просто тупо логирование. То есть это означает, что дальнейший код как бы продолжает просто банально выполняться. Ну, кстати, тут тоже интересно девайс shutdown error. Давайте я посмотрю. А это он уже был он уже был остановлен или у нас не получилось это сделать? Сейчас я гляну наверх, что он тут имеет в виду. Если устройство не приостановлено, enable to shutdown. Но вообще звучит странно. Звучит так, как будто статус девайса suspended, то есть типа оно уже остановлено. То есть если м вот так вот с ходу смотреть на этот код, выглядит так, что скорее это речь идёт про идемпотентность. То есть мы пытаемся его остановить, а оно уже остановлено. Но если оно уже остановлено, опять же, если под шотдауном он имеет в виду suspended, который он тут проверяет, device suspended, да, то, соответственно, скорее всего это должна быть и допотентная операция. То есть, если мы пытаемся остановить остановленное, ничего не должно происходить. Это вообще не является ошибкой. Тут просто не должно возникать исключение, если мы рассматриваем это так. А, допустим, что речь идёт о чём-то другом, и у него просто какой-то другой статус, да? То есть мы пытаемся затдаунить, а он в каком-то статусе, в котором, допустим, этого делать нельзя. Ну, во-первых, просто вывод в лог и при этом по сути погасили это исключение. Это очень странное поведение. Я не могу себе представить ситуацию, при которой человек ожидал, что в этой системе произойдёт остановка у этого девайса, и ему вообще никаким образом никак не про демонстрировали это, не показали никакого системного сообщения, ничего. И мы просто тупо пишем влог. То есть это может увидеть только технический специалист. То есть это звучит так, что либо он вообще в принципе забил на логику, ну типа я вот вам показываю исключение, не думайте, что там на самом деле происходит. Либо всё-таки действительно речь идёт о том, что он уже остановленный, мол, значит, мы просто влог пишем, что вот ошибка, но на самом-то деле человек получил то, что хотел, девайс был остановлен. Но тогда возникает вопрос: причём здесь исключение? То есть тогда оно должно работать и допатентно, и тогда не должно возникать исключение. Это просто нормальное поведение. Вообще глобально, кстати, сквозь всё, что он тут пишет, можно одну такую вещь сказать. А я периодически буду к этому возвращаться. Он показывает исключение в тех местах, где, а, фактически речь идёт просто про нормальную логику. И исключения, они нужны не для того, чтобы управлять логикой программы. Это немножко другая история. Я чуть попозже про это расскажу, но сейчас вот давайте посмотрим сюда, да, если есть возможность проверить надёжно, что мы не можем остановить девайс по какой-то причине, то должна быть проверка и должно быть нормальное как бы поведение. Но это вообще ни разу не исключительная ситуация. Исключительная ситуация - это те вещи, которые, ну, физически по разным причинам невозможно сделать. Ну, например, там мы пытаемся писать файл, в это время какая-то другая система этот файл может удалить, да, и, естественно, может возникнуть исключение. Это никак нельзя контролировать, и эта ошибка может произойти просто вот внезапно из ниоткуда. Соответственно, как только та функция, которая выполняет эту задачу, ну, например, пишет файл, и она не может выполнить свою задачу вообще никаким образом, то она бросает исключение, потому что она не знает, что делать в такой ситуации. Это знает кто-то где-то в другом месте. А здесь всё очень чётко, скорее всего, да, что мы понимаем, когда и как мы останавливаем. Тем более, если он уже остановлен, ну, значит, мы просто, допустим, ничего не делаем, потому что, скорее всего, опять же, из коды это понять невозможно, но из реальной жизни это работает. Так что если мы пытаемся остановить что-то остановленное, то ничего, собственно говоря, не происходит. И это должно делаться не потому, что мы там обернули, а потому, что это как бы нормальная логика программы, да, это не является исключительной ситуацией. Вот. Поехали дальше. Значит, он пишет: "Начните с написания команды track finли". В исключении есть одна интересная особенность, но это он говорит. Они определяют область видимости в вашей программе. Размещая код в секции трай команды TRCH Final утверждает, что выполнение программы может прерваться в любой точке, а затем продолжиться в секции catch. Вот. Вот тут уже такой немножко как бы он подводит к тому, что это управление логикой программы именно в плане того, что нормальная логика. Может так быть вот такой немножко опасный момент. И дальше пишет: "Блоки и трай в каком-то отношении напоминают транзакции". Вот на этом этапе я, конечно, подзавиз. То есть опять же при том, что я читал эту книгу, естественно, там, ну, очень много лет назад. Но вот это меня, конечно, удивило, потому что они вообще ни в каком виде не напоминают транзакции. То есть трай - это не транзакция ни разу. Он дальше пишет, что секция catch должна оставить программу в целостм состоянии, чтобы не произошло в секции try. Тем более говорит, что блок try не транзакция. То есть можно, конечно, представить, что секция Чтаки должна восстановить состояние, если что-то пошло не так. Но а это же так тоже не работает, потому что ну какая нафиг транзакция, если у вас лого даже нет изменений, которые там можно откатить. Ну если бы мы по-серьёзному к этому относились, да, потому что, ну, о'кей, у вас есть секцияч, но ваша секцияч может не выполниться. Это всегда так происходит, потому что у вас может там, не знаю, питание выключиться, перезагрузиться сервак. Поэтому ожидать, что логика программы так построена, что в трай мы делаем всё, что угодно, а кетч откатывает, и за счёт этого мы получаем такую типа некую атомарность операции. Это, конечно, абсолютная [ __ ] такого не будет. Это практически невозможно обеспечить. А это можно обеспечить плюс-минус только а тем, что сама логика в трае построена таким образом, что она не ломает текущее состояние программы. Ну, например, такое часто бывает с файлами. Вот, знаете, когда, например, вы устанавливаете какой-то новый софт или распаковывается что, ну, короче, какая-то процедура происходит, связанная с изменением файлов. Так вот, делается это обычно так. Оно распаковывается не туда, где у вас вот прямо что-то сейчас лежит важное, и там действительно в середине может процесса половина файлов от старого, половина файлов от нового. Оно просто кладётся в какую-то временную папочку, новую папочку. То есть все изменения делаются так, чтобы они просто не трогали основной блок. И когда это делается в конце, например, там меняется simлин или там какой-то, ну, короче, какая-то подмена такая действительно более-менее старается быть атомарной, при этом, чтобы не покараптить текущий, например, конфиг. Ну, допустим, если м у вас есть программный способ менять конфигурацию, да, я думаю, что те, кто писали подобные программы или сталкивались с этим, вот если какая-то появляется новая софтина, которая это делает, там часто такой баг есть, когда действительно они, ну, как бы вот же конфиг, вот ты пошёл, его поменял, и там действительно возникают из-за этого проблемы. Поэтому здесь не трайкеч атомарность какую-то обеспечивает, а то, как мы, собственно, пишем наш код и решаем задачи. И во многом это, кстати, вопрос исключительно опыта. То есть можно сколько угодно говорить о том, что есть типа правила архитектура, этого можно учиться. Нифига подобного. Вот пока человек сам с этим не столкнётся или ему не покажут и прямо явно не начнёт думать, он вряд ли заранее сможет догадаться, просто почитав миллион классных идей о том, как писать код и строить архитектуру. То есть до этого ещё надо дойти, а об это надо обжечься. А, кстати, хороший пример. Вот мы тесты регулярно тоже разбираем. Это в тестах проявляется вот РДАУ, да, есть такой в конце тестов, который выполняется, и про него часто говорят. Вот тирдау - это место, где выполняется код, который делает чи очистку. Да. Вот это почти никогда не работает, потому что опять же тердаун выполняется после, и он никаким образом не связан с тем, что было до этого с точки зрения того гарантии. Это означает, что тирдаун, вообще-то, может не выполниться, потому что омкиллер какой-нибудь убьёт ваш запуск тестов, что-нибудь перезагрузится. В общем, любая фигня может произойти или Ctrl C кто-то жмякнет, да, и никакой тердаун не выполнится. Смысл в том, что если в тесте были какие-то изменения итердаун их не почистил, не изменил, не восстановил, то, соответственно, следующий запуск приведёт к проблемам. Поэтому обычно всегда очистка делается не в конце, а наоборот в начале в сетапе. А ещё, кроме всего прочего, там, конечно, могут произойти исключения и вообще в рамках теста что-то вообще пойдёт не так. И тердаун ещё либо нормально не отработает, либо сам выбросит исключение. То есть всякое разное возможно. Так что я бы сказал, что вот эта вот история про напоминание транзакции, попытка к этому как-то свести, ну, не очень хорошая штука, которая создаёт у людей неправильные ассоциации восприятия того, что происходит. Что тут про это пишет? Что по этой причине написание кода, который можно инициировать исключение, рекомендуется начинать с конструкции Try Catch Finally. Это поможет вам определить, чего должен ожидать пользователь кода, чтобы не произошло в кодей. Опять же, на практике я бы не согласился здесь, потому что заранее думать об ошибках и о всех этих штуках такое себе. Есть языки, конечно, которые вас заставят. Например, опять же, тот же Хаскиль, в котором вы просто не сможете написать, пока там все кейсы не обработаете. Но, мм, в мейнстримовых языках в основном всё-таки с более-менее стандартным, принятым и, как естественным образом происходит подходом является написание основного сценария рабочего. И при этом, да, у вас по пути могут быть выбрасываться исключения, вы даже сами можете дописывать какие-то исключения, но прямо сидеть с ходу такие: "А давайте я тут везде тракетчив понаставлю". Вот этого делать вообще не стоит, потому что вы ещё пока до какого-то момента не допишете программу, если вы с нуля там начали, да? то понять, в каком месте что и как обрабатывать - это ещё большой вопрос. Опять же, если у вас нет опыта, например, с той областью, в рамках которой вы это пишете. Поэтому лучше наоборот ничего не делать, а потом, когда уже вы начнёте писать тесты или там гонять будут это всё добро тестировщики или вы сами будете ходить проверять, вы будете видеть конкретные кейсы и понимать, ага, как в как в той или иной ситуации нужно себя вести. Кстати, интересный просто пример приведу. Вот ошибки в валидации, да, есть книжки, по-моему, даже у Мартина где-то было и в Джаве тоже особенно это было принято. Показывали прямо в примерах, что вот если ошибка возникла, а, в смысле валидации, то бросаем исключение. И тут как бы есть два момента. Первое, ошибка валидации вообще не является ошибкой. Это просто нормальное поведение программы. Вот если в базу не получилось сохранить, вот это реальная ошибка. И там что-то надо делать. Скорее всего, вообще 500 показывается, говорится, не смогла, всё плохо. Но если у вас просто не прошла валидация, это вообще абсолютно нормальное поведение программы, и оно, вообще-то, с точки зрения смысла исключений не должно быть реализовано исключением. Но это часто показывают. А вторая проблема, которая тут возникает, по крайней мере, вот в тех книжках, которые там про код, про всякие подходы, там часто показывают прямо проверку и выброс исключения. Смотрите, что происходит. Ну, допустим, у вас форма и в ней там 10 полей. Получается, что при таком подходе проверяется только первое поле, и если там ошибка, бросается исключение. Остальные действия не проверяются. Соответственно, вы в форме просто банально валидацию увидите только для первого поля. Хотя ошибка может быть, ну, ошибок может быть много. Они относятся к разным полям. Вот поэтому в исключениях, если бы, например, я писал соответствующую книжку, кстати, не могу не отметить, мне там периодически тоже поненты про это пишет, что вот, мол, Кирилла что-то написал. Я много написал, на самом деле. Другой опрос, что просто в другом формате. То есть у меня есть достаточно много курсов, в которых есть изучение того, как строятся абстракции. Например, у меня есть даже целый курс, посвящённый тому, как правильно работать с исключениями. Он в осно, правда, на JS только написан, но там прямо мы работаем с созданием своей собственной файловой системы, а файловая система с она соотносится с большим количеством как раз исключительных ситуаций. И там я как бы на протяжении многих уроков учу, как, собственно, какие исключения надо, почему, где их там создавать и так далее. Поэтому, в принципе, это проходит и прошло большое количество людей. Но, естественно, не такое большое, как прочитал чистый код. Вот поэтому мне тут точно есть, что сказать. И на уровне того, что я написал, на уровне того, как это повлияло на людей, которые всё это проходили и проходят. Так что оно никуда не делось. Если кому-то интересно, можно пойти посмотреть. Давайте поехали дальше. Поговорим о том, что происходит. Короче, он начинает показывать, а, сразу то, что мы пишем, а, обработку ошибок. То есть даже не основной кейс. Он говорит: "Давайте начнём с модульного теста". Я особо этот код разбирать не буду, просто повторюсь, что, несмотря на то, что его подход, я не могу говорить, что он неправильный. Ну вот кто-то, наверное, считает правильным, кто-то, наверное, так и делает. А, но я бы так не делал. То есть я бы всегда писал с минимумом проверок, а и дальше постепенно потом оно становилось понятно, как и где нужно с этим работать. Но в любом случае, скажу так, чем ближе исключение, такая евристическое такое правило, чем ближе обработка исключения находится к месту, где оно возникло, тем больше вероятность, что мы вообще неправильно работаем. Потому что, как правило, всё-таки исключительные ситуации, настоящие исключительные ситуации, они в большинстве случаев приводят к тому, что программа не может корректно продолжить свою работу и нужна помощь оператора человека, там, не знаю, открываем файл, там какая-то беда, а, соответственно, не знаем, что с этим сделать. Ну, максимум бывает там ретраи, допустим, какие-то, которые иногда могут сработать. А исключение где-то в глубине - это как раз мы погасили какую-то какое-то поведение и типа восстановились и смогли дальше продолжить работу. А такое, конечно же, бывает. В конце концов, всё-таки бывает. То есть разные можно кейсы привести, но опять же скажу, что это не является стандартной ситуацией. То есть она всё-таки такая особая. И чаще всего она, кстати, возникает не потому, что там реальные исключения, а именно потому, что библиотеки так были написаны, что они бросают исключения там, где, в принципе, этого не нужно делать. Кстати, вот есть интересный пример. А, такая библиотечка AXOS для HTTP запросов в Джесе. Она устроена следующим образом. Вот, допустим, мы делаем запрос на страничку, которая возвращает 500 или любой другой код, не 200. Является ли вообще эта ошибкой с точки зрения этой библиотеки? Ну, вообще-то нет, потому что она выполнила запрос, там ей ответили HTTP, вернули просто код 500. То есть с точки зрения, вообще-то, HTTP библиотеки, код 500 не означает ничего плохого. Это просто возврат, который, ну, её попросили сделать, она сделала и она вернула. Так вот, там так устроено, что эта библиотека по дефолту бросает исключение, если там явно это не указать. И поэтому, например, когда с ней работаешь, то есть библиотека сама по себе классная, но конкретно в этом моменте там постоянно приходится указывать, что не надо бросать исключения для таких ситуаций. Вот, а для того, чтобы иметь возможность работать с тем, что она возвращает. Ну, как такой вот пример того, как исключения пихают слишком много, слишком часто там, где их не нужно делать. Так, ну, здесь вот, собственно, он рассказывает про то, как он, значит, обрабатывает исключение, как он пишет, значит, какую-то штуку про ТДD. У него, кстати, потом будет блок про ТД. Мы обязательно его разберём тоже. Вот. Ну и, в общем-то, всё. Тут как бы разбирать особо не будем. Просто речь о том, что а он предлагает вот заранее все исключения а проработать. Дальше используйте непроверяемые исключение. Я, когда готовился, думал вообще про это говорить или не говорить, но скажу так, в Джаве есть особая конструкция, это проверяемые, не проверяемые исключения. Это специфика Джавы. По большому счёту, сама эта идея провалилась, потому что она оказалась неудобной на практике, несмотря на всю концептуальную идею, что, э, мы фактически выносим исключение в сигнатуру метода. То есть, если мы бросаем какое-то исключение, то мы перечисляем там все исключения, которые бросает этот метод. Соответственно, тот, кто его вызывает, он должен будет тоже это всё описать. И таким образом по цепочке наверх мы как бы везде эти исключения указываем. Но по и там в итоге всё это будет приводить, потому что на самом верхнем уровне нам придётся перечислять вообще все исключения, какие только в принципе есть во всех библиотеках. И речь идёт о гигантском количестве исключений, конечно. Вот. И, соответственно, при изменении любой внутренней истории надо будет менять всю верхнюю систему. Вот такая вот беда. Сама идея не является плохой, но из-за того, что у вас много разных видов исключений, то она вот приводит к тому, что это довольно неудобно на практике. Ну не будем, короче, погружаться, просто, к слову, это, мм, специфика Jav. У остальных таких э такой механики нет, и поэтому они с этим просто не сталкиваются. Поэтому можно, в общем-то, пропускать. Хотя тут есть, конечно, что сказать. А в реальности, может быть, я в двух словах даже скажу. Мы будем ещё возвращаться к побочным эффектам регулярно, потому что, как я уже говорил, я не просто так про это разговариваю. Побочные эффекты - это одна из самых главных мм вещей, связанных с проектированием, с тем, что вообще куда относится. Вот что-то похожее с тем, что надо прописывать в типе какие-то возможные такие эффекты аля исключения, да? А есть в Хаскиле, собственно, когда мы работаем с вводом-выводом, когда у нас есть i, то есть когда нам надо что-то напечатать, например, там логи всякие вывести и так далее. И там действительно так и происходит. То есть, например, если вы как бы в своём коде используете функцию, которая, допустим, пишет файл, вам просто придётся теперь все функции, которые, то есть функция, которая вызывает функцию, которая пишет файл, сама становится функцией, которая пишет файл. То есть неважно, что там внутри какая-то прокладка это делает. Смысл в том, что с точки зрения внешнего наблюдателя её результатом работы становится запись файла. Это логично абсолютно. И то же самое касается э как бы всего остального по цепочке выше. Это очень похожая ситуация. Другой вопрос, что исключений много разных, а здесь просто речь идёт о том, что в принципе идёт работа с Айо. Поэтому такой проблемы, как необходимость описывать там огромное количество исключений разного вида, да, то есть там скорее просто, что каждая, грубо говоря, функция становится грязной в терминах вот функционального программирования. И это довольно прикольная вещь, которая, с одной стороны бесит для тех, кто вот с этим работает, а с другой стороны именно по этой причине многие говорят, вообще Хаскель как язык очень сильно помогает как раз сам именно своей структурой понимать, как правильно писать программы. Дело в том, что концепция отделения побочных эффектов от чистого кода - это не концепция функционального программирования, это просто история именно с тем, как писать хорошие, классные, надёжные программы, что у вас логика там, где. Ну и, соответственно, даже если вы посмотрите на всякие вот эти архитектуры, которые предлагаются для опять же тем же Фаулерам или Мартином, они на самом деле все про это. Просто они часто явно это не проговаривают, что у вас, э, например, там есть домен, это чистая логика, где есть, э, разные абстракции и и вычисления, а вот, мол, давайте работать с базой отдельно, они где-то там они внизу сами это вызывают там внешние какие-то сервисы отдельно и так далее. Это на самом деле плюс-минус о том же самом. И в этом смысле, когда вы работаете в Хаскеле, вы просто сразу он вам язык вас заставляет правильно проектировать хотя бы вот в этой части. Потому что как только вы написали какую-то функцию, которая делает просто вычисление, ну там любят приводить пример обычно, когда про ОП говорят, там репортер какой-то, да, и внутри вы такие: "А давайте я сейчас файл запишу". Вам приходится, то есть, во-первых, у вас появляются потенциальные ошибки, которых раньше не было. у вас усложняется вся цепочка, ну и снаружи становится с этим сложнее работать, потому что там уже просто возврат функции не посмотришь, там надо файлы смотреть и так далее. Короче, как только вы начинаете писать в таком языке, он вас автоматически заставляет думать об этом и приучает тому, что надо с побочными эффектами уметь работать и выделять их и делать так, чтобы у вас основное ядро программы, оно вообще не взаимодействовало с внешним виром. И вы как бы чётко понимаете: "Ага, вот эта вычислительная часть программы алгоритмическая, вот эта часть связана с тем, что она там что-то пишет, делает и так далее". И я знаю, что как и в прошлый раз, многие там, ну, не то, что многие там ребята писали о том, что, ну, вот тащите в OP мир функциональные какие-то ваши штуки. Нет, это не функциональная штука. Просто есть вещи, которым в функциональном языке очень много хорошему учат э за счёт разных фишек, которые там есть. Я просто хочу напомнить, что гигантское количество вещей, которые есть в современных языках мейнстримовых, они пришли из функциональных языков. И в этом плане не существует противодействия FP или там ОП. Это, кстати, тоже тема для отдельного видео, потому что действительно у многих в главе есть представление, что это некие какие-то противоборствующие концепции. Нет, противоборствующая концепция - это императивное и декларативное программирование, а ООП и F прекрасно сочетается. Я уже даже говорил, что вы можете открыть какой-нибудь ОКМЛ и посмотреть, как язык одновременно является и функциональным, и объектно ориентированным. И всё это прямо в одних и тех же строчках кода. Я уж не говорю про те же стримы, например, в Джаве, да, или там Mapфilter Redce. Это всё стандартные функциональные штуки. И прекрасно они вызываются у объектов как методы. И никакого противоречия тут нет. При этом обратите внимание, что в тех же самых стримах запрещено внутри, например, мапы делать побочные эффекты. Не просто так. Всё это очень важно, потому что у нас есть параллельность, многопоточность, всякие разные гарантии и так далее. И наличие побочных эффектов, а во многом вот то, что связано с IIO, оно, конечно, приводит к проблемам со всех сторон. Так что если даже язык вас не заставляет этим заниматься, мышление через такой взгляд, оно очень сильно помогает. Ну вот, есть несколько таких фундаментальных вещей, на которые наслаивается вообще всё остальное. Вот одна из них - это это. Второе - это немножко история, связанная с конечными автоматами, вообще пониманием состоянием переходов вот такого добра. Ну ещё какие вещи, которые давайте сейчас не будем отвлекаться, потому что я уже чуть-чуть отошёл от темы. Но те, кто часто регулярно меня слушает, знает, что я примерно ту же одну и ту же канву рассказываю там на протяжении многих лет. Не перестану этого рассказывать и не перестану спорить с теми, кто в ООП видит. сильно другие вещи. Так что добро пожаловать в комментарии снова обсуждать все эти моменты. Поехали дальше. Передавайте контекст с исключениями. Каждое исключение инициируемое в программе должно содержать достаточно контекстной информации для определения источника местонахождения ошибки. Бла-бла-бла-бла-бла. Ну, это такой чисто прикладная история. Грубо говоря, когда вы сами отлаживаете свою программу, в какой-то момент просто видите, ну, что вам не хватает информации. Ну, и каким-то образом постепенно она, да, может расширяться. А заранее об этом подумать сложно, заранее предугадать это почти нереально, и проблемы это никакой не несёт. То есть это не будет историей про то, что вы этого не сделали и пожалели. Нет, вы просто в нужный момент просто берёте и добавляете. Поэтому думать об этом заранее особо не надо. Надо просто знать, что вы, в принципе, имеете возможность такую делать. И когда вы сталкиваетесь с определённым типом ошибок, вы понимаете, что, хм, самого исключения недостаточно, надо ещё каких-то данных. Ну, например, там код возврата. Если вы там с внешней системой работали, она вернула код, а вы это превратили в исключение. Ну, код можно туда положить. Вот такой вариант возможен. Но сидеть и заранее думать, что я сейчас весь контекст туда засуну, лучше так не делать. Определяйте класс исключений в контексте потребностей вызывающей страны. А, ну здесь про классификацию ошибок, про класс исключений. Тут действительно немножко сложно, потому что бывает такая ситуация, что исключение, которое генерируется внутри, допустим, оно не совсем то, которое мы бы хотели показать наружу. Или мы, например, используем какую-то библиотеку, сегодня одну, а завтра другую будем. И они бросают какие-то исключения, которые их внутренние. Соответственно, если мы их не конвертируем, например, в наши исключения, то при обновлении библиотеки мы можем очень сильно подставить людей, которые пользуются нашим кодом, потому что они будут завязаны исключения либо, которые больше там не существуют. Поэтому это одна из вещей важная при проектировании исключений. Иногда бывает нужно обёртки сделать, иногда нужно исключение, которое, допустим, было такое generic общего какого-то типа аля, не знаю, nullpinter exception, мы превращаем во что-то значимое, если оно действительно должно произойти. Вот. Ну и, соответственно, то, как мы их разбираем, эта история такая, за 2 минуты не рассказать, очень сильно зависит от конкретных задач. И здесь, наверное, лучше прямо по кейсам разбираться. Но проблема вот кода, который показывает Мартин, который вот красной линии сквозь всю книжку идёт, он показывает слишком абстрактный код. Вот какой-то port Open, ACME порт. Наверное, люди, которые с такими вещами работают, они скажут: "Кирилл, всё понятно, я типа вообще и всё и так это знаю". Но книжка для широкого круга читателей, которые вообще не факт, что с такими вещами работали. И поэтому, глядя на это, сказать, что это вообще нормально или ненормально, просто, ну, нереально. Я смотрю на код, и я понимаю, что мне не хватает информации для оценки того, насколько это всё хорошо. Ну да, он показывает, что у нас тут типа репорт ошибки примерно один и тот же, но является ли это причиной вообще, что там что-то неправильное с эксепшенами, это вообще не факт. То есть приходится верить ему на слово, а я не очень этого хочу делать, зная, как какие вещи он нагда рассказывает и делает. Вот так. Поехали дальше. Ну, собственно, он про это рассказывает. Здесь вот ещё в чём прикол. Он, э, подводит немножко к файнале, что есть общие вещи, которые вот можно так выносить и, соответственно, делать какой-то общий репорт, например, допустим. А, ну ещё и про иерархию, понятное дело. То есть понятно, как правило, когда вы строите, ну, например, опять же, если вы библиотечку пишете, то у вас всё-таки идеально, если все исключения идут от одного какого-то базисного исключения, определённого этой библиотекой. Тогда, конечно, работать по-настоящему будет приятно. Но тут другая вот эта проблема, да, с постоянным обёртыванием тоже можно переборщить и совсем уже обалдеть от количества исключений, перехватов, перебросов, что и в том числе на перфоманс влияет. Короче, тут может быть сложно, но если у вас такое прямо совсем базовое, чистенькое, то, конечно, идеальная ситуация, при которой у вас есть базовые исключения, от него какие-то там наследники, а, и вот у вас такая иерархия. Ну, опять же, когда говорю базовые исключения наследники, это вполне возможно, речь идёт про интерфейсы. Надо смотреть по ситуации. Поехали дальше. Определите нормальный путь выполнения. Выполнение рекомендации обеспечить хорошие день без логиково обработки ошибок. Под вопросом. Под вопросом, потому что я не считаю, что человек, который вот прочитал то, что он там написал, вот, допустим, у меня всегда есть такая лакмусовая бумажка. Я её проверяю на своих курсах, на своих материалах или вещах, которые где-то рассказываю и и на чужих тоже. Я говорю: "Вот смотрите, давайте вот возьмём, допустим, он говорит, что предыдущие рекомендации, вот это уже классно, всё, они вас учат классно разделять бизнес-логику от исключений". Я честно считаю, что человек, который это прочитал первый раз и у него немножко опыта, и вот он на базе этого учится, это ему вообще практически никак не поможет, честно говоря. Слишком мало, слишком абстрактные примеры, мало реальных кейсов. Понятно, что что-то устарело банально, да, но этого недостаточно. Да, если бы мы ему дали, то вот этот блок, так сказали: "Всё прочитай". Ну что, давай теперь пиши. Получилось бы довольно печально. А может быть, даже ещё хуже. Знаете, как часто бывает, когда подобные книжки читают и потом все бесятся, потому что человек ну пересмотрел, значит, э чего-то, переслушал и начал везде это использовать, где надо и где не надо, да, и пытаться всё там оборачивать, всё разделять. Поэтому, кстати, в этом плане обучение вообще программированию оно, да и многим вещам, оно устроено таким образом, что баланса не бывает. Я, мне кажется, уже об этом несколько раз говорил, о том, что когда мы учимся, мы, как правило, видим какой-то новый классный способ, и мы пытаемся везде его применять. Потом нас бьёт по голове фигурально, да, что кто-то может нам это сказать или жизнь заставит, или мы увидим какие-то проблемы. И мы, например, в этот момент видим ещё какой-то новый способ. И таким образом, а, идёт как бы прыгание от одного максимума к другому. Пока, в конце концов это где-то не сбалансируется, но вот так, что мы взяли человека с нуля, начали его учить программированию, и он в какой-то момент такой, читая все книжки, сразу такой весь сбалансированный, делает всё так по правильному восприятию мира, что вот здесь вот надо компромисс, здесь баланс, здесь вот подойдёт, здесь не подойдёт. Так не бывает. Легко об этом говорить, когда человек уже опытный программист и про это рассказывает. Но человек, который учится, его, конечно, всегда будет бросать из стороны в сторону. И это нормально. Главное не застревать, да? То есть, что мы попробовали одно, потом второе, третье, и вот на когда таких много полюсов, оно как где-то там в серединке зацепится, и человек уже появится здоровое отношение к разным вещам. Вот поэтому, когда он такие вещи пишет, ээ как бы с одной стороны тут, ну, обсуждать вроде нечего, а с другой стороны я понимаю, что всё равно это такая немножко, ну, манипуляция. Мне это вот не очень нравится. И есть даже видео, где когда разбирают фаулеровские срачи, где он участвует, там прямо чувак сфокусировался на том, что он разбирает именно то, как фатин занимается именно такой словоблудием и манипуляторством, когда он общается с людьми. Это и по книжке видно, и по его вообще взаимодействию в твиттере, когда он уходит от вопросов, там, от ответов, когда он там переворачивает всё с ног на голову. То есть просто реально там, я не знаю, на миллион просмотров целый видос, а посвящённый именно этому. Не к тому, что хочу разгонять, просто есть вот этот вот моментик, который можно просто не увидеть и считать, что да, да, он всё написал, всё классно, всё теперь понятно. Ну, ту типа на трёх страничках про исключение. Блин, люди десятилетием их понять не могут и использовать правильно. Тем более, кстати, сейчас я покажу пример, где там на самом деле всё довольно печально. Дальше он показывает пример кода и рассказывает вот что. Он говорит: "Смотрите, пример. В следующем довольно неклюжем фрагменте суммируются командировочные расходы на питание". Дальше он показывает опять же такой очень вырванный из контекста код, к которому есть вопросы, я бы его разобрал. То есть мне кажется, что скорее всего вообще то, что здесь написано, в принципе, так не решается. То есть там скорее это вообще по сущностям по-другому сделано. Там будет просто суммирование какое-нибудь по базе. Ну, в общем, то, что он здесь написал, нельзя про это сказать, что это вот осмысленно правильный код. Не с точки зрения того, как вот обработка исключений здесь идёт, а вот вообще в целом, если не брать этого внимания. Поэтому в каком-то смысле мы это не будем касаться здесь, потому что опять мы сейчас скатимся в историю про абстракции, но цель у нас немножко другая. Так вот, что я хочу сказать. Он показывает tryйке cйч и показывает, значит, как мы считаем расходы. Тут, конечно, странностей много. Get meals вообще из-за названия этого метода сделать вывод, что он возвращает именно расходы просто невозможно. То есть это явно название метода не соответствует смыслу. Вот. Аэ, ну, допустим, короче, он возвращает какие-то расходы, и дальше идёт суммирование расходов. И после этого, а, возникает исключение me expenses not found, где продолжается логика. Вот, в принципе, на этом можно было бы и заканчивать. То есть, если бы кто-то где-то такое написал, там бы сразу сказали: "О, я на этом этапе перестал смотреть, значит, дальнейший код, потому что тут, в принципе, всё понятно". С точки зрения чего? В Martinн ниже он показывает код, про который вообще по-другому надо рассказывать, что фактически человек, который написал, он построил логику программы на исключениях. То есть, если у нас соответственно м gets что-то там вернул, то мы считаем тотал одним способом. Если он не вернул, то считаем тотал другим способом. Ну, просто не погружаю в детали, потому что вот он тут целый пост написал на тему того, что здесь происходит. Вот. Не вернул именно not found. Причём, кстати, Note Found - это обычно всё-таки относится к базе данных, да, когда, например, там запись не бы не была найдена. Вот. Причём опять же me expenses not found, а мы делаем get mes, что очень странно выглядит. Ну вот, в общем, а в итоге получается, что это, вообще-то, никакая не исключительная ситуация. Это просто поведение в зависимости от того, есть оно или нет. И оно должно решаться нормальным, добрым, старым ифом, а с проверкой того, что там он нашёл или не нашёл. Он не говорит о том, что исключения здесь вообще не должно быть. Он говорит, что, смотрите, мы можем переписать код так, чтобы этого исключения не было, при этом подразумевая его как нормальное поведение. То есть он типа, он говорит фрагмент просто типа неуклюжим, да? А и дальше он предлагает по сути использовать э паттерн. На русский он перевели его как особый случай. Я, честно говоря, забыл, что вообще по-русски так его называют, потому что для меня это просто, а, null object, то есть, когда, грубо говоря, если у нас есть NAL и а мы для того, чтобы не делать проверки нал постоянно, возвращаем вместо этого объект такой пустышку, да, который соответствует тому же интерфейсу, как и нормальный объект, который мы ожидаем, и внутри него реализованы методы, которые возвращают какие-то дефолтные значения. И да, это вообще-то классный паттерн. Это классическое применение полиморфизма подтипов. Это одна из вообще причин существования ОП, потому что полиморфизм там вот этот именно является краугольным камнем, который реально влияет на программы. И есть места, где это очень классно использовать. Я могу пример даже привести, то, что я сам люблю N object в нужных местах пропагандирую, про него рассказываю. Это, например, особенно в вебе current user. То есть вот если человек не залогинен - это некий гст, если залогинен - это usер. И это довольно классно, потому что в таком случае у нас всегда есть некий юзер, из которого можно дёргать всякие методы. И, как правило, просто они, ес, ну, например, допустим, юзер возвращает коллекцию чего-то там, неважно коллекцию чего, тост просто будет возвращать пустую коллекцию. То есть, и это всегда будет работать, и это подходит в большинстве случаев. Почему там это важно? Потому что там иначе, поскольку в вебе это может встречаться, там в разных медварах, в контроллерах, в обработчиках, короче, везде это может встречаться. Это приводит к тому, что код просто будет проверять нал или не нал. То есть и этих вот этих проверок будет по всему коду очень много. То есть если кода будет много, то их будет дофигище. И в таком случае это классная реальная кейс, когда на objectжик подходит. Есть много других случаев, когда, несмотря на то, что NA возвращается и мы такие: "О'кей, мы можем любой нал заменить на на object. Это правда, но в реальности это только усложнит восприятие, потому что у вас появится куча объектов, которые вообще не несут никакой пользы в местах, которые вот одноразовые по сути. И у вас всёвсёвсё будет постоянно инициации объектов там. Это и производительности касается, и того, что а у вас, скорее всего, там интерфейсы будут не очень маленькие, да, довольно крупные, потому что опять же не все интерфейсы делятся там на один метод, соответственно, вам дофигище чего придётся реализовывать. А в некоторых местах вообще будет странное поведение, типа, а как вообще должен этот метод у него действовать? О, должен, не должен. В любом случае иногда надо какие-то проверки делать, потому что бывают более хитрые ситуации. Короче, я к чему? К тому, что такой подход создаст монстра, если мы всегда и везде будем это делать. Заменять налы на лобжик. Там он потом ниже про это, кстати, сейчас ещё будет говорить. Мы тоже к этому вернёмся, это немножко обсудим. Но там есть проблема с тем, что не точнее, не проблема, а особенность Джавы. В современном мире большинство языков, особенно новых, ну и не только новых, они уже сableл работают, да? То есть у нас есть возможность описывать, нал вообще допустим или недопустим. И это очень сильно снижает количество возможных проблем, которые могут возникнуть. Вот если говорить просто про убирание банально ифа на полиморфизм, а это, кстати, всегда так есть, то есть полиморфизм что делает? Он убирает, по сути, if. То есть у нас либо есть if на, соответственно, вот ту проверку, которая здесь описана, а, me expenses not found, да, найдены или не найдены. А если мы делаем n, у нас типа уходит. Но, честно говоря, вот так вот глядя на этот код и догадаться, что на object возвращает что-то, что соответствует тут такое есть описание, как будто это не расходы ежедневные, а человек, который в командировке, это вот здесь такой кейс, а именно некие дефолтные це число денег, которое просто он должен в день тратить. Это, блин, [ __ ] как не очевидно, потому что называться-то оно будет так же, как здесь get total. А то, что на самом деле это является вообще чем-то другим, это оченьочень сильно не видно. И в итоге, да, вы как бы прячете эту логику, у вас типа код становится простым, но если вот весь код из такого состоит, понять, что реально там происходит, просто становится невозможно. Вот тут можно со мной спорить, говорить, что нет, это классно, я вообще не согласен, потому что этот код не становится проще. Этот код просто начинает всё это прятать. У вас сложность уходит вниз, вы можете видеть действительно код, который выглядит как, ну вот такой последовательные методы. При этом отсутствие ифов не убирает эти ифы. А вы должны теперь будете до них догадываться. И в случае, опять же повторюсь, каurнт юзера это вообще классно, нормально работает, там чётко есть вот это деление и понятно, как это использоваться будет, то вот в этом конкретном кейсе это создаст проблемы. То есть человек, скорее всего, не будет ожидать. И более того, глядя на код, в котором есть вот просто вот он пример тут приводит, вот типа просто total плюс равно expenses get total и всё. А внутри там, соответственно, в зависимости от того, какой это тип. Но, во-первых, у вас в любом случае есть создание, то есть у вас в любом случае этот if есть, он просто внутри находится, и там идёт проверка, если не не нал, то, соответственно, создаём эту штуку и прокидываем. А снаружи вы просто не догадайтесь, что у вас есть такая логика никак. То есть наоборот она спрятана. Можно ли сказать, что это типа абстракция, это хорошо и так далее? А, в данном случае не согласен, потому что это именно тот, именно этот уровень а абстракции. То есть мы прямо знаем, что если у нас нету э расходов по конкретному дню, по конкретному человеку, то мы добавляем там а какую-то стандартную единицу расходов, то вот это должно быть явно прописано. Ну либо как-то вообще всё это переназвать, может, вынести какой-то метод, ещё что-то сделать. Но в общем, я считаю, что это решение неправильно. Вот поехали дальше. Ну вот он как раз приходит к возврату нал и говорит, что приводит пример, говорит: "Смотрите, везде проверки, всё плохо, так делать нельзя". Ну здесь всё уже, в принципе, сказано, да? То есть современные языки - это nullable. И возврат на не приводит к проблемам, потому что нас просто компилятор заставит, а все эти проверки написать там, где они реально нужны, там, где не нужны, соответственно, писать их не надо, а и всё будет нормально вызываться. И вообще ошибка типа Pointer exception просто в куче языков, ну, можно сказать, не существует. Ну, есть, конечно, но там это всё связано с внешним взаимодействием, которое гораздо реже встречается, чем вот то, что там по коду расписано и раскидано. Поэтому я бы не сказал, что это сейчас большая проблема. То есть тут просто с двух сторон можно смотреть. С одной стороны есть вот именно джавовая штука, что там набл нет. И тогда, да, там речь идёт про постоянные возможные ошибки, а с другой стороны, речь идёт просто про то, что типа много этих проверок может быть. Ну, честно говоря, ну, могут эти проверки быть, ничего в этом страшного. Возьмите пример какой-нибуд, не знаю, дом в джаваскрипте. Вы по селектору достаёте элемент, а его просто физически в этом доме нет. А может не быть. И, соответственно, этот метод может вернуть нал. При этом вы же сами этот HTML написали, ну, допустим, и вы точно знаете, что он там есть. Поэтому в динамическом языке вы бы не парились. Вы такие: "Всё, я делаю query selector, а достаю, соответственно, какой-то элемент по селектору, и я знаю, что он там есть, потому что вот этот мой собственный HTML, который я написал, но нет, Typeescript вас заставит написать if и проверку нал. Можете ли вы сказать QERY селектору: "Возвращая не нал". Не можете. Вы обязаны это написать. Это абсолютная норма. При этом, если бы он возвращал всегда не нал, то вам бы всё равно каким-то образом приходилось бы проверять, потому что а как вы с этим потом будете работать? Да, не всегда можно представить, что если у вас вместо нала вернуться на object, что вы работаете с реальным объектом, не обращая внимания на то, как он себя ведёт. А может быть, наоборот, надо остановиться и сказать, не знаю, там, пользователю, что мы вообще ничего не нашли. Поэтому, несмотря на красоту потенциальную такую теоретическую, на практике попытка везде это втыкать приводит к проблем. Даже могу сказать вот про свой недавний кейс. Прямо сейчас вот на Хексте мы там кое-что переделываем. И исторически мы как раз так пытались делать. То есть, грубо говоря, у нас есть такой, например, есть такое понятие - это человек, который проходит текущий курс или там текущий урок или текущую программу. То есть там всегда есть, потому что отмечается дата старта, а статус прошёл, не прошёл и так далее. И мы в какой-то момент решили, что давай там всё налобжектаме обложим. И у нас, например, банально, когда человек находится на страничке курса, то мембр есть всегда. И изначально это казалось классной идеей, но чем больше вот прошли годы, да, и мы с этим сталкиваемся постоянно, каждый раз ты понимаешь, что тебе нужно думать об этом, и ты не можешь просто вот делать вид, что ничего не происходит, есть мембр или нет, потому что там от этого много чего зависит. И в итоге мы, например, стали там банально это выпиливать. А так что, видите, оно не всегда м так удобно. Так, хорошо. В общем, вот это вот большой блок, связанный с науми. А, ну, кстати, вот тут пример приводит, что, смотрите, нам не надо возвращать, где можно вернуть коллекцию. Это, кстати, классный совет, и я всегда про это тоже говорю. То есть, если у вас есть метод, который возвращает список, но он обязан возвращать список всегда. У вас не должно быть такого, что этот метод вообще технически может вернуть на то есть, если ничего нет, то должен возвращаться пустой список. Вот такие дела. Не передавайте нал. Ну, он показывает пример опять же из той же серии, что мы, по сути, ручками систему типов заменяем, начинаем проверять и всё это только в рантайме будет видно, и один фиг выкинет такое же исключение, не N pointer exception, но просто исключение. Но смысла в этом как бы большого нет. Поэтому нал был наше всё с одной стороны, а с другой стороны, ну, мы же редко передаём нал, потому что мы, а, прямо сами явно его передаём. Как правило, он передаётся, потому что где-то возник нал сам по какой-то причине. То есть мы передаём идентификатор, а внутри может лежать нал. Поэтому говорит, что не передавайте нал, но это не всегда честно, потому что мы не всегда это контролируем. Это может зависеть от того, что вернулось от предыдущей системы, от которой мы получили данные для текущего вызова. Вот. Но в целом проблема есть, и страдать джавистам с этим приходится больше, чем, наверное, другим в статических языках современных. Поэтому, что называется, селяви. На этом, ребят, сегодня всё. Я надеюсь, было интересно. Обязательно напишите в комментариях, что вы по этому поводу думаете. Я приложу туда обязательно одну классную статью с Хабра, а которая, наверное, я считаю, это лучший вообще материал, очень короткий, очень компактный, очень осмысленный на тему того, зачем нужно исключения, как их использовать. А, и в целом скажу, что это, вообще-то, не самая простая тема. Обработка ошибок и материалов о том, как вообще реально ими пользоваться, не пользоваться и так далее, в интернете практически нет. Да, есть какие-то отдельные статьи, можно говорить про чистый код, но я не считаю, что то, что здесь рассказано- это реально классный труд, который позволяет научиться пользоваться исключениями и вообще понять их на глубоком уровне. Поэтому я бы, наверное, что-то другое ещё порекомендовал. Ну, помимо того, что у меня есть собственный курс на эту тему. Всем большое спасибо. До новых встреч. Жду вас всех в комментариях. Если понравилось видео, ставьте лайк. Если не понравилось, ставьте дизлайк.

[музыка]