Команда 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)
ETOZHEKIM
25.07.2025 14:24Удобно видеть, какие ошибки могут возникнуть при вызове метода. Не удобно, что они возвращаются функцией, обработка будет выглядеть странно. В try catch ты получаешь явно объект ошибки, думаю поэтому его и придумали. Как будто это шаг назад
KivApple
25.07.2025 14:24Должна быть функциональная обвязка, как map/map_err в Rust. Тогда можно будет строить цепочки обработчиков.
gBear
25.07.2025 14:24Не удобно, что они возвращаются функцией, ...
?! Как раз - наоборот. В кои-то веки, "обшибки" - по честному - часть сигнатуры ф-ции. С нормальной композицией, выводом и т.п.
Если не понятно - User | NetworkError из примера - это полноценный тип. Эдакий AA union type, с единственным ограничением: AA это исключительно для подтипов Error.
Оно - может быть - и не универсально. Зато - сильно утилитарно получается. Как в Haskell - может быть и хочется, но не получится по очевидным причинам. А как в Scala - ну мы видели, к чему "оно" приводит :-)
... обработка будет выглядеть странно
Да ладно?! Даже самый очевидный (но не единственный) вариант обработки - через when выглядит очень органично.
В try catch ты получаешь явно объект ошибки, думаю поэтому его и придумали.
Тут у нас отдельный высший тип для ошибок - Error. Т.е. они ("обшибки") - by design - не пересекаются с подтипами Any? - куда уж явней "объект ошибки" получать?
Единственное преимущество Throwable - это наличие stacktrace. Но и тут - если оно нам таки надо - всегда можно "раскрутиться" через !! оператор.
Как будто это шаг назад
В каком месте?!
Dhwtj
25.07.2025 14:24Теперь почти как в rust))
Интеграция с фреймворками: Самый веский практический аргумент. Этот подход лучше всего работает в ядре бизнес-логики, изолированном от фреймворка. На границе (в контроллерах) пишется адаптер: when (result) { is Ok -> ..., is Err -> mapToHttpError(...) }. Пытаться тянуть Result до самого Spring MVC — плохая идея
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 - будет выглядеть "немножко странно". Нет?
gBear
25.07.2025 14:24Сугубое имхо...
Выглядит оно уже сейчас сильно "вкусно". Ребята из arrow - уже облизываются :-) Наконец-то нормальная "человечья" error composition "из коробки".
Озвученные "сомнения" - в разрезе Kotlin - выглядят, мягко говоря, неубедительно.
Яб таки дождался финализации. Возможно, оно действительно "негативненько" скажется на interop'е со стороны Java. Возможно (ну а вдруг), придумают как таки обойтись - в этой части - без "костыликов". Но это пока единственное, что хоть сколько-то "пугает".
ilja903
25.07.2025 14:24Какой-то недо typescript. Тулбокс для всего этого маленький, зачем возвращать ошибку если можно было бы возвращать разные классы например. Типов нет, а семантика как будто есть. Котлин стал не лучшей джавой, а недо сишарпом.
ris58h
25.07.2025 14:24Есть где-нибудь нормальное краткое описание фичи? 45-минутное видео смотреть не горю желанием.
Не понял как "кидается" ошибка. Через throw или через return? Могу ли я создать функцию, которая возвращает и кидает ошибку того же класса одновременно? Если да, то как понять был ли это успешный возврат или ошибка?
ExTalosDx
Spring могут и доработать.
Нет смысла спешить с мнением, кмк.
С моей точки зрения это более явный и более удобный optional. Только в разрезе не только null safety.
Так же могу накинуть минус: кто-то может сказать что это checked exception 2.0.
Потому что позволяет одновременно проверять насколько ошибок в более удобном "try-catch (when)".
Но для меня это плюс, хоть и тоже самое, но читать такой код как в примере на презентации крайне приятно.