Переезжать и чинить придется все равно — сейчас или через n лет, когда будет следующий LTS. А вот начать экономить копейку из-за фич и оптимизаций, которые завозят в каждую промежуточную версию, лучше сейчас, чем через n лет. Вполне прагматично.
Имхо, если ты всю жизнь использовал винду, хочешь, чтобы все было как винде, и не хочешь разбираться с чем-то другим — просто используй дальше винду. Незачем разводить хейт про линукс только потому, что там не сработал какой-то твой виндовый сценарий использования, и ты потратил 0 времени, чтобы понять, почему это произошло. Ты не хотел, чтобы получилось, вот оно и не получилось.
Ну в режиме "жму на все кнопки, не хочу разбираться" проблемы будут не только с линуксом, а вообще с чем угодно.
Просто дайте себе отчет в том, что когда-то вы инвестировали довольно много времени, чтобы научиться что-то делать в винде. Почему в линуксе вы ожидаете мгновенного результата?
Всё это позволит писать данные в Manticore Search вместо Elasticsearch из Logstash, Fluentd, Beats и им подобных.
Возможно, тут как раз подходящее место, чтобы задать вопрос, который мучает меня довольно долго. Зачем нужно складывать логи приложений в системы полнотекстового поиска? Лично мне очень сложно представить себе реальный пример, когда для поиска по логам нужно что-то сложнее grep или его микросервис-аналога типа Grafana Loki.
С тестами-то понятно, можно любой из этих вариантов забороть. Но далеко не все проекты могуть похвастаться хорошим покрытием. Поэтому я здесь рассматриваю, что мог быть дать Optional сам по себе.
И да, как я уже указал, Optional вам при первом же чтении даст корректный NPE в рантайме. Потому что контракт, что все поля инициализированные, а необязательные — empty. Если мы ожидаем, что поле инициализировано, а оно оказалось null, NPE — это ровно то, что и должно произойти.
С @Nullable вы давите NPE (что вообще говоря не самоцель), но лишаетесь возможности различить ситуацию неинициализированно по ошибке / необязательно без значения, потому что и то и другое в этом случае — null.
class B {
@Nullable
public A field_a_a;
@Nullable
public A fielda_a_;
@Nullable
public A fielda_aa;
@Nullable
public A fieldaa_a;
}
class MyAbstractBlaBlaBlaBuilder {
public B build() {
var b = new B();
b.field_a_a = field_a_a;
b.fielda_a_ = fielda_a_;
b.fieldaa_a = fielda_aa;
b.fieldaa_a = fieldaa_a;
return b;
}
}
Если бы вместо @Nullable был Optional — вы бы на первом запуске отловили NPE. С @Nullable такая фигня может оставаться незамеченной довольно долго.
в отличие например от Option в Scala
В Scala с этим так же, как и в Java, разве что с линтерами получше.
Они там и не нужны — IDE автоматически выводит nullability из контрактов над методами или самого кода.
Нет никакой гарантии, что человек, который писал код использует такую IDE / настроил эти варнинги / обратил на них внимание. Еще меньше вероятность, что это сделает другой человек, который будет мержить это в мастер, ведь он скорее всего даже не откроет этот код в IDE.
Начиная с Java 9 ваш пример можно записать чуть более приятным образом с помощью ifPresentOrElse (и тут меня опередил комментатор выше). Конечно, для тех, кто привык к императивному коду, это будет менее читаемо, чем явный if. И в целом соглашусь, что Optional в императивном коде выглядит чужеродно.
С использованием nullable-аннотаций, на мой взгляд, есть несколько проблем:
нельзя отличить неинициализированное по ошибке поле от необязательного без значения
аннотации для локальных переменных выглядят сильно хуже, чем Optional
для null нет никаких средств композиции, для Optional есть flatMap
здесь был бы рад ошибиться, но вроде для Java нет какого-то стандартного статического анализатора nullable-аннотаций, чтобы можно было запускать на CI, например
Причина резкости оценок очень проста, и вы ее правильно указали. Вместо того, чтобы открыто и честно признать свои ошибки, написав статью в духе "Как не надо переходить на микросервисы" или "В каких случаях использование микросервисов не оправдано", чуваки наводят тень на плетень. Они пишут, что криво сделанные микросервисы хуже монолита, делая негативный акцент на микросервисах. А мне кажется, акцент надо делать на то, что они были криво сделаны. Криво сделанное что угодно хуже, чем нормально сделанное что-то другое.
Графана делает много крутых компонентов для мониторинга, которые вдохновляют. Я вот тоже не так давно настолько протащился от их системы централизованного сбора логов Loki, что начал делать свой первый серьезный open-source проект Logback appender, чтобы облегчить интеграцию Java-приложений с Loki. Цифры пока конечно скромнее, около 100 уникальных скачиваний за 2 месяца. Но я верю, что Loki наберет популярность в Java-комьюнити и хорошенько потеснит ELK, в том числе благодаря моему проекту :)
Избранная группа инженеров начала разбивать приложения на части, которые, по их мнению, соответствовали границам ответственности команд. Это было сделано для того, чтобы команды могли работать более независимо, деплоить независимо и повысить свою продуктивность.
Изначально был выбран неверный критерий разбивки монолита на микросервисы. Сам автор это признает.
По мере миграции команд на современную платформу сервисы, за которые отвечали эти команды, приходится передавать остающимся legacy-командам.
Не самый рациональный подход к миграции, когда не во время, а уже после после перехода на новую платформу надо все равно поддерживать старую.
Микросервисы — это не «микро». У них «правильный размер»
К чему это словоблудие про "правильный размер"? Неужели реально где-то есть взрослые люди, которые воспринимают технические термины буквально?
большинству технологий не требуется «независимое масштабирование»
Я несколько раз перечитал этот раздел, и вроде начало написано там правильно, а выводы — неверные. Такое ощущение, что они еще сами не до конца отрефлексировали, что причина их бед не в том, что независимое масштабирование нужно не везде (с этим сложно спорить), а в том, что они этого не поняли изначально. Т.е. они представляют свое непонимание технологии как ее недостаток.
Вот и весь рассказ. История одного конкретно взятого провала проектирования архитектуры и тех, кто за это отвечал. Зачем лить столько "воды" про Таноса и гордость за команду? Мне кажется, вместо Таноса тут больше бы подошла отсылка к фильму "Идиократия".
Ну да, про это как раз был мой первоначальный комментарий. Try просто класс, который все равно откуда придет, — это так только на прикладном уровне. На библиотечном уровне вы не будете подключать все подряд с целью минимизации зависимостей (и как следствие проблем у юзеров вашей библиотеки), поэтому там стандартная поставка имеет большое значение.
Хм… а я вот как-то стал очень разборчивым после всех этих депендеси хеллов в спарко-хадупе и прочих замечательных фрейморках, которые не могут без того, чтобы скачать пару гигабайт из мейвен централа, а потом на старте навернуться с чем-нибудь типа NoSuchMethodError. Лучше я возьму десять утилитарных zero-dependency либ, с каждой из которых легко разобраться в отдельности, чем одного огромного монстра, с котором непонятно что делать.
Очевидно же почему: чтобы не навязывать пользователю вашей библиотеки нерелевантных решений и чтобы избежать jar hell.
Вот сделали вы библиотеку для логгинга, которая зависит от project-reactor. Те, у кого в проекте нет реактивщины или, например Akka вместо project-reactor, — точно не оценят и будут искать другую библиотеку без этих нерелевантных зависимостей. Ваша библиотека не пройдет фильтр естественного отбора.
Понятно, что если у вас в описании к библиотеке написано, что она про FP и fault tolerance в Java, тащить vavr логично. Но я же говорю про библиотеки в общем случае.
Так что Scala в этом месте от Java почти не отличается.
Если вы автор библиотеки, например, вы скорее всего не будете тащить внешнюю зависимость от vavr или чего-то подобного. Соответственно, весь ваш арсенал будет ограничен тем, что есть в стандартном наборе. Так что отличия есть, и они весьма существенны.
Вряд ли выход Scala 3 привлечет этих людей обратно. Пока все выглядит так, что разработчики языка решили отойти и не конкурировать с котлином за нишу better java.
Примеры же просто демонстрируют новый синтаксис. Моноид, я думаю, выбрали потому, что удобно показывать разницу между методом, привязанным к типу (unit), и методом, привязанным к экземпляру (combine).
Если вас интересует какой-нибудь сравнительно простой пример прикладного использования тайпклассов, и при этом аллергия на теорию категорий, я бы рекомендовал посмотреть сюда. Это очень простая библиотека для чтения конфигов. В качестве упражнения, можно попробовать сделать такую же функциональность на "классическом ООП" без тайпклассов.
Хм… было бы интересно узнать, какие еще есть опции. Вот хочу я сделать библиотеку для сериализации JSON. Естественно, пользователи этой библиотеки могут захотеть сериализовать любой класс, который у них есть. Какие у меня есть варианты кроме наследования/рефлексии/оберток с одной стороны и тайпклассов с другой?
Переезжать и чинить придется все равно — сейчас или через n лет, когда будет следующий LTS. А вот начать экономить копейку из-за фич и оптимизаций, которые завозят в каждую промежуточную версию, лучше сейчас, чем через n лет. Вполне прагматично.
Имхо, если ты всю жизнь использовал винду, хочешь, чтобы все было как винде, и не хочешь разбираться с чем-то другим — просто используй дальше винду. Незачем разводить хейт про линукс только потому, что там не сработал какой-то твой виндовый сценарий использования, и ты потратил 0 времени, чтобы понять, почему это произошло. Ты не хотел, чтобы получилось, вот оно и не получилось.
Ну в режиме "жму на все кнопки, не хочу разбираться" проблемы будут не только с линуксом, а вообще с чем угодно.
Просто дайте себе отчет в том, что когда-то вы инвестировали довольно много времени, чтобы научиться что-то делать в винде. Почему в линуксе вы ожидаете мгновенного результата?
Спасибо за такой подробный и исчерпывающий ответ!
Возможно, тут как раз подходящее место, чтобы задать вопрос, который мучает меня довольно долго. Зачем нужно складывать логи приложений в системы полнотекстового поиска? Лично мне очень сложно представить себе реальный пример, когда для поиска по логам нужно что-то сложнее
grep
или его микросервис-аналога типа Grafana Loki.Тут надо уточнить, что получать деньги в Россию и страны СНГ через GitHub (пока еще?) нельзя.
С тестами-то понятно, можно любой из этих вариантов забороть. Но далеко не все проекты могуть похвастаться хорошим покрытием. Поэтому я здесь рассматриваю, что мог быть дать
Optional
сам по себе.И да, как я уже указал,
Optional
вам при первом же чтении даст корректный NPE в рантайме. Потому что контракт, что все поля инициализированные, а необязательные — empty. Если мы ожидаем, что поле инициализировано, а оно оказалосьnull
, NPE — это ровно то, что и должно произойти.С
@Nullable
вы давите NPE (что вообще говоря не самоцель), но лишаетесь возможности различить ситуацию неинициализированно по ошибке / необязательно без значения, потому что и то и другое в этом случае —null
.Поясню на примере:
Если бы вместо
@Nullable
былOptional
— вы бы на первом запуске отловили NPE. С@Nullable
такая фигня может оставаться незамеченной довольно долго.В Scala с этим так же, как и в Java, разве что с линтерами получше.
Нет никакой гарантии, что человек, который писал код использует такую IDE / настроил эти варнинги / обратил на них внимание. Еще меньше вероятность, что это сделает другой человек, который будет мержить это в мастер, ведь он скорее всего даже не откроет этот код в IDE.
Начиная с Java 9 ваш пример можно записать чуть более приятным образом с помощью
ifPresentOrElse
(и тут меня опередил комментатор выше). Конечно, для тех, кто привык к императивному коду, это будет менее читаемо, чем явныйif
. И в целом соглашусь, чтоOptional
в императивном коде выглядит чужеродно.С использованием nullable-аннотаций, на мой взгляд, есть несколько проблем:
Optional
null
нет никаких средств композиции, дляOptional
естьflatMap
Справедливости ради,
if
постоянно не надо писать. Можно:foo.map(f -> f.bar).map(b -> b.baz)
.Причина резкости оценок очень проста, и вы ее правильно указали. Вместо того, чтобы открыто и честно признать свои ошибки, написав статью в духе "Как не надо переходить на микросервисы" или "В каких случаях использование микросервисов не оправдано", чуваки наводят тень на плетень. Они пишут, что криво сделанные микросервисы хуже монолита, делая негативный акцент на микросервисах. А мне кажется, акцент надо делать на то, что они были криво сделаны. Криво сделанное что угодно хуже, чем нормально сделанное что-то другое.
Это очень круто, поздравляю!
Графана делает много крутых компонентов для мониторинга, которые вдохновляют. Я вот тоже не так давно настолько протащился от их системы централизованного сбора логов Loki, что начал делать свой первый серьезный open-source проект Logback appender, чтобы облегчить интеграцию Java-приложений с Loki. Цифры пока конечно скромнее, около 100 уникальных скачиваний за 2 месяца. Но я верю, что Loki наберет популярность в Java-комьюнити и хорошенько потеснит ELK, в том числе благодаря моему проекту :)
Попробую резюмировать.
Изначально был выбран неверный критерий разбивки монолита на микросервисы. Сам автор это признает.
Не самый рациональный подход к миграции, когда не во время, а уже после после перехода на новую платформу надо все равно поддерживать старую.
К чему это словоблудие про "правильный размер"? Неужели реально где-то есть взрослые люди, которые воспринимают технические термины буквально?
Я несколько раз перечитал этот раздел, и вроде начало написано там правильно, а выводы — неверные. Такое ощущение, что они еще сами не до конца отрефлексировали, что причина их бед не в том, что независимое масштабирование нужно не везде (с этим сложно спорить), а в том, что они этого не поняли изначально. Т.е. они представляют свое непонимание технологии как ее недостаток.
Вот и весь рассказ. История одного конкретно взятого провала проектирования архитектуры и тех, кто за это отвечал. Зачем лить столько "воды" про Таноса и гордость за команду? Мне кажется, вместо Таноса тут больше бы подошла отсылка к фильму "Идиократия".
Ну да, про это как раз был мой первоначальный комментарий.
Try
просто класс, который все равно откуда придет, — это так только на прикладном уровне. На библиотечном уровне вы не будете подключать все подряд с целью минимизации зависимостей (и как следствие проблем у юзеров вашей библиотеки), поэтому там стандартная поставка имеет большое значение.Хм… а я вот как-то стал очень разборчивым после всех этих депендеси хеллов в спарко-хадупе и прочих замечательных фрейморках, которые не могут без того, чтобы скачать пару гигабайт из мейвен централа, а потом на старте навернуться с чем-нибудь типа NoSuchMethodError. Лучше я возьму десять утилитарных zero-dependency либ, с каждой из которых легко разобраться в отдельности, чем одного огромного монстра, с котором непонятно что делать.
Очевидно же почему: чтобы не навязывать пользователю вашей библиотеки нерелевантных решений и чтобы избежать jar hell.
Вот сделали вы библиотеку для логгинга, которая зависит от project-reactor. Те, у кого в проекте нет реактивщины или, например Akka вместо project-reactor, — точно не оценят и будут искать другую библиотеку без этих нерелевантных зависимостей. Ваша библиотека не пройдет фильтр естественного отбора.
Понятно, что если у вас в описании к библиотеке написано, что она про FP и fault tolerance в Java, тащить vavr логично. Но я же говорю про библиотеки в общем случае.
Если вы автор библиотеки, например, вы скорее всего не будете тащить внешнюю зависимость от vavr или чего-то подобного. Соответственно, весь ваш арсенал будет ограничен тем, что есть в стандартном наборе. Так что отличия есть, и они весьма существенны.
Вряд ли выход Scala 3 привлечет этих людей обратно. Пока все выглядит так, что разработчики языка решили отойти и не конкурировать с котлином за нишу better java.
Примеры же просто демонстрируют новый синтаксис. Моноид, я думаю, выбрали потому, что удобно показывать разницу между методом, привязанным к типу (
unit
), и методом, привязанным к экземпляру (combine
).Если вас интересует какой-нибудь сравнительно простой пример прикладного использования тайпклассов, и при этом аллергия на теорию категорий, я бы рекомендовал посмотреть сюда. Это очень простая библиотека для чтения конфигов. В качестве упражнения, можно попробовать сделать такую же функциональность на "классическом ООП" без тайпклассов.
Хм… было бы интересно узнать, какие еще есть опции. Вот хочу я сделать библиотеку для сериализации JSON. Естественно, пользователи этой библиотеки могут захотеть сериализовать любой класс, который у них есть. Какие у меня есть варианты кроме наследования/рефлексии/оберток с одной стороны и тайпклассов с другой?