Команда Spring АйО не могла остаться в стороне и не прокомментировать одну из самых обсуждаемых новинок Kotlin, анонсированную на KotlinConf 2025 — Rich Errors. Вместо того чтобы выбрасывать исключения, теперь функции могут возвращать возможные ошибки как часть своей сигнатуры:

fun fetchUser(): User | NetworkError

Такой подход делает потенциальные сбои явными, упрощает тестирование и избавляет от try-catch для предсказуемых ошибок. Новинка уже доступна в Kotlin 2.4 и, по мнению авторов, особенно полезна в бизнес-логике.

Но в сообществе мнения разделились.

Что говорят сторонники Rich Errors?

  • Это логичное продолжение идеи безопасности типов, как null safety.

  • Ошибки становятся частью API — теперь явно видно, какие именно проблемы могут возникнуть.

  • try-catch больше не нужен там, где ошибка — ожидаемый результат.

  • Тестировать становится проще: вместо моков и исключений — обычная проверка значения.

  • Хорошо сочетается с функциональными паттернами без необходимости подключать сторонние библиотеки.

Что вызывает сомнения?

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

  • Нет способа элегантно агрегировать ошибки: A | B | C работает, но не имеет общей семантики.

  • В рамках Spring-приложений реалистичная польза ограничена — фреймворки останутся на исключениях.

  • Добавление такого типа обработки может серьёзно сказаться на времени компиляции.

И что теперь?

Для одних Rich Errors — это долгожданное улучшение и эволюция Kotlin. Для других — спорный эксперимент, который добавляет сложности без ощутимой пользы в реальных проектах.

А вы как думаете? Используете ли Rich Errors в своём коде — или пока просто наблюдаете со стороны?


Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм — Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано

Комментарии (9)


  1. ExTalosDx
    25.07.2025 14:24

    Spring могут и доработать.

    Нет смысла спешить с мнением, кмк.

    С моей точки зрения это более явный и более удобный optional. Только в разрезе не только null safety.

    Так же могу накинуть минус: кто-то может сказать что это checked exception 2.0.

    Потому что позволяет одновременно проверять насколько ошибок в более удобном "try-catch (when)".

    Но для меня это плюс, хоть и тоже самое, но читать такой код как в примере на презентации крайне приятно.


  1. ETOZHEKIM
    25.07.2025 14:24

    Удобно видеть, какие ошибки могут возникнуть при вызове метода. Не удобно, что они возвращаются функцией, обработка будет выглядеть странно. В try catch ты получаешь явно объект ошибки, думаю поэтому его и придумали. Как будто это шаг назад


    1. KivApple
      25.07.2025 14:24

      Должна быть функциональная обвязка, как map/map_err в Rust. Тогда можно будет строить цепочки обработчиков.


    1. gBear
      25.07.2025 14:24

      Не удобно, что они возвращаются функцией, ...

      ?! Как раз - наоборот. В кои-то веки, "обшибки" - по честному - часть сигнатуры ф-ции. С нормальной композицией, выводом и т.п.

      Если не понятно - User | NetworkError из примера - это полноценный тип. Эдакий AA union type, с единственным ограничением: AA это исключительно для подтипов Error.

      Оно - может быть - и не универсально. Зато - сильно утилитарно получается. Как в Haskell - может быть и хочется, но не получится по очевидным причинам. А как в Scala - ну мы видели, к чему "оно" приводит :-)

      ... обработка будет выглядеть странно

      Да ладно?! Даже самый очевидный (но не единственный) вариант обработки - через when выглядит очень органично.

      В try catch ты получаешь явно объект ошибки, думаю поэтому его и придумали.

      Тут у нас отдельный высший тип для ошибок - Error. Т.е. они ("обшибки") - by design - не пересекаются с подтипами Any? - куда уж явней "объект ошибки" получать?

      Единственное преимущество Throwable - это наличие stacktrace. Но и тут - если оно нам таки надо - всегда можно "раскрутиться" через !! оператор.

      Как будто это шаг назад

      В каком месте?!


  1. Dhwtj
    25.07.2025 14:24

    Теперь почти как в rust))

    Интеграция с фреймворками: Самый веский практический аргумент. Этот подход лучше всего работает в ядре бизнес-логики, изолированном от фреймворка. На границе (в контроллерах) пишется адаптер: when (result) { is Ok -> ..., is Err -> mapToHttpError(...) }. Пытаться тянуть Result до самого Spring MVC — плохая идея


    1. gBear
      25.07.2025 14:24

      Пытаться тянуть Result до самого Spring MVC — плохая идея

      "Прикол" в том, что тут как раз нет Result'а. Ни в терминах rust'а, ни в терминах kotlin'а.

      Result - это "эвфемизм" более общего Either. А Either - by design - скажем так, "хромает" в плане композиции "левой" своей части. Т.е. "левая" часть композиции выводится в что-то осмысленное только при прям очень сильных ограничениях, накладываемых на. Чего - по понятным причинам - делать вообще не хочется.

      Result - соответственно - "хромает" на "правую" свою часть.

      То, что предлагается ребятами из kotlin - позволяет с одной стороны - избежать такого рода "хромоты". А с другой - не скатится в AA-union-hell.

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

      Я к тому, что иметь - условный -

      private val <T:Any> (T|Error).responseEntity: ResponseEntity<out Any> = 
        get() -> when { ...}
      

      на уровне контроллера (или даже его пакета) - не выглядит, имхо, чем-то прям "ужос-ужос". Error и upper-bound - понятно, "в реальности" заменяются на какие-то более осмысленные type aliase'ы.

      А вот как раз "городьба" отдельной иерархии для передачи "типизации в ошибках" в контроллер - при условии наличия rich errors - будет выглядеть "немножко странно". Нет?


  1. gBear
    25.07.2025 14:24

    Сугубое имхо...

    Выглядит оно уже сейчас сильно "вкусно". Ребята из arrow - уже облизываются :-) Наконец-то нормальная "человечья" error composition "из коробки".

    Озвученные "сомнения" - в разрезе Kotlin - выглядят, мягко говоря, неубедительно.

    Яб таки дождался финализации. Возможно, оно действительно "негативненько" скажется на interop'е со стороны Java. Возможно (ну а вдруг), придумают как таки обойтись - в этой части - без "костыликов". Но это пока единственное, что хоть сколько-то "пугает".


  1. ilja903
    25.07.2025 14:24

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


  1. ris58h
    25.07.2025 14:24

    Есть где-нибудь нормальное краткое описание фичи? 45-минутное видео смотреть не горю желанием.

    Не понял как "кидается" ошибка. Через throw или через return? Могу ли я создать функцию, которая возвращает и кидает ошибку того же класса одновременно? Если да, то как понять был ли это успешный возврат или ошибка?