Всем привет!
Если просто написать какой-то правильный тезис - скорее всего он забудется и на зайдет.
Поэтому проиллюстрирую тезис, даже два, случаем из моей практики.
Была задача сделать систему управления контентом. Она же CMS. Но вот беда - в организации, где я работал, такой системы не было. А так как мы являемся бизнес-подразделением - внедрить готовое решение своими силами было очень сложно, практически невозможно.
Что ж, будем искать - а что же есть подходящее. Нашел систему управления справочниками. Свои справочники заводить можно. UI для их редактирования есть. Есть ролевая модель для управления доступом. Есть даже версионность - можно изменения заводить в виде версий, включаемых на ПРОМ атомарно.
Да, не UI бедноват, но на безрыбье и рак рыба...
В общем тогда мне это показалось хорошей идей.
Сейчас считаю это одним из самых больших своих факапов как архитектора.
В чем основные проблемы:
1) выяснилось, что заведение прикладных справочников хоть и технически возможно, но по факту запрещено. Точнее разрешено только через отдельную процедуру согласования. Т.е. из справочников хотят таки сделать как ни банально бы это звучало справочники - систему хранения нормативной, редко меняющейся информации. И с этим аргументом сложно поспорить)
2) более того - не только у нас одних возникла задача ведения контента, в недрах нашей организации ведется работа над CMS. На тот момент она была в пилотной эксплуатации, но при большом желании можно даже было в этом пилоте поучаствовать.
3) самое главное - даже если бы мы внедрили нашу реализацию, то с очень большой вероятностью через год-два столкнулись бы с тем, что UI справочников не позволяет удобно настраивать контент, также, как это делается в CMS. А дорабатывать UI никто не будет, т.к. это же справочники.
В итоге через год команда перешла на ту самую CMS, уже после начала ее промышленной эксплуатации.
Выводы:
1) не надо использовать сервисы, утилиты, фреймворки нецелевым образом. Рано или поздно это аукнется. В данном случае я считаю - хорошо, что аукнулось рано)
2) не изобретайте велосипеды, используйте уже существующие) А они в 95% случаев уже есть.
#fuckup #projects #arch
Если просто написать какой-то правильный тезис - скорее всего он забудется и на зайдет.
Поэтому проиллюстрирую тезис, даже два, случаем из моей практики.
Была задача сделать систему управления контентом. Она же CMS. Но вот беда - в организации, где я работал, такой системы не было. А так как мы являемся бизнес-подразделением - внедрить готовое решение своими силами было очень сложно, практически невозможно.
Что ж, будем искать - а что же есть подходящее. Нашел систему управления справочниками. Свои справочники заводить можно. UI для их редактирования есть. Есть ролевая модель для управления доступом. Есть даже версионность - можно изменения заводить в виде версий, включаемых на ПРОМ атомарно.
Да, не UI бедноват, но на безрыбье и рак рыба...
В общем тогда мне это показалось хорошей идей.
Сейчас считаю это одним из самых больших своих факапов как архитектора.
В чем основные проблемы:
1) выяснилось, что заведение прикладных справочников хоть и технически возможно, но по факту запрещено. Точнее разрешено только через отдельную процедуру согласования. Т.е. из справочников хотят таки сделать как ни банально бы это звучало справочники - систему хранения нормативной, редко меняющейся информации. И с этим аргументом сложно поспорить)
2) более того - не только у нас одних возникла задача ведения контента, в недрах нашей организации ведется работа над CMS. На тот момент она была в пилотной эксплуатации, но при большом желании можно даже было в этом пилоте поучаствовать.
3) самое главное - даже если бы мы внедрили нашу реализацию, то с очень большой вероятностью через год-два столкнулись бы с тем, что UI справочников не позволяет удобно настраивать контент, также, как это делается в CMS. А дорабатывать UI никто не будет, т.к. это же справочники.
В итоге через год команда перешла на ту самую CMS, уже после начала ее промышленной эксплуатации.
Выводы:
1) не надо использовать сервисы, утилиты, фреймворки нецелевым образом. Рано или поздно это аукнется. В данном случае я считаю - хорошо, что аукнулось рано)
2) не изобретайте велосипеды, используйте уже существующие) А они в 95% случаев уже есть.
#fuckup #projects #arch
Всем привет!
Сегодняшний пост начну издалека. Распределенные системы обмениваются сообщениями. Каждое сообщение можно рассматривать как событие, требующее какой-то обработки и передачи дальше - в другую подобную систему или для хранения в БД. Т.об. мы получаем распределенную цепочку микросервисов, через которые проходит событие. Существуют т.наз. семантики доставки сообщений:
- at-most-once
- at-least-once
- exactly-once
at-most-once - максимум один раз, т.е. 0 или 1. Т.е. есть вероятность потерять и не обработать сообщение. Подходит для случаев, когда поток сообщений большой, используется для сбора статистики и потеря одного сообщения не критична. Например - статистика по кликам на сайте.
at-least-once - минимум один раз, т.е. 1 или более. Т.е. есть вероятность повторно отправить какое-то сообщение и, соответственно, обработать его дважды. Подходит для систем, где важно знать последнее значение. Пример: стоимость акции на сайте брокера. Или для систем, идемпотентно обрабатывающих входящие сообщения - https://habr.com/ru/companies/domclick/articles/779872/
exactly-once - строго один раз. Идеальный случай.
Да, система может поддерживать сразу несколько семантик, т.е. по сути иметь несколько режимов работы.
Самый интересный и сложный - это конечно exactly-once. Как с ним обстоят дела?
Например, его поддерживает Kafka - самая распространённая система потоковой передачи сообщений - https://docs.confluent.io/kafka/design/delivery-semantics.html
А также самые распространенные системы потоковой обработки данных:
Kafka Streams - https://kafka.apache.org/33/documentation/streams/core-concepts
Flink - https://flink.apache.org/2018/02/28/an-overview-of-end-to-end-exactly-once-processing-in-apache-flink-with-apache-kafka-too/
Spark - https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html
Кажется, что все хорошо. Но не совсем)
Если прочитать внимательнее, что пишут, например, создатели Kafka, то выяснится что exactly-once гарантируется на участке Producer - Kafka, но далее все зависит от того, как организована работа в Consumer-е. Вот неплохая переводная статья на эту тему от одного из создателей Kafka: https://habr.com/ru/companies/badoo/articles/333046/ // в статье detected american style самореклама, но все равно она неплохая)))
Создатели Flink тоже говорят, что
а) мы даем механизм для exactly-once - в случае сбоя мы откатим ваш обрабатывающий процесс на конкретное состояние (checkpoint), и вы четко будете знать, что это за состояние - его метку времени, что было до и после него, но
б) что делать уже обработанными записями, находящимися после восстановленного состояния - разбирайтесь сами
в) возможность вернуться к сообщениям, на которые указывает checkpoint - тоже на стороне разработчика. В случае Kafka это чаще всего элементарно - сообщения не удаляются сразу после чтения из топика, а если это MQ или сетевой сокет...
г) а еще можно рассмотреть кейс, если кто-то обновил хранилище, где хранится состояние
д) или если в функции потоковой обработки используются какие-то внешние вызовы, которые сломаются на повторах после отката...
Но по большому счету это частности. Основная проблема - системы типа Kafka или Flink могут обеспечить exactly-once на какой-то небольшой части вашей микросервисной системы. Как обеспечить ее на всей системе - в любом случае задача архитектора и разработчика.
Подсказка: наличие operationId (traceId), идемпотентность, транзакции там где это возможно, докаты и наконец админка для ручного разбора инцидентов если не помогли все предыдущие варианты.
#streaming #kafka #flink #arch #microservices #exactly_once
Сегодняшний пост начну издалека. Распределенные системы обмениваются сообщениями. Каждое сообщение можно рассматривать как событие, требующее какой-то обработки и передачи дальше - в другую подобную систему или для хранения в БД. Т.об. мы получаем распределенную цепочку микросервисов, через которые проходит событие. Существуют т.наз. семантики доставки сообщений:
- at-most-once
- at-least-once
- exactly-once
at-most-once - максимум один раз, т.е. 0 или 1. Т.е. есть вероятность потерять и не обработать сообщение. Подходит для случаев, когда поток сообщений большой, используется для сбора статистики и потеря одного сообщения не критична. Например - статистика по кликам на сайте.
at-least-once - минимум один раз, т.е. 1 или более. Т.е. есть вероятность повторно отправить какое-то сообщение и, соответственно, обработать его дважды. Подходит для систем, где важно знать последнее значение. Пример: стоимость акции на сайте брокера. Или для систем, идемпотентно обрабатывающих входящие сообщения - https://habr.com/ru/companies/domclick/articles/779872/
exactly-once - строго один раз. Идеальный случай.
Да, система может поддерживать сразу несколько семантик, т.е. по сути иметь несколько режимов работы.
Самый интересный и сложный - это конечно exactly-once. Как с ним обстоят дела?
Например, его поддерживает Kafka - самая распространённая система потоковой передачи сообщений - https://docs.confluent.io/kafka/design/delivery-semantics.html
А также самые распространенные системы потоковой обработки данных:
Kafka Streams - https://kafka.apache.org/33/documentation/streams/core-concepts
Flink - https://flink.apache.org/2018/02/28/an-overview-of-end-to-end-exactly-once-processing-in-apache-flink-with-apache-kafka-too/
Spark - https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html
Кажется, что все хорошо. Но не совсем)
Если прочитать внимательнее, что пишут, например, создатели Kafka, то выяснится что exactly-once гарантируется на участке Producer - Kafka, но далее все зависит от того, как организована работа в Consumer-е. Вот неплохая переводная статья на эту тему от одного из создателей Kafka: https://habr.com/ru/companies/badoo/articles/333046/ // в статье detected american style самореклама, но все равно она неплохая)))
Создатели Flink тоже говорят, что
а) мы даем механизм для exactly-once - в случае сбоя мы откатим ваш обрабатывающий процесс на конкретное состояние (checkpoint), и вы четко будете знать, что это за состояние - его метку времени, что было до и после него, но
б) что делать уже обработанными записями, находящимися после восстановленного состояния - разбирайтесь сами
в) возможность вернуться к сообщениям, на которые указывает checkpoint - тоже на стороне разработчика. В случае Kafka это чаще всего элементарно - сообщения не удаляются сразу после чтения из топика, а если это MQ или сетевой сокет...
г) а еще можно рассмотреть кейс, если кто-то обновил хранилище, где хранится состояние
д) или если в функции потоковой обработки используются какие-то внешние вызовы, которые сломаются на повторах после отката...
Но по большому счету это частности. Основная проблема - системы типа Kafka или Flink могут обеспечить exactly-once на какой-то небольшой части вашей микросервисной системы. Как обеспечить ее на всей системе - в любом случае задача архитектора и разработчика.
Подсказка: наличие operationId (traceId), идемпотентность, транзакции там где это возможно, докаты и наконец админка для ручного разбора инцидентов если не помогли все предыдущие варианты.
#streaming #kafka #flink #arch #microservices #exactly_once
Хабр
Идемпотентность: больше, чем кажется
Друзья, всем привет! Идемпотентность в проектировании API — не просто формальность. Это свойство, часто рассматриваемое как способ получения одинакового ответа на повторяющийся запрос, на самом деле...
Всем привет в новом году!
Предположу - многие знают о том, что для Data Science, ML, DL, AI в основном используют Python. Я уже подымал эту тему тут https://t.me/javaKotlinDevOps/142
Хотел бы подчеркнуть важность экосистемы - в Python было создано так много ML\DL\NLP библиотек, что догнать его другим экосистемам стало очень сложно.
А тут недавно и ChatGPT появился, что привело к взрывному росту популярности ML и AI ... Ну так вот - уже летом этого года началась разработка 2 альтернатив для работы с GPT моделями в Java.
1) langchain4j https://github.com/langchain4j/langchain4j Характерно, что проект сделан на основе одноименной Python библиотеки. Поддерживается в Quarkus.
2) конечно же Spring в виде Spring AI - https://docs.spring.io/spring-ai/reference
Оба проекта находятся в начальной стадии своего развития, версии вида 0.х, обновления выходят часто.
Еще важный момент - оба проекта имеют коннекторы к популярным GPT провайдерам - OpenAI (ChatGPT), Azure OpenAI...
Вот тут описан элементарный пример работы со Spring AI - https://habr.com/ru/articles/784128
А вот тут можно почитать про основные понятия и примеры кода для полноценной работы с ML моделями:
https://www.javaadvent.com/2023/12/java-and-the-ai-frontier-leveraging-modern-tools-and-techniques-for-machine-learning.html
#ai #java #ml #libraries
Предположу - многие знают о том, что для Data Science, ML, DL, AI в основном используют Python. Я уже подымал эту тему тут https://t.me/javaKotlinDevOps/142
Хотел бы подчеркнуть важность экосистемы - в Python было создано так много ML\DL\NLP библиотек, что догнать его другим экосистемам стало очень сложно.
А тут недавно и ChatGPT появился, что привело к взрывному росту популярности ML и AI ... Ну так вот - уже летом этого года началась разработка 2 альтернатив для работы с GPT моделями в Java.
1) langchain4j https://github.com/langchain4j/langchain4j Характерно, что проект сделан на основе одноименной Python библиотеки. Поддерживается в Quarkus.
2) конечно же Spring в виде Spring AI - https://docs.spring.io/spring-ai/reference
Оба проекта находятся в начальной стадии своего развития, версии вида 0.х, обновления выходят часто.
Еще важный момент - оба проекта имеют коннекторы к популярным GPT провайдерам - OpenAI (ChatGPT), Azure OpenAI...
Вот тут описан элементарный пример работы со Spring AI - https://habr.com/ru/articles/784128
А вот тут можно почитать про основные понятия и примеры кода для полноценной работы с ML моделями:
https://www.javaadvent.com/2023/12/java-and-the-ai-frontier-leveraging-modern-tools-and-techniques-for-machine-learning.html
#ai #java #ml #libraries
Telegram
(java || kotlin) && devOps
Всем привет! Возвращаясь к теме Machine Learning. Если погуглить эту тему, то в первых сторонах будет Python и библиотеки на Python. Почему не Java - вот тут попытка дать ответ: https://habr.com/ru/companies/piter/articles/429596/ Согласен с тем, что для…
Всем привет!
Сегодня расскажу про технологию native image.
Стандартная схема работы JVM приложения такая:
1) компилятор превращает исходники в байт-код
2) байт-код запускается на JVM
3) в процессе работы JVM анализирует использование байт-кода и при необходимости оптимизирует его, включая компиляцию в бинарное представление для конкретной процессорной архитектуры. И основные оптимизации надо отметить происходят именно здесь, а не при первичной компиляции. Еще важный момент - классы\библиотеки подгружаются в память не обязательно при старте приложения, а по мере использования. Все это называется JIT - Just in time компиляция. Влиять на нее можно с помощью ряда флагов запуска Java приложения - -server, -client.
Плюс такого подхода - JVM позволяет в 90% случаев игнорировать, на каком железе запускается Java приложение. Минус - долгий старт Java приложения плюс время для "разогрева" и выхода на рабочий режим.
Но с другой стороны с развитием Docker мы и так можем игнорировать особенности железа и ОС на хост-сервере, главное, чтобы там можно было запустить Docker. И наконец кроме долгого старта и разогрева собственно JVM у нас как правило есть Spring с кучей модулей, число которых растет, и в итоге время старта типичного Spring Boot приложения доходит до совсем неприличных величин.
Альтернатива - AOT - Ahead-of-Time compilation. В этом случае мы компилируем исходники в бинарный код в момент первичной компиляции. Причем как собственно приложение, так и JVM и все JAR. Получается такой native image монолит. Проект называется GraalVM https://www.graalvm.org/, официально поддерживается Oracle. Есть open-source версия, основанная на OpenJDK.
Плюс этого подхода - скорость запуска. Это критически важно в облаках, т.к. k8s может "случайно" рестартовать под при изменении конфигурации железа или настроек Deployment. Еще будет выигрыш в скорости обработки запросов, т.к. не тратится CPU и память в runtime на JIT компиляцию.
Какие минусы?
1) невозможна динамическая\ленивая загрузка библиотек\плагинов, classpath фиксируется в момент компиляции. К слову - у этого ограничения есть и плюсы, сложнее эксплуатировать уязвимости типа log4j injection - см. https://t.me/javaKotlinDevOps/4
2) вопрос - откуда компилятор узнает, какой код ему нужно добавить в наш native монолит? Ответ: он идет от метода main. Соответственно, код который явно не вызывается, а, например, вызывается через рефлексию, он не увидит. Соответственно, никакой рефлексии в ПРОМ коде. Что, надо сказать, в целом правильно)
3) аналогично просто так не заработает магия Spring, основанная на рефлексии и динамических прокси. Из чего следует, что мало добавить в Spring приложение AOT компилятор - нужно дорабатывать сам Spring, что и было сделано в Spring Boot 3.2. Другие фреймворки также придется дорабатывать. Например, Mockito до сих пор не работает в native image. Справедливости ради тут причина такая же, как в анекдоте про неуловимого ковбоя Джо - не нужен Mockito в native image)
4) если продолжить про Spring - загрузка бинов по условию: @ConditionalOnProperty, @Profile - тоже не заработает. Нужно указывать при сборке необходимый профиль, чтобы уже при компиляции нужные бины были обнаружены и добавлены в дистрибутив.
5) еще вопрос - но ведь среднее Java приложение + библиотеки + JVM = миллионы строк кода, что будет с компиляцией? Ответ - компиляция будет долгой, до 10 минут на spring boot hello world. Поэтому в документации Spring прямо сказано, что хотя Spring поддерживает запуск тестов в native image - делать так нужно только для интеграционных тестов, лучше на CI, а модульные запускать по старинке, т.к. тут критична скорость получения результата.
#jvm #performance #native_image #spring #docker #buildpacks #cloud #java_start_boost
Сегодня расскажу про технологию native image.
Стандартная схема работы JVM приложения такая:
1) компилятор превращает исходники в байт-код
2) байт-код запускается на JVM
3) в процессе работы JVM анализирует использование байт-кода и при необходимости оптимизирует его, включая компиляцию в бинарное представление для конкретной процессорной архитектуры. И основные оптимизации надо отметить происходят именно здесь, а не при первичной компиляции. Еще важный момент - классы\библиотеки подгружаются в память не обязательно при старте приложения, а по мере использования. Все это называется JIT - Just in time компиляция. Влиять на нее можно с помощью ряда флагов запуска Java приложения - -server, -client.
Плюс такого подхода - JVM позволяет в 90% случаев игнорировать, на каком железе запускается Java приложение. Минус - долгий старт Java приложения плюс время для "разогрева" и выхода на рабочий режим.
Но с другой стороны с развитием Docker мы и так можем игнорировать особенности железа и ОС на хост-сервере, главное, чтобы там можно было запустить Docker. И наконец кроме долгого старта и разогрева собственно JVM у нас как правило есть Spring с кучей модулей, число которых растет, и в итоге время старта типичного Spring Boot приложения доходит до совсем неприличных величин.
Альтернатива - AOT - Ahead-of-Time compilation. В этом случае мы компилируем исходники в бинарный код в момент первичной компиляции. Причем как собственно приложение, так и JVM и все JAR. Получается такой native image монолит. Проект называется GraalVM https://www.graalvm.org/, официально поддерживается Oracle. Есть open-source версия, основанная на OpenJDK.
Плюс этого подхода - скорость запуска. Это критически важно в облаках, т.к. k8s может "случайно" рестартовать под при изменении конфигурации железа или настроек Deployment. Еще будет выигрыш в скорости обработки запросов, т.к. не тратится CPU и память в runtime на JIT компиляцию.
Какие минусы?
1) невозможна динамическая\ленивая загрузка библиотек\плагинов, classpath фиксируется в момент компиляции. К слову - у этого ограничения есть и плюсы, сложнее эксплуатировать уязвимости типа log4j injection - см. https://t.me/javaKotlinDevOps/4
2) вопрос - откуда компилятор узнает, какой код ему нужно добавить в наш native монолит? Ответ: он идет от метода main. Соответственно, код который явно не вызывается, а, например, вызывается через рефлексию, он не увидит. Соответственно, никакой рефлексии в ПРОМ коде. Что, надо сказать, в целом правильно)
3) аналогично просто так не заработает магия Spring, основанная на рефлексии и динамических прокси. Из чего следует, что мало добавить в Spring приложение AOT компилятор - нужно дорабатывать сам Spring, что и было сделано в Spring Boot 3.2. Другие фреймворки также придется дорабатывать. Например, Mockito до сих пор не работает в native image. Справедливости ради тут причина такая же, как в анекдоте про неуловимого ковбоя Джо - не нужен Mockito в native image)
4) если продолжить про Spring - загрузка бинов по условию: @ConditionalOnProperty, @Profile - тоже не заработает. Нужно указывать при сборке необходимый профиль, чтобы уже при компиляции нужные бины были обнаружены и добавлены в дистрибутив.
5) еще вопрос - но ведь среднее Java приложение + библиотеки + JVM = миллионы строк кода, что будет с компиляцией? Ответ - компиляция будет долгой, до 10 минут на spring boot hello world. Поэтому в документации Spring прямо сказано, что хотя Spring поддерживает запуск тестов в native image - делать так нужно только для интеграционных тестов, лучше на CI, а модульные запускать по старинке, т.к. тут критична скорость получения результата.
#jvm #performance #native_image #spring #docker #buildpacks #cloud #java_start_boost
Есть еще ряд интересных моментов. Я расскажу про них на примере Spring Boot native image.
Для борьбы с тем, что часть кода недостижима если идти от точки входа (метод main), есть два инструмента.
1) специальный tracing агент, который можно подключить к приложению, и он будет в runtime логировать такие скрытые вызовы. https://www.graalvm.org/22.3/reference-manual/native-image/metadata/AutomaticMetadataCollection/
2) далее можно создать т.наз. hints - подсказки AOT компилятору, что включить в native image, из того, что он не нашел сам - https://www.graalvm.org/latest/reference-manual/native-image/metadata/ Собственно, большая доля в адаптации фреймворка типа Spring для native image - подготовка таких hints, https://docs.spring.io/spring-boot/docs/3.2.1/reference/html/native-image.html
А что делать если в момент сборки еще не ясно - нужен native image или нет? Или нужны обе версии? Нет проблем - можно совместить оба режима JIT и AOT и создать артефакт, Spring Boot Executable Jar, с байткодом и всеми необходимыми для native image метаданными. И собрать из него native image позже в DevOps pipeline при необходимости.
Для Spring Boot есть два режима сборки. Основной - Native Image Using Buildpacks, в котором в итоге получается docker образ. Для него нужен только Docker на машине-сборщике. И т.наз. Native Build Tools - нужно устанавливать дистрибутив GraalVM, содержащий эти tools, в итоге получается бинарник для железа, на котором происходит сборка.
Итого - штука полезная, но только если вас категорически не устраивает время запуска приложения и все используемые вами фреймворки поддерживают native image.
#jvm #performance #native_image #spring #docker #buildpacks #cloud #startup_time
Для борьбы с тем, что часть кода недостижима если идти от точки входа (метод main), есть два инструмента.
1) специальный tracing агент, который можно подключить к приложению, и он будет в runtime логировать такие скрытые вызовы. https://www.graalvm.org/22.3/reference-manual/native-image/metadata/AutomaticMetadataCollection/
2) далее можно создать т.наз. hints - подсказки AOT компилятору, что включить в native image, из того, что он не нашел сам - https://www.graalvm.org/latest/reference-manual/native-image/metadata/ Собственно, большая доля в адаптации фреймворка типа Spring для native image - подготовка таких hints, https://docs.spring.io/spring-boot/docs/3.2.1/reference/html/native-image.html
А что делать если в момент сборки еще не ясно - нужен native image или нет? Или нужны обе версии? Нет проблем - можно совместить оба режима JIT и AOT и создать артефакт, Spring Boot Executable Jar, с байткодом и всеми необходимыми для native image метаданными. И собрать из него native image позже в DevOps pipeline при необходимости.
Для Spring Boot есть два режима сборки. Основной - Native Image Using Buildpacks, в котором в итоге получается docker образ. Для него нужен только Docker на машине-сборщике. И т.наз. Native Build Tools - нужно устанавливать дистрибутив GraalVM, содержащий эти tools, в итоге получается бинарник для железа, на котором происходит сборка.
Итого - штука полезная, но только если вас категорически не устраивает время запуска приложения и все используемые вами фреймворки поддерживают native image.
#jvm #performance #native_image #spring #docker #buildpacks #cloud #startup_time
www.graalvm.org
GraalVM
GraalVM is an advanced JDK with ahead-of-time Native Image compilation.
Всем привет!
Является ли ООП - объекто-ориентированное программирование - чем-то плохим? Ответ - ну нет. Благодаря объектам мы можем воспроизвести в коде реальные бизнес-объекты, а это стирает барьеры между заказчиком, аналитиком и разработчиком. Конечно, есть альтернативные подходы, например, функциональный. Или если посмотреть в другую сторону - декларативный. Но ООП жил, жив и будет жить)
Является ли архитектура сервиса, состоящая из нескольких слоев абстракции, какой-то излишней или неправильной? Нет, это стандартный подход в архитектуре - вводить новые уровни абстракции. Даже шутка на этот счет есть) Spring, Hibernate и куча других библиотек - это тоже новые слои абстракции. Цель введения нового слоя - упростить использование какой-то библиотеки или адаптировать ее для новой предметной области.
Что плавно подводит нас к паттернам Адаптер, Прокси и иже с ними https://t.me/javaKotlinDevOps/124 Паттерны - штука полезная, и да, я снова об этом уже писал https://t.me/javaKotlinDevOps/52 )))
И последний (риторический) вопрос: являются ли принципы DRY - Don't Repeat Yourself - и Single Responsibility вредными? Наоборот, они делают код более устойчивым к изменениям и упрощают его изучение.
Но почему же тогда в мире ПО не редкость встретить вот такую фабрику фабрик фабрик: https://factoryfactoryfactory.net ?
Ответ: ООП, принципы и паттерны не заменяют здравый смысл и чувство меры)
#oop #patterns #craftmanship #dev_compromises
Является ли ООП - объекто-ориентированное программирование - чем-то плохим? Ответ - ну нет. Благодаря объектам мы можем воспроизвести в коде реальные бизнес-объекты, а это стирает барьеры между заказчиком, аналитиком и разработчиком. Конечно, есть альтернативные подходы, например, функциональный. Или если посмотреть в другую сторону - декларативный. Но ООП жил, жив и будет жить)
Является ли архитектура сервиса, состоящая из нескольких слоев абстракции, какой-то излишней или неправильной? Нет, это стандартный подход в архитектуре - вводить новые уровни абстракции. Даже шутка на этот счет есть) Spring, Hibernate и куча других библиотек - это тоже новые слои абстракции. Цель введения нового слоя - упростить использование какой-то библиотеки или адаптировать ее для новой предметной области.
Что плавно подводит нас к паттернам Адаптер, Прокси и иже с ними https://t.me/javaKotlinDevOps/124 Паттерны - штука полезная, и да, я снова об этом уже писал https://t.me/javaKotlinDevOps/52 )))
И последний (риторический) вопрос: являются ли принципы DRY - Don't Repeat Yourself - и Single Responsibility вредными? Наоборот, они делают код более устойчивым к изменениям и упрощают его изучение.
Но почему же тогда в мире ПО не редкость встретить вот такую фабрику фабрик фабрик: https://factoryfactoryfactory.net ?
Ответ: ООП, принципы и паттерны не заменяют здравый смысл и чувство меры)
#oop #patterns #craftmanship #dev_compromises
Enterprise Craftsmanship
Domain model purity vs. domain model completeness (DDD Trilemma)
I’ve been meaning to write this article for a long time and, finally, here it is: the topic of domain model purity versus domain model completeness.
Всем привет!
Если кто не знает - у PostgreSQL есть такая интересная фича, как механизм NOTIFY/LISTEN https://www.postgresql.org/docs/current/sql-notify.html
А вот пример его использования: https://eax.me/postgresql-notify-listen/
По сути мы получаем очередь средствами СУБД. Чем она лучше той же Kafka - тем, что выполняется в контексте транзакции СУБД, и т.об. событие не будет отправлено потребителям до тех пор, пока транзакция не закоммитится. Это логично, более того, для событий, возникающих внутри БД, может быть критично. Что интересно: если LISTEN также находится внутри транзакции - событие тоже будет доставлено только после ее фиксации. Это не так очевидно, но причина аналогична NOTIFY - если транзакция с LISTEN откатится, а в коде клиентского приложения, подписанного на события, будут выполнены какие-то действия вне контура БД - откатить их не получится.
Самое простое применение NOTIFY/LISTEN - инвалидация кэша при изменении таблицы в БД.
Но такую "очередь" в БД можно использовать для произвольных событий, не связанных с БД. Вот подробный пример как интегрировать Spring Integration и NOTIFY\LISTEN https://www.baeldung.com/spring-receiving-postresql-push-notifications
Зачем? Ну например если Kafka разворачивать не хочется, PostgreSQL уже есть, нужна персистентность, и есть множество подписчиков в разных процессах\на разных серверах.
Какие у данного механизма ограничения:
1) производительность PostgreSQL на порядки меньше Kafka
2) есть ограничения на размер сообщения и общий размер очереди
3) все сообщения хранятся в одной очереди pg_notify на диске, которая может стать узким местом.
4) очередь pg_notify в отличие от данных в таблицах не устойчива к падениям СУБД, при перезапуске она очищается, WAL не используется
5) каждая подписка на события забирает один коннект к СУБД из пула
Использовать ли события PostgreSQL? Пилот или небольшая нагрузка + допустимость потерь сообщений - почему бы нет. Остальные случаи надо смотреть детальнее и конечно же проводить НТ
#postgresql #messaging
Если кто не знает - у PostgreSQL есть такая интересная фича, как механизм NOTIFY/LISTEN https://www.postgresql.org/docs/current/sql-notify.html
А вот пример его использования: https://eax.me/postgresql-notify-listen/
По сути мы получаем очередь средствами СУБД. Чем она лучше той же Kafka - тем, что выполняется в контексте транзакции СУБД, и т.об. событие не будет отправлено потребителям до тех пор, пока транзакция не закоммитится. Это логично, более того, для событий, возникающих внутри БД, может быть критично. Что интересно: если LISTEN также находится внутри транзакции - событие тоже будет доставлено только после ее фиксации. Это не так очевидно, но причина аналогична NOTIFY - если транзакция с LISTEN откатится, а в коде клиентского приложения, подписанного на события, будут выполнены какие-то действия вне контура БД - откатить их не получится.
Самое простое применение NOTIFY/LISTEN - инвалидация кэша при изменении таблицы в БД.
Но такую "очередь" в БД можно использовать для произвольных событий, не связанных с БД. Вот подробный пример как интегрировать Spring Integration и NOTIFY\LISTEN https://www.baeldung.com/spring-receiving-postresql-push-notifications
Зачем? Ну например если Kafka разворачивать не хочется, PostgreSQL уже есть, нужна персистентность, и есть множество подписчиков в разных процессах\на разных серверах.
Какие у данного механизма ограничения:
1) производительность PostgreSQL на порядки меньше Kafka
2) есть ограничения на размер сообщения и общий размер очереди
3) все сообщения хранятся в одной очереди pg_notify на диске, которая может стать узким местом.
4) очередь pg_notify в отличие от данных в таблицах не устойчива к падениям СУБД, при перезапуске она очищается, WAL не используется
5) каждая подписка на события забирает один коннект к СУБД из пула
Использовать ли события PostgreSQL? Пилот или небольшая нагрузка + допустимость потерь сообщений - почему бы нет. Остальные случаи надо смотреть детальнее и конечно же проводить НТ
#postgresql #messaging
PostgreSQL Documentation
NOTIFY
NOTIFY NOTIFY — generate a notification Synopsis NOTIFY channel [ , payload ] Description The NOTIFY command sends a notification …
Всем привет!
Хорошая статья про String Templates в Java 21 https://blog.jetbrains.com/idea/2023/11/string-templates-in-java-why-should-you-care/
Самое интересное в шаблонах Java вот что.
Лично у меня часто складывается впечатление, что Java копирует языковые фишки, они же синтаксический сахар, у Kotlin, Scala, C# и других языков. Копия иногда такая же по функционалу, иногда хуже. Повторюсь - именно это впечатление, т.к. строго говоря появление фичи А после фичи Б не значит, что она скопирована.
Но в данном случае строковые шаблоны получились хоть и не такими синтаксически простыми, но более крутыми по функционалу.
Выглядят они так:
STR."some string \{varWithMeaningfulName}"
Да, странно что \ вместо $, но самое важное здесь то, что шаблон является методом у некого класса. Это класс StringTemplate.Processor<String, RuntimeException>. В Java core есть ряд его стандартных реализаций, но самое главное - можно сделать свою. В статье выше есть парочка интересных примеров.
Как по мне - крутая фича! Жалко, что дотянули до 21-й Java, и то пока preview)))
#java #kotlin #strings #java_new_version
Хорошая статья про String Templates в Java 21 https://blog.jetbrains.com/idea/2023/11/string-templates-in-java-why-should-you-care/
Самое интересное в шаблонах Java вот что.
Лично у меня часто складывается впечатление, что Java копирует языковые фишки, они же синтаксический сахар, у Kotlin, Scala, C# и других языков. Копия иногда такая же по функционалу, иногда хуже. Повторюсь - именно это впечатление, т.к. строго говоря появление фичи А после фичи Б не значит, что она скопирована.
Но в данном случае строковые шаблоны получились хоть и не такими синтаксически простыми, но более крутыми по функционалу.
Выглядят они так:
STR."some string \{varWithMeaningfulName}"
Да, странно что \ вместо $, но самое важное здесь то, что шаблон является методом у некого класса. Это класс StringTemplate.Processor<String, RuntimeException>. В Java core есть ряд его стандартных реализаций, но самое главное - можно сделать свою. В статье выше есть парочка интересных примеров.
Как по мне - крутая фича! Жалко, что дотянули до 21-й Java, и то пока preview)))
#java #kotlin #strings #java_new_version
The JetBrains Blog
String Templates in Java - why should you care? | The IntelliJ IDEA Blog
TLDR; The existing String concatenation options are difficult to work with and could be error prone. String Templates (a preview feature introduced in Java 21) greatly improves how we create strings i
Всем привет!
Недавно я хвалил Java, пришло время поругать) Для начала вот фактура:
https://habr.com/ru/articles/676852/
Если вкратце: в Java есть стримы и есть проверяемые исключения. Если первые совместить со вторыми - код стримов из компактного и красивого становится ужасным. Да, технически все работает, но теряется смысл стримов - компактный код в функциональном стиле.
С одной стороны можно сказать - да и фиг с ними, с этими проверяемыми исключениями. Да, их можно не использовать в своем коде. Я, например, так и делаю, т.к. недостатки проверяемых исключений перевешивают их преимущества, см. https://t.me/javaKotlinDevOps/205.
Но во-первых они могут приходить к нам из внешних библиотек, а во-вторых основной принцип Java, ее сила - максимальная совместимость. Именно совместимостью оправдывается скорость развития Java. А тут получается про нее забыли. И что важно - streams появились в Java 8. Недавно вышла Java 21. Функционал проверяемых исключений хоть особо не развивается, но и не стал deprecated. Значит условный Try из статьи выше должен быть в стандартной библиотеке Java.
#java #exceptions #checked_exceptions #java_streams
Недавно я хвалил Java, пришло время поругать) Для начала вот фактура:
https://habr.com/ru/articles/676852/
Если вкратце: в Java есть стримы и есть проверяемые исключения. Если первые совместить со вторыми - код стримов из компактного и красивого становится ужасным. Да, технически все работает, но теряется смысл стримов - компактный код в функциональном стиле.
С одной стороны можно сказать - да и фиг с ними, с этими проверяемыми исключениями. Да, их можно не использовать в своем коде. Я, например, так и делаю, т.к. недостатки проверяемых исключений перевешивают их преимущества, см. https://t.me/javaKotlinDevOps/205.
Но во-первых они могут приходить к нам из внешних библиотек, а во-вторых основной принцип Java, ее сила - максимальная совместимость. Именно совместимостью оправдывается скорость развития Java. А тут получается про нее забыли. И что важно - streams появились в Java 8. Недавно вышла Java 21. Функционал проверяемых исключений хоть особо не развивается, но и не стал deprecated. Значит условный Try из статьи выше должен быть в стандартной библиотеке Java.
#java #exceptions #checked_exceptions #java_streams
Хабр
Обработка исключений в Java в функциональном стиле
Обработка исключений в Java в функциональном стиле В данной статье автор предоставит информацию о собственной библиотеке для обработки исключений (Exception) в функциональном стиле. Предпосылки В Java...
Всем привет!
Недавно набрел на интересную статью - https://enterprisecraftsmanship.com/posts/domain-model-purity-completeness
Настоятельно рекомендую ее прочитать, но как всегда вкратце перескажу)
В крупную клетку любой нормально спроектированный сервис включает в себя контроллер (порт в терминологии гексагональной архитекторы), модель и адаптеры.
Вся логика должна быть в модели. Но что делать, если предварительная проверка данных требует обращения к внешней системе (БД), и при этом построение запроса для проверки - это тоже бизнес-логика.
Варианта предлагается три:
1) внести ее в модель, скрыть обращение к данным за интерфейсами, но в любом случае в итоге наше ядро (модель) лезет в БД, что плохо. В первую очередь плохо концептуально, а если "спустится на землю" - сложнее писать модульные тесты, увеличиваются риски "загрязнения" ядра. Т.е. следующие поколения разработчиков видя, что из модели вызывается СУБД, скажут - а что, так можно было?) И будут тянуть в модель другие внешние зависимости. Теория еще такая есть, разбитых окон. К слову - автор статьи также автор отличной книги о модульном тестировании, я о ней уже писал https://t.me/javaKotlinDevOps/50, возможно поэтому ему данный вариант не нравится
2) оставить часть логики в контроллере. Но тогда получается, что логика размазана по двум слоям
3) заранее загрузить нужные данные в ядро. Допустимо, но только для каких-то маленьких и редко меняющихся справочников, типа регионов. Т.е. только в отдельных случаях.
В итоге у нас компромисс между полнотой модели, строгостью соблюдения архитектурных принципов и скоростью работы.
Что тут интересно - главная идея статьи не о том, как сделать правильно, а про то, что разработка ПО - это искусство компромиссов.
Теорема CAP, упоминаемая в статье, к слову о том же. Единственного правильного для всех случаев жизни решения нет. Увы(
Еще одно дополнение. В теории возможен еще один вариант, расширяющий пространство компромиссов. Он немного "наркоманский" в том плане, что усложняет систему.
Предположим мы сделаем интерфейс валидатора в модели. Правила валидации будет задавать декларативно и хранить в модели, не привязывая их к конкретному хранилищу. Т.е. код контроллера вместо:
validateLogic()
callModel()
превращается в:
val rules = getValidateRulesFromModel()
val request = buildValidateRequest()
validate(request)
callModel()
Сам же и покритикую. Задача вроде решена, но дополнительного кода потребуется много. Второй минус, тоже важный - последовательность вызовов неочевидна. Если новый разработчик придет в команду - очень может быть он скажет "WTF" и захочет переписать. Как решение этой проблемы могу предложить описывать алгоритм в документации к коду или аналитике. Документация не нужна с "говорящим" кодом, но тут как раз исключение. Но сложность понимания кода в любом случае повышается.
Т.об. в пространство компромиссов мы вводим еще один параметр - сложность. Полнота модели, целостность архитектуры, скорость и сложность.
#arch #unittests #dev_compromises
Недавно набрел на интересную статью - https://enterprisecraftsmanship.com/posts/domain-model-purity-completeness
Настоятельно рекомендую ее прочитать, но как всегда вкратце перескажу)
В крупную клетку любой нормально спроектированный сервис включает в себя контроллер (порт в терминологии гексагональной архитекторы), модель и адаптеры.
Вся логика должна быть в модели. Но что делать, если предварительная проверка данных требует обращения к внешней системе (БД), и при этом построение запроса для проверки - это тоже бизнес-логика.
Варианта предлагается три:
1) внести ее в модель, скрыть обращение к данным за интерфейсами, но в любом случае в итоге наше ядро (модель) лезет в БД, что плохо. В первую очередь плохо концептуально, а если "спустится на землю" - сложнее писать модульные тесты, увеличиваются риски "загрязнения" ядра. Т.е. следующие поколения разработчиков видя, что из модели вызывается СУБД, скажут - а что, так можно было?) И будут тянуть в модель другие внешние зависимости. Теория еще такая есть, разбитых окон. К слову - автор статьи также автор отличной книги о модульном тестировании, я о ней уже писал https://t.me/javaKotlinDevOps/50, возможно поэтому ему данный вариант не нравится
2) оставить часть логики в контроллере. Но тогда получается, что логика размазана по двум слоям
3) заранее загрузить нужные данные в ядро. Допустимо, но только для каких-то маленьких и редко меняющихся справочников, типа регионов. Т.е. только в отдельных случаях.
В итоге у нас компромисс между полнотой модели, строгостью соблюдения архитектурных принципов и скоростью работы.
Что тут интересно - главная идея статьи не о том, как сделать правильно, а про то, что разработка ПО - это искусство компромиссов.
Теорема CAP, упоминаемая в статье, к слову о том же. Единственного правильного для всех случаев жизни решения нет. Увы(
Еще одно дополнение. В теории возможен еще один вариант, расширяющий пространство компромиссов. Он немного "наркоманский" в том плане, что усложняет систему.
Предположим мы сделаем интерфейс валидатора в модели. Правила валидации будет задавать декларативно и хранить в модели, не привязывая их к конкретному хранилищу. Т.е. код контроллера вместо:
validateLogic()
callModel()
превращается в:
val rules = getValidateRulesFromModel()
val request = buildValidateRequest()
validate(request)
callModel()
Сам же и покритикую. Задача вроде решена, но дополнительного кода потребуется много. Второй минус, тоже важный - последовательность вызовов неочевидна. Если новый разработчик придет в команду - очень может быть он скажет "WTF" и захочет переписать. Как решение этой проблемы могу предложить описывать алгоритм в документации к коду или аналитике. Документация не нужна с "говорящим" кодом, но тут как раз исключение. Но сложность понимания кода в любом случае повышается.
Т.об. в пространство компромиссов мы вводим еще один параметр - сложность. Полнота модели, целостность архитектуры, скорость и сложность.
#arch #unittests #dev_compromises
Enterprise Craftsmanship
Domain model purity vs. domain model completeness (DDD Trilemma)
I’ve been meaning to write this article for a long time and, finally, here it is: the topic of domain model purity versus domain model completeness.
Всем привет!
В продолжение темы про скорость и аккуратность внедрения новых фичей в Java. Хороший пример - pattern matching, который наконец окончательно внедрили в в Java 21. Почему хороший пример - потому что до этого было 4 Preview начиная с 17 Java) Всего 2.5 года получается)
Вот тут детали: https://www.infoq.com/news/2023/07/tranforming-java-pattern
Кажется долго, да.
А если я уточню, что речь идет про pattern matching for switch.
switch (response) {
case null -> { }
case String s when s.equalsIgnoreCase("YES") -> System.out.println("You got it");
case String s when s.equalsIgnoreCase("NO") -> System.out.println("Shame");
case String s -> System.out.println("Sorry?");
}
А до этого был Pattern matching для instance of https://www.baeldung.com/java-16-new-features#pattern-matching-for-instanceof-jep-394 в Java 16?
if (x instance of String) {
x.trim()
}
А кроме того параллельно pattern matching для record в 19 Java https://www.baeldung.com/java-19-record-patterns
Double result = switch (object) {
case Location(var name, GPSPoint(var latitude, var longitude)) -> latitude;
default -> 0.0;
};
P.S. Но история еще не закончена)
См. описание последнего внедренного JEP, тот самый из Java 21, раздел Future work https://openjdk.org/jeps/441 -
работа с примитивами, деконструкция произвольных классов, а не только record, сложные условия в case блоке
P.P.S. Но Kotlin обогнали https://youtrack.jetbrains.com/issue/KT-186 )))
Со Scala вроде паритет, но нужны эксперты
#java
В продолжение темы про скорость и аккуратность внедрения новых фичей в Java. Хороший пример - pattern matching, который наконец окончательно внедрили в в Java 21. Почему хороший пример - потому что до этого было 4 Preview начиная с 17 Java) Всего 2.5 года получается)
Вот тут детали: https://www.infoq.com/news/2023/07/tranforming-java-pattern
Кажется долго, да.
А если я уточню, что речь идет про pattern matching for switch.
switch (response) {
case null -> { }
case String s when s.equalsIgnoreCase("YES") -> System.out.println("You got it");
case String s when s.equalsIgnoreCase("NO") -> System.out.println("Shame");
case String s -> System.out.println("Sorry?");
}
А до этого был Pattern matching для instance of https://www.baeldung.com/java-16-new-features#pattern-matching-for-instanceof-jep-394 в Java 16?
if (x instance of String) {
x.trim()
}
А кроме того параллельно pattern matching для record в 19 Java https://www.baeldung.com/java-19-record-patterns
Double result = switch (object) {
case Location(var name, GPSPoint(var latitude, var longitude)) -> latitude;
default -> 0.0;
};
P.S. Но история еще не закончена)
См. описание последнего внедренного JEP, тот самый из Java 21, раздел Future work https://openjdk.org/jeps/441 -
работа с примитивами, деконструкция произвольных классов, а не только record, сложные условия в case блоке
P.P.S. Но Kotlin обогнали https://youtrack.jetbrains.com/issue/KT-186 )))
Со Scala вроде паритет, но нужны эксперты
#java
InfoQ
JEP 441: Transforming Java with Pattern Matching for switch
JEP 441, Pattern Matching for the switch, is completed in JDK 21, following four previews in JDKs 17-20 (JEPs 406, 420, 427, 433). It finalizes the feature, addressing feedback from previews, and enhances the language with pattern matching for switch expressions…
Всем привет!
Есть такая проблема в больших компаниях, а-ка "кровавый enterprise" - сложность внедрения новых технологий.
Я про СУБД, в частности noSQL, брокеры сообщений, стриминговые платформы, системы мониторинга, DevOps pipeline, облачные решения и т.д.
Объясняется это тем, что любую внешнюю систему кто-то должен поддерживать, а сейчас в компании этим никто не занимается.
И это на самом деле сильный аргумент, т.к. в больших компаниях разработка и поддержка разделены. А это значит разработчик может затащить новую систему, разворачивать, настраивать и поддерживать которую будет кто-то еще. Более того, если говорить о крупных компаниях - сопровождение там может делиться на прикладное, канальное и инфраструктурное. За СУБД и очереди отвечает последнее.
Что же тут можно сделать?
Просто запретить все новое - вариант очевидно плохой.
А вот снизить "боль" разработчиков, пытающихся внедрить что-то новое, можно так:
1) вести открытый реестр разрешенных технологий. Открытый - значит информацию о нем не нужно искать через корпоративных архитекторов, секретные ссылки и т.д
2) около каждой системы должно быть описание - чем она сильна, какие альтернативы рассматривались и почему в конченом итоге была выбрана именно она
3) должна быть процедура предложений по внедрению новых решений, доступная любой команде
4) в этой процедуре должны быть описаны необходимые для внедрения условия. Тут видится два варианта:
а) внедрение поддерживает множество команд, с конкретными сроками, тогда компания сама "покупает" поддержку - создает команду сопровождения внутри или берет внешних подрядчиков.
б) команда, предложившая новую технологию, сама начинает отвечать за ее поддержку. Как она будет совмещать это со своей текущей деятельностью, потребуется ли ее перевод в инфраструктурные, возможно ее разделение на 2 части - вопрос договоренностей. Ну и да, инициатива имеет инициатора, к этому нужно быть готовым)
5) должны существовать открытые канала для обсуждения новых технологий. Чаты, рассылки, база знаний
Я не изучал, как этот вопрос решается в Agile, но кажется, что должен решаться именно так. В рамках подхода снизу-вверх.
Ну а плохой вариант - когда список технологий спускается откуда-то сверху, назовем это место башней из слоновой кости) А вопросы почему именно это и можно ли что-то добавить подвисают в воздухе, т.к. не понятно, кому их вообще задавать.
#arch #technology
Есть такая проблема в больших компаниях, а-ка "кровавый enterprise" - сложность внедрения новых технологий.
Я про СУБД, в частности noSQL, брокеры сообщений, стриминговые платформы, системы мониторинга, DevOps pipeline, облачные решения и т.д.
Объясняется это тем, что любую внешнюю систему кто-то должен поддерживать, а сейчас в компании этим никто не занимается.
И это на самом деле сильный аргумент, т.к. в больших компаниях разработка и поддержка разделены. А это значит разработчик может затащить новую систему, разворачивать, настраивать и поддерживать которую будет кто-то еще. Более того, если говорить о крупных компаниях - сопровождение там может делиться на прикладное, канальное и инфраструктурное. За СУБД и очереди отвечает последнее.
Что же тут можно сделать?
Просто запретить все новое - вариант очевидно плохой.
А вот снизить "боль" разработчиков, пытающихся внедрить что-то новое, можно так:
1) вести открытый реестр разрешенных технологий. Открытый - значит информацию о нем не нужно искать через корпоративных архитекторов, секретные ссылки и т.д
2) около каждой системы должно быть описание - чем она сильна, какие альтернативы рассматривались и почему в конченом итоге была выбрана именно она
3) должна быть процедура предложений по внедрению новых решений, доступная любой команде
4) в этой процедуре должны быть описаны необходимые для внедрения условия. Тут видится два варианта:
а) внедрение поддерживает множество команд, с конкретными сроками, тогда компания сама "покупает" поддержку - создает команду сопровождения внутри или берет внешних подрядчиков.
б) команда, предложившая новую технологию, сама начинает отвечать за ее поддержку. Как она будет совмещать это со своей текущей деятельностью, потребуется ли ее перевод в инфраструктурные, возможно ее разделение на 2 части - вопрос договоренностей. Ну и да, инициатива имеет инициатора, к этому нужно быть готовым)
5) должны существовать открытые канала для обсуждения новых технологий. Чаты, рассылки, база знаний
Я не изучал, как этот вопрос решается в Agile, но кажется, что должен решаться именно так. В рамках подхода снизу-вверх.
Ну а плохой вариант - когда список технологий спускается откуда-то сверху, назовем это место башней из слоновой кости) А вопросы почему именно это и можно ли что-то добавить подвисают в воздухе, т.к. не понятно, кому их вообще задавать.
#arch #technology
Всем привет!
В развитие прошлого поста предлагаю поговорить про большие компании и новые языки программирования.
С одной стороны тут нет противоречия разработка-сопровождение.
Если говорить про JVM или .NET языки - т.е. случай, когда есть виртуальная машина между приложением и ОС - то при смене языка ничего в плане сопровождения не меняется. Случаи, когда новый язык сгенерирует какой-то неработающий или неоптимальный байт-код я рассмотрю далее.
А если даже язык не JVM\.NET - мы живем в мире победившего Docker и тут все еще лучше: сопровождению остается провалидировать базовый образ, который к нам пришел от создателей языка, и\или выставить требования, что там должно быть.
Может возникнуть вопрос - а что делать, если готового базового образа нет?
Я бы его перефразировал - а что, если язык настолько редкий или новый, что его создатели забыли\не доросли до Docker.
Это вопрос к тем, кто занимается наймом. HR или Tech Lead\ИТ лидер. От них нужен анализ рынка для того, чтобы ответить на вопрос: что будет, если разработчик, предложивший новый перспективный язык, уйдет из компании? Насколько такие разработчики дороже?
Кроме анализа рынка тут может помочь https://www.tiobe.com/tiobe-index/, в частности подсказать тренд - растет или падает популярность языка.
И третий критерий фильтрации языков - насколько язык сложен для обучения. Тут поможет экспертное мнение Team Lead\Tech Lead.
По первому критерию для сферической компании в вакууме я бы отмел, к примеру, Scala. И как ни обидно для Scala - по-третьему тоже. Все же Scala - это немного brainfuck)
Почему для сферической компании в вакууме - есть компании в РФ, где Scala живет давно, для них очевидно этот аргумент не актуален.
Да и по сложности обучения все не так однозначно - если в компании принцип: "Отбираем лучших", - то Scala как раз может быть позитивным фильтром)
По второму критерию можно отфильтровать Ruby, пик его популярности пройден.
Итого - согласовывать переход на новый язык программирования нужно, но основной вопрос здесь в легкости\стоимости найма разработчиков и простоте обучения для новичков. К сопровождению или корпоративной архитектуре он отношения не имеет.
Да, возвращаясь к вопросу про неработающий байт-код. Если язык на рынке давно, то вероятность такого события пренебрежимо мала. А чтобы ее уменьшить еще сильнее есть древнее правило - не надо обновляться на новые версии в первых рядах, стоит подождать хотя бы месяц-другой. И чтобы добить эту тему - возражение "мы не сможем оказывать вам поддержку (консультации) для языка xyz" - некорректно. Т.к. если язык распространен - поддержку окажет документация, stackoverflow, habr, Yandex\google, или даже ChatGPT
#jvm #language #scala
В развитие прошлого поста предлагаю поговорить про большие компании и новые языки программирования.
С одной стороны тут нет противоречия разработка-сопровождение.
Если говорить про JVM или .NET языки - т.е. случай, когда есть виртуальная машина между приложением и ОС - то при смене языка ничего в плане сопровождения не меняется. Случаи, когда новый язык сгенерирует какой-то неработающий или неоптимальный байт-код я рассмотрю далее.
А если даже язык не JVM\.NET - мы живем в мире победившего Docker и тут все еще лучше: сопровождению остается провалидировать базовый образ, который к нам пришел от создателей языка, и\или выставить требования, что там должно быть.
Может возникнуть вопрос - а что делать, если готового базового образа нет?
Я бы его перефразировал - а что, если язык настолько редкий или новый, что его создатели забыли\не доросли до Docker.
Это вопрос к тем, кто занимается наймом. HR или Tech Lead\ИТ лидер. От них нужен анализ рынка для того, чтобы ответить на вопрос: что будет, если разработчик, предложивший новый перспективный язык, уйдет из компании? Насколько такие разработчики дороже?
Кроме анализа рынка тут может помочь https://www.tiobe.com/tiobe-index/, в частности подсказать тренд - растет или падает популярность языка.
И третий критерий фильтрации языков - насколько язык сложен для обучения. Тут поможет экспертное мнение Team Lead\Tech Lead.
По первому критерию для сферической компании в вакууме я бы отмел, к примеру, Scala. И как ни обидно для Scala - по-третьему тоже. Все же Scala - это немного brainfuck)
Почему для сферической компании в вакууме - есть компании в РФ, где Scala живет давно, для них очевидно этот аргумент не актуален.
Да и по сложности обучения все не так однозначно - если в компании принцип: "Отбираем лучших", - то Scala как раз может быть позитивным фильтром)
По второму критерию можно отфильтровать Ruby, пик его популярности пройден.
Итого - согласовывать переход на новый язык программирования нужно, но основной вопрос здесь в легкости\стоимости найма разработчиков и простоте обучения для новичков. К сопровождению или корпоративной архитектуре он отношения не имеет.
Да, возвращаясь к вопросу про неработающий байт-код. Если язык на рынке давно, то вероятность такого события пренебрежимо мала. А чтобы ее уменьшить еще сильнее есть древнее правило - не надо обновляться на новые версии в первых рядах, стоит подождать хотя бы месяц-другой. И чтобы добить эту тему - возражение "мы не сможем оказывать вам поддержку (консультации) для языка xyz" - некорректно. Т.к. если язык распространен - поддержку окажет документация, stackoverflow, habr, Yandex\google, или даже ChatGPT
#jvm #language #scala
Всем привет!
Уже прошел примерно год как появился ChatGPT. Его активно пытаются применить везде - для решения бизнес-задач, автоматизации рутины и конечно же написания кода. Какую же рутину можно автоматизировать в разработке? А например такую - https://github.com/Nutlope/aicommits
#git #commit
Уже прошел примерно год как появился ChatGPT. Его активно пытаются применить везде - для решения бизнес-задач, автоматизации рутины и конечно же написания кода. Какую же рутину можно автоматизировать в разработке? А например такую - https://github.com/Nutlope/aicommits
#git #commit
GitHub
GitHub - Nutlope/aicommits: A CLI that writes your git commit messages for you with AI
A CLI that writes your git commit messages for you with AI - Nutlope/aicommits
image_2024-02-05_18-02-46.png
173.1 KB
Всем привет!
Не так давно появилась такая технология как Function as a Service или сокращено FaaS. Ее цель - затащить в облако все кроме написания кода. Сегодня нашел неплохую картинку, иллюстрирующую ее на примере сервисов Google.
Слева - это как раз FaaS. Т.е. мы отдаем на откуп облачного провайдера железо и виртуальные машины (instance), управление контейнерами, runtime (JDK,.NET framework), веб сервер и даже веб контроллер (single-endpointed). И хоть тут это не указано - CI\CD pipeline.
Плюсы:
1) сопровождение и DevOps уходят на аутсорс.
Минусы:
1) vendor lock, хотя стандарт разрабатывается,
2) платим за время работы кода и платим достаточно дорого,
3) не подходит для выполняющихся долго или висящих в памяти постоянно процессов
#cloud #faas
Не так давно появилась такая технология как Function as a Service или сокращено FaaS. Ее цель - затащить в облако все кроме написания кода. Сегодня нашел неплохую картинку, иллюстрирующую ее на примере сервисов Google.
Слева - это как раз FaaS. Т.е. мы отдаем на откуп облачного провайдера железо и виртуальные машины (instance), управление контейнерами, runtime (JDK,.NET framework), веб сервер и даже веб контроллер (single-endpointed). И хоть тут это не указано - CI\CD pipeline.
Плюсы:
1) сопровождение и DevOps уходят на аутсорс.
Минусы:
1) vendor lock, хотя стандарт разрабатывается,
2) платим за время работы кода и платим достаточно дорого,
3) не подходит для выполняющихся долго или висящих в памяти постоянно процессов
#cloud #faas
Всем привет!
В продолжение темы облачных сервисов - https://telegra.ph/Urovni-pogruzheniya-v-oblako-02-06
#cloud #k8s #openshift #spring
В продолжение темы облачных сервисов - https://telegra.ph/Urovni-pogruzheniya-v-oblako-02-06
#cloud #k8s #openshift #spring
Telegraph
Уровни погружения в облако
В предыдущем посте я говорил про FaaS - Function as a Service. Это максимальная степень передачи инфраструктуры разработки в облако - все кроме кода. Больше - это только пользоваться готовым сервисом - Software as a Service. Захотелось посмотреть - а что…
Всем привет!
Периодически я вижу в коде такую ситуацию.
Есть метод с 5 параметрами, надо передать ему еще 5. Если сделать это "в лоб" - и выглядит ужасно, и SonarQube ругается.
Что можно сделать?
Ну, например, завести объект XYZParams. По сути DTO, используемую в двух классах - вызывающем и вызываемом.
Мне это решение изначально не понравилось, но я не сразу смог объяснить чем именно) Самое простое объяснение - выглядит как lifehack. Требует SonarQube меньше параметров - вот тебе один параметр. Но объяснение мне не нравилось, стал копать дальше.
И как часто бывает - набрел на статью метра, почему это плохо.
https://martinfowler.com/bliki/LocalDTO.html
Советую почитать и вообще занести этот сайт себе в закладки.
Но если вкратце:
1) DTO - как Transfer объект был создан для передачи по сети, когда велики накладные расходы. Ну или для удовлетворения требований безопасности
2) новая DTO - это всегда маппинг, как минимум односторонний. Маппинг - потенциальный источник ошибок при изменениях
3) и добавка от меня: если доменную модель со всех сторон обложить DTO и маппингами - а зачем она тогда вообще нужна?)
Решение же проблемы может быть два:
1) завести подходящие доменные объекты и передавать их, а не одиночные параметры
2) реорганизовать логику, чтобы одному методу не требовалось столько данных
#arch #dto
Периодически я вижу в коде такую ситуацию.
Есть метод с 5 параметрами, надо передать ему еще 5. Если сделать это "в лоб" - и выглядит ужасно, и SonarQube ругается.
Что можно сделать?
Ну, например, завести объект XYZParams. По сути DTO, используемую в двух классах - вызывающем и вызываемом.
Мне это решение изначально не понравилось, но я не сразу смог объяснить чем именно) Самое простое объяснение - выглядит как lifehack. Требует SonarQube меньше параметров - вот тебе один параметр. Но объяснение мне не нравилось, стал копать дальше.
И как часто бывает - набрел на статью метра, почему это плохо.
https://martinfowler.com/bliki/LocalDTO.html
Советую почитать и вообще занести этот сайт себе в закладки.
Но если вкратце:
1) DTO - как Transfer объект был создан для передачи по сети, когда велики накладные расходы. Ну или для удовлетворения требований безопасности
2) новая DTO - это всегда маппинг, как минимум односторонний. Маппинг - потенциальный источник ошибок при изменениях
3) и добавка от меня: если доменную модель со всех сторон обложить DTO и маппингами - а зачем она тогда вообще нужна?)
Решение же проблемы может быть два:
1) завести подходящие доменные объекты и передавать их, а не одиночные параметры
2) реорганизовать логику, чтобы одному методу не требовалось столько данных
#arch #dto
martinfowler.com
bliki: Local D T O
a bliki entry for Local D T O
Всем привет!
Вытяну ссылку из комментариев сюда: https://youtu.be/hUzpe73Oa3g?si=c_dY1YU2Cc_F8YiY
Хороший ролик про границы применимости паттерна Value Object.
На всякий случай - что такое Value Object и чем он отличается от DTO - https://matthiasnoback.nl/2022/09/is-it-a-dto-or-a-value-object/
Также стоит отметить, что данный паттерн является одним из основных в DDD - Domain Driven Development.
А по видео у меня такой краткий вывод - а точнее два:
1) у любого паттерна есть своя область применения
2) когда вы придумали некий хитрый лайфхак, перед тем как реализовывать его в коде стоит взять паузу и подумать.
Насколько он понятен для новичка? Не усложнит ли он код? Насколько? Не станет ли поддержка такого кода сложнее? Не добавит ли он в вашу модель "уязвимость", позволяющую использовать классы и методы не так, как задумывалось изначально?
Часто лучше написать больше простого кода, чем меньше, но неочевидного и допускающего неверное использование. И далее либо разбить этот код на микросервисы, либо на модули - например, см. мой пост про Modulith - https://t.me/javaKotlinDevOps/143
#patterns #arch #dev_compromises
Вытяну ссылку из комментариев сюда: https://youtu.be/hUzpe73Oa3g?si=c_dY1YU2Cc_F8YiY
Хороший ролик про границы применимости паттерна Value Object.
На всякий случай - что такое Value Object и чем он отличается от DTO - https://matthiasnoback.nl/2022/09/is-it-a-dto-or-a-value-object/
Также стоит отметить, что данный паттерн является одним из основных в DDD - Domain Driven Development.
А по видео у меня такой краткий вывод - а точнее два:
1) у любого паттерна есть своя область применения
2) когда вы придумали некий хитрый лайфхак, перед тем как реализовывать его в коде стоит взять паузу и подумать.
Насколько он понятен для новичка? Не усложнит ли он код? Насколько? Не станет ли поддержка такого кода сложнее? Не добавит ли он в вашу модель "уязвимость", позволяющую использовать классы и методы не так, как задумывалось изначально?
Часто лучше написать больше простого кода, чем меньше, но неочевидного и допускающего неверное использование. И далее либо разбить этот код на микросервисы, либо на модули - например, см. мой пост про Modulith - https://t.me/javaKotlinDevOps/143
#patterns #arch #dev_compromises
YouTube
Семен Киреков — Spring, Hibernate, паттерн Value Object и границы его применения
Ближайшая конференция — JPoint 2025, 3–4 апреля (Москва + трансляция).
Подробности и билеты: https://jrg.su/T2zfbS
— —
При разработке ПО всегда заходит вопрос о валидации и корректной работе с данными. Если выполнить бизнес-операцию с неверными входными…
Подробности и билеты: https://jrg.su/T2zfbS
— —
При разработке ПО всегда заходит вопрос о валидации и корректной работе с данными. Если выполнить бизнес-операцию с неверными входными…
Всем привет!
Есть такой интересный вопрос - можно ли поместить СУБД в облако?
Если отвечать на него строго технически - да, можно, для этого в k8s есть специальные типы объектов - StatefulSet https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/ и PersistentVolume
https://kubernetes.io/docs/concepts/storage/persistent-volumes/
которые обеспечивают ряд требуемых для СУБД характеристик:
1) подключенное хранилище не удаляется при каждой остановке пода
2) экземпляр StatefulSet имеет постоянное имя
3) процедура масштабирования StatefulSet в обе стороны последовательна - поды добавляются\удаляются по одному
Более того, в документации кубера есть пример с раскатыванием MySQL https://kubernetes.io/docs/tasks/run-application/run-replicated-stateful-application
Причем с возможностью автоматического масштабирования на чтение. Пример на самом деле меня впечатлил: сотня строк манифестов yaml - и БД уезжает в облако.
Так ли все хорошо?
Конечно же нет)
Для начала я бы задал типичный "менеджерский" вопрос - какую задачу решаем?
Первый момент - горизонтальное масштабирование на чтение можно обеспечить для любой СУБД. Как в облаке, так и без облака. На запись - только для NoSQL хранилищ с поддержкой шардирования.
Второй момент: сопровождение СУБД - это не просто гарантия наличия работоспособного инстанса на ПРОМ. Это еще и периодические бэкапы, откаты на резервную копию, начальное заполнение БД данными, заполнение "горячего" кэша, выбор нового master если старому плохо (leader election), тонкие настройки репликации и многое другое, чего я просто не знаю. А знают - хорошие DBA. Т.е. получается даже если мы поместим обычную СУБД в облако - DBA все равно нужен. Причем DBA, умеющий в облако, а это, кажется, редкая птица)
Т.е. все плохо? И снова нет. Если есть потребность отдать БД "на аутсорс" в облако, то выход - использование хранилищ от облачных провайдеров, спроектированных для работы в облаке. Там все вышеописанные тонкости будут учтены. Примеры:
1) Amazon Aurora https://habr.com/ru/companies/oleg-bunin/articles/471686/
2) YDB https://cloud.yandex.ru/ru/services/ydb
3) Azure Cosmos DB for PostgreSQL https://learn.microsoft.com/en-us/azure/cosmos-db/postgresql/introduction
Ну или хотя бы использование заточенных под работу в облаке СУБД. Вот пара примеров, первыми попавшихся мне на глаза:
1) Tarantool https://habr.com/ru/companies/vk/articles/533308/
2) CockroachDB https://www.cockroachlabs.com/docs/v23.2/deploy-cockroachdb-with-kubernetes
Как можно заметить, в обоих случаях есть специальный оператор k8s для управления кластером.
P.S. Тема сложная, интересная, поэтому думаю это не последняя статья.
#k8s #storage #cloud #rdbms
Есть такой интересный вопрос - можно ли поместить СУБД в облако?
Если отвечать на него строго технически - да, можно, для этого в k8s есть специальные типы объектов - StatefulSet https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/ и PersistentVolume
https://kubernetes.io/docs/concepts/storage/persistent-volumes/
которые обеспечивают ряд требуемых для СУБД характеристик:
1) подключенное хранилище не удаляется при каждой остановке пода
2) экземпляр StatefulSet имеет постоянное имя
3) процедура масштабирования StatefulSet в обе стороны последовательна - поды добавляются\удаляются по одному
Более того, в документации кубера есть пример с раскатыванием MySQL https://kubernetes.io/docs/tasks/run-application/run-replicated-stateful-application
Причем с возможностью автоматического масштабирования на чтение. Пример на самом деле меня впечатлил: сотня строк манифестов yaml - и БД уезжает в облако.
Так ли все хорошо?
Конечно же нет)
Для начала я бы задал типичный "менеджерский" вопрос - какую задачу решаем?
Первый момент - горизонтальное масштабирование на чтение можно обеспечить для любой СУБД. Как в облаке, так и без облака. На запись - только для NoSQL хранилищ с поддержкой шардирования.
Второй момент: сопровождение СУБД - это не просто гарантия наличия работоспособного инстанса на ПРОМ. Это еще и периодические бэкапы, откаты на резервную копию, начальное заполнение БД данными, заполнение "горячего" кэша, выбор нового master если старому плохо (leader election), тонкие настройки репликации и многое другое, чего я просто не знаю. А знают - хорошие DBA. Т.е. получается даже если мы поместим обычную СУБД в облако - DBA все равно нужен. Причем DBA, умеющий в облако, а это, кажется, редкая птица)
Т.е. все плохо? И снова нет. Если есть потребность отдать БД "на аутсорс" в облако, то выход - использование хранилищ от облачных провайдеров, спроектированных для работы в облаке. Там все вышеописанные тонкости будут учтены. Примеры:
1) Amazon Aurora https://habr.com/ru/companies/oleg-bunin/articles/471686/
2) YDB https://cloud.yandex.ru/ru/services/ydb
3) Azure Cosmos DB for PostgreSQL https://learn.microsoft.com/en-us/azure/cosmos-db/postgresql/introduction
Ну или хотя бы использование заточенных под работу в облаке СУБД. Вот пара примеров, первыми попавшихся мне на глаза:
1) Tarantool https://habr.com/ru/companies/vk/articles/533308/
2) CockroachDB https://www.cockroachlabs.com/docs/v23.2/deploy-cockroachdb-with-kubernetes
Как можно заметить, в обоих случаях есть специальный оператор k8s для управления кластером.
P.S. Тема сложная, интересная, поэтому думаю это не последняя статья.
#k8s #storage #cloud #rdbms
Kubernetes
StatefulSets
A StatefulSet runs a group of Pods, and maintains a sticky identity for each of those Pods. This is useful for managing applications that need persistent storage or a stable, unique network identity.
Всем привет!
Есть интересная тема - инструменты сборки для JVM проектов. А в рамках нее другая горячая тема - управление конфликтами зависимостей. Когда в проект подтягивается, как правило транзитивно, две версии одной и той же зависимости. А должна остаться только одна)
Отличное сравнение 3 систем сборки по управлению конфликтами зависимостей еще 10+ лет назад проведено в этой статье: https://habr.com/ru/companies/jugru/articles/191246/
Вывод из статьи - в Maven все сделано, скажет так, странно)))
Приходится явно указывать нужную версию каждой конфликтной зависимости в проекте.
Первый вопрос, который приходит на ум - зачем в Maven так сделали и когда собираются исправлять.
Ответ тут - https://stackoverflow.com/questions/34201120/maven-set-dependency-mediation-strategy-to-newest-rather-than-nearest
Спойлер - исправлять не собираются, считают, что так сборка будет более предсказуемой и повторяющейся. Т.е. описанный выше подход - запускай приложение, находи конфликты в runtime и указывай явно версию в своем модуле - считается правильным. Но есть лайфхак - см. ответ на stackoverflow.
Ну а чтобы найти версии проблемной зависимости - нужен mvn dependency:tree. О его "секретных" (на самом деле полезных) ключах этой таски Maven можно почитать тут https://www.digitalocean.com/community/tutorials/maven-dependency-tree-resolving-conflicts
Ну и если хочется копнуть глубже, например понять, как разрешается конфликт scope-ов зависимости или узнать про то, как разработчик библиотеки может уменьшить возможность появления конфликта (optional) - см. главный источник истины по Maven - его документацию https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
#maven #gradle #java #buildtool #dependency_management
Есть интересная тема - инструменты сборки для JVM проектов. А в рамках нее другая горячая тема - управление конфликтами зависимостей. Когда в проект подтягивается, как правило транзитивно, две версии одной и той же зависимости. А должна остаться только одна)
Отличное сравнение 3 систем сборки по управлению конфликтами зависимостей еще 10+ лет назад проведено в этой статье: https://habr.com/ru/companies/jugru/articles/191246/
Вывод из статьи - в Maven все сделано, скажет так, странно)))
Приходится явно указывать нужную версию каждой конфликтной зависимости в проекте.
Первый вопрос, который приходит на ум - зачем в Maven так сделали и когда собираются исправлять.
Ответ тут - https://stackoverflow.com/questions/34201120/maven-set-dependency-mediation-strategy-to-newest-rather-than-nearest
Спойлер - исправлять не собираются, считают, что так сборка будет более предсказуемой и повторяющейся. Т.е. описанный выше подход - запускай приложение, находи конфликты в runtime и указывай явно версию в своем модуле - считается правильным. Но есть лайфхак - см. ответ на stackoverflow.
Ну а чтобы найти версии проблемной зависимости - нужен mvn dependency:tree. О его "секретных" (на самом деле полезных) ключах этой таски Maven можно почитать тут https://www.digitalocean.com/community/tutorials/maven-dependency-tree-resolving-conflicts
Ну и если хочется копнуть глубже, например понять, как разрешается конфликт scope-ов зависимости или узнать про то, как разработчик библиотеки может уменьшить возможность появления конфликта (optional) - см. главный источник истины по Maven - его документацию https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
#maven #gradle #java #buildtool #dependency_management
Хабр
Разрешение конфликтов в транзитивных зависимостях — Хороший, Плохой, Злой
Вместо предисловия В ближайшую субботу мы с EvgenyBorisov будем выступать в Питере на JUG.ru . Будет много веселого трэша и интересной инфы (иногда не разберешь, где проходит граница), и одно из моих...