(java || kotlin) && devOps
374 subscribers
6 photos
1 video
7 files
318 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Всем привет!

Еще одна проблема, которую я замечаю в unit тестах - это засилье copy-paste. Тесты есть, покрытие хорошее, тесты проходят. Все хорошо?
Не совсем.
Как я уже писал тест - это самая лучшая документация к коду. Поэтому если в тесте есть mock объект, скопированный с другого теста, но на самом деле не нужный для успешного прохождения теста - это вводит в заблуждение при изучении проекта. Аналогично - если есть лишние проверки verify, которые конкретно у данного теста всегда успешны или не проверяют ничего полезного, зато приводят к хрупкости теста.

Резюме: тесты - это тоже код, к их написанию нужно относится с той же серьезностью, как и к коду.

#unittests #antipatterns
August 29, 2023
Всем привет!

Есть такая отличная штука в Kotlin, как контекстные функции.
Вот документация https://kotlinlang.org/docs/scope-functions.html
Вот пример, хорошо иллюстрирующий зачем они нужны:

val man = Person("Vasya").apply {
age = 20 // same as this.age = 20
city = "Moscow"
}

Код стал проще, читается хорошо. Если, конечно, ты знаешь про контекстные функции)

Но как и любая вещь, контекстные функции могут быть использованы не только во благо(
Вот несколько антипаттернов:

val params = claim.systemState?.let {
FailureParams(
claim.partnerName,
it.name,
)
}

Что мне здесь не нравится - читаемость. Если читать сверху вниз, то получается, что мы берем статус из заявки и присваиваем переменной params ... не его, а совершенно другой объект, созданный внутри let. Скорость понимания кода страдает.

return claim.also {
saveToCache(it)
}

Опять же, мне не нравится читаемость кода. Мы возвращаем результат метода, claim. А нет, не возвращаем. Вначале пишем его в кэш. А потом уже возвращаем.
Гораздо проще было бы:

saveToCache(claim)
return claim

Ну и наконец самый хит:

return claim.also {
saveToCache(someOtherObject)
}

Зачем? Почему? Не понятно)

P.S. По ссылке выше есть неплохая табличка https://kotlinlang.org/docs/scope-functions.html#function-selection Это по сути навигатор по контекстным функциям - позволяет выбрать одну из 6 для вашего конкретного случая. На первое время точно будет полезной

#kotlin #antipatterns
September 1, 2023
Всем привет!

Сегодня пост о крутой фиче Kotlin, которая решила одну важную проблему. И добавила другую)
Я о Null safety.
Суть ее в том, что в Kotlin любой тип представлен в двух ипостасях - одна может содержать null значения, другая - нет.
String - не может содержать null,
String? - может.
Это два разных типа, неявное приведение второго к первому "без приседаний" невозможно.
Что дает Null safety?
По умолчанию предлагается использовать тип not null, и если так и делать, то кажется, что про NPE - NullPointerException - можно забыть. А заодно забыть о проверках на null как в коде, так и в тестах. Небольшой оффтоп - проверка not null значений на null в тестах - еще один антипаттерн. Говорит о том, что пишущий этот код еще не до конца познал Kotlin)
Вроде бы все хорошо. Но есть нюанс. Что будет, если присвоить not null переменной nullable значение? Ошибка компиляции. А всегда ли компилятор знает, что это nullable значение? Если тип Kotlin - то всегда. Если тип Java - то в общем случае не знает. Что же он делает в таком случае? А ничего, просто разрешает присваивание.

//Java
public class JavaNullability {
private Boolean value;

public Boolean getValue() {
return value;
}
}

// Kotlin
class KotlinNullability {
constructor(test: JavaNullability) {
val nullValue: Boolean? = test.value
val notNullValue: Boolean = test.value
// компилятор разрешает оба варианта
}
}

Что же будет в runtime? Привычный нам в Java NPE на втором присваивании. Да, null safety не защищает от NPE в Kotlin коде.

Что тут можно сделать?

1) при взаимодействии Kotlin-Java знать nullability Java параметров, с которым мы работаем. Это может быть описание контракта - OpenAPI спецификация или любой другой контракт.
2) явная проверка на null в коде присваивания
3) самый плохой вариант, антипаттерн - всегда присваивать значения из Java nullable типам в Kotlin. Почему это плохо? nullable типы расползаются по всей программе, и в итоге мы теряем все преимущества Kotlin null safety.

P.S. Но в целом: null safety - это круто! Подробнее можно о ней можно почитать в официальной документации: https://kotlinlang.org/docs/null-safety.html#nullable-receiver

#kotlin #java #antipatterns #nullsafety
September 2, 2023
September 5, 2023
September 6, 2023
Всем привет!

Если посмотреть на тему null safety с другой стороны - то это лишь один из аспектов более широкого принципа. Я бы сформулировал его как: "Знай свои данные".

Что же нужно знать:
1) кодировку текстовых данных - всегда явно ее указывать, не ждать, что всегда будет UTF-8
2) часовой пояс\locale - опять же не надеяться на то, что CI pipeline, тестовые и ПРОМ стенды работают в одном часовом поясе. А даже если это так - что на всех правильно настроен часовой пояс) И что никто не запусти код в другом часовом поясе. И никто не пришлет время из другого часового пояса
3) файловую систему, в частности разделители в пути к файлу. Для них есть специальные константы в Java Core, ОС в которой происходит запуск также можно определить
4) все проверки на injection - sql injection и аналоги
5) указание конкретных проверяемых исключений вместо Exception. Это не совсем данные, но часто исключения вызываются некорректными данными
6) unit тесты на граничные значения
7) ну и наконец null safety - может ли по бизнес-процессу значение принимать null или нет

Принцип можно расширить до инфраструктуры и добавить к примеру версии используемых библиотек и плагинов. Если версии не фиксировать - могут подтянутся новые в неожиданный момент с неожиданным поведением)

P.S. Если что-то забыл - дополняйте в комментах

#data #principles
September 8, 2023
Всем привет!

Постов долгое время не было, причина типичная - много работы. Вообще не помню времени, когда ее было мало((( И были ли вообще такие времена?)

Хотел бы поднять сегодня такую важную тему как взаимодействие разработчиков и сопровождения.
Для начала одна общеизвестная информация - разработчики и сопровождение исходя из своих задач обречены на противостояние.
Задача разработчиков - менять приложение, задача сопровождения - обеспечивать его работоспособность. А как известно: работает - не трогай) Любое изменение потенциальный источник проблем.
Отсюда часто следует одна крайность - сопровождение максимально критично относится к любым изменениям, разработка "виновна" по умолчанию, требуется строгое соблюдение регламентов, любая нестандартная просьба разработки встречается "в штыки".
Почему это плохо?
Сейчас основная методология разработки - это Agile в разных вариациях. Один из ключевых моментов в Agile - это команда, командная ответственность, гибкие решения в команде. А сопровождение в описываемом кейсе выступает внешним "врагом" - блокирует инициативы команды, замедляет скорость выпуска новых версий. А высокая частота выхода в ПРОМ - еще одна важная часть Agile.
С таким кейсом я, увы, сталкивался и наблюдал его губительный для команды эффект. Часто свои требования сопровождение объясняет требованиями надежности. Хорошее ли это объяснение - зависит от деталей. Если объяснение звучит как-то так - это снизит надежность, т.к. ... и идет описание причин - то да, хорошее.
Если слово надежность произносится, а никаких деталей не приводится - это признак того, что сопровождение боится изменений и не хочет развиваться.
Есть и другая крайность - сопровождение "согласно на все") Не выставляет никаких требований, принимает любые дистрибутивы. Кейс более редкий. Чем это плохо - разработчики опять же исходя из своих основных задач редко думают о том, как их код будет сопровождаться. Обычно на разработку времени хватает впритык.
Какой выход из данной ситуации?
Выставлять требования со стороны сопровождения.
Требования зависят от компании, отрасли, приложения, числа пользователей и много чего.
Но базовые требования могут быть такими:
1) список метрик, позволяющих отслеживать работоспособность
2) требования к логам - где и в каком объеме. Сюда же я бы добавил требования к фильтрации логов, с важным дополнением - возможность фильтрации зависит как от разработчиков бизнес-приложения, так и от разработчиков системы просмотра логов
3) требования к трассировке (tracing) - особенно важно если мы имеем дело с микросервисами
4) наличие инструкции для сопровождения в случае, если установка релиза требует ручных действий
5) наличие сценария отката на предыдущую версию. Это может быть выключение feature toggle или номер версии для отката. Самое важное - сама возможность отката. Это тоже требование, его нужно или соблюдать, или если это невозможно, например, в случае необратимых изменений в БД - составлять план действий на случай неудачной раскатки
6) фиксирование таймаутов внешних вызовов: я уже писал что бесконечные таймауты - одна из основных причин падения приложения
7) требования по UI для ручного разбора ошибок, если все предыдущие требования не помогли)

Почему я говорю "могут быть такие требования" - я все же изначально разработчик, поэтому имею некие предустановки и не вижу полной картины. Если есть возражения и дополнения - возражайте и дополняйте)

#dev #ops
September 16, 2023
September 19, 2023
Всем привет!

Может ли API с широким функционалом стать проблемой? Имея несколько реализаций от разных производителей ПО, являясь эталонной реализацией паттернов интеграции https://www.enterpriseintegrationpatterns.com/ при этом быть хуже конкретного продукта с ограниченым набором функционала?
Как можно догадаться из вопроса - ответ: да. Я про JMS vs Kafka.

JMS - это API из состава Java EE (сейчас Jakarta EE). Есть несколько реализаций: практически у каждого сервера приложений есть свой JMS - IBM, Oracle, JBoss, SAP, есть и Open source решения - ActiveMQ, Artemis MQ и другие.
Что есть в JMS? Стандарт широкий: есть очереди (точка-точка, ака P2P) и топики (подписка, ака PubSub), опциональная персистентность и транзационность, возможность настраиваемой маршрутизации и конвертации сообщений. И с security все хорошо.

У Kafka же только топики, нет продвинутой маршрутизации, трансформации, персистентность постоянная, транзакционности нету, гарантии однократной вычитки должен обеспечивать клиент. Да и вендор один. Но справедливости ради vendor lock нет, т.к продукт open source, за деньги только поддержка.

При этом Kafka успешно отвоевывает долю рынка. В чем же дело?

Секрет Kafka в том, что она выпустила достаточно простой, очень быстрый open source продукт, достаточный для большого числа клиентов. Этим нивелируется преимущество JMS в возможности выбора реализации. Причем Kafka быстрая с включённой по умолчанию персистентностью, а значит и высокой надёжностью. По скорости однозначно бьет все реализации JMS. Про то, как удалось добиться такого результата, я писал тут https://t.me/javaKotlinDevOps/91

В чем проблемы JMS:
попытка объять необъятное в API = переусложнение,
много вендорской специфики, которая может помешать смену реализации,
слишком большая роль брокера - все возможности по маршрутизации и трансформации не бесплатны по производительности, и кроме того ведут к тому, что обычная очередь превращается в Enterprise Service Bus, а у этой концепции есть свои минусы.

Я не хочу сказать, что JMS можно выкидывать, а лишь пишу почему Kafka удалось ее так сильно потеснить. Если вам нужно взаимодействие точка-точка и нет больших объёмов данных - JMS вполне подойдёт.

#kafka #jms #comparison
September 23, 2023
Всем привет!

Я уже писал о важности рефакторинга.
Но как оценить, что рефакторинг достиг своих целей?
Можно экспертно. Но не всегда этот вариант годится для обоснования времени на рефакторинг у бизнеса.
Можно ли как-то подтвердить эффект цифрами?
Самый простой вариант - если проблема в производительности. Тогда изменяем TPS - transaction per second - до и после и демонстрируем эффект.
А если проблема в сложности кода как это чаще всего бывает?
Тогда на помощь могут прийти следующие метрики:
- цикломатическая сложность кода и другие похожие метрики https://www.tiobe.com/knowledge/article/demystifying-code-complexity/ После рефакторинга сложность должна снизиться, при этом в процессе может временно повышаться. А стандартные метрики хороши тем, что уже есть утилиты для их подсчёта. Например, Checkstyle https://checkstyle.org/checks/metrics/cyclomaticcomplexity.html или Sonarqube https://habr.com/ru/articles/565652/ Также благодаря тому, что эти метрики широко известны в узких кругах разработчиков )) их проще объяснить бизнесу. Или пусть изучают формулу, или верят на слово разработчикам)
- если сложность кода по каким-то причинам не подходит - можно рассмотреть среднее число строк в классе или методе. После рефакторинга это число также должно снижаться,
- в некоторых случаях может даже подойти абсолютное число строк кода. Если приложение «раздуто», то результатом рефакторинга станет уменьшение размера кодовой базы.
- число TODO в коде. Каждая TODO - потенциальный техдолг.
- число длинных комментариев в коде. Если код самодокументирующийся - с говорящими названиями методов и классов - то длинный комментарий можно рассматривать как аналог TODO
- скорость вникания в код сервиса для новых разработчиков. Изменить достаточно трудоёмко, но метрика хорошая
- число ошибок ПРОМ, связанных с изменяемым кодом. Если их много - значит с кодом что-то не так
- постоянно увеличивающееся или стабильно большое время разработки новых фичей. Например, по сравнению с временем в начале жизненного цикла сервиса. Сравнивать с другими командами и сервисам часто не корректно.

Отдельно я бы рассмотрел случай, когда новая фича не вписывается в существующую архитектуру. Может получится, что просто сделать ее будет быстрее, чем отрефакторить и сделать без техдолга. Эффект может проявится на следующих фичах, но априори это оценить сложно

#java refactoring #metrics
September 25, 2023
Всем привет!

Продолжая тему рефакторинга. Основное предусловие для начала рефакторинга - это наличие хорошего тестового покрытия. Т.к. мы не меняем бизнес функционал, а улучшаем код либо для повышения производительности, либо для его упрощения. Но при этом нужно гарантировать, что бизнес функционал не сломался, т.е. не появились регрессионные баги. Ведь рефакторинг, в отличие от новой фичи, может затронуть все приложение. Соответственно, баги могут появиться в любом месте, и без тестового покрытия - это большие риски.
Можно рассмотреть похожий кейс. У нас есть монолит, мы хотим распилить его на микросервисы. Это тоже своего рода рефакторинг, только на уровне архитектурном уровне. И тоже аналогичное условие для его начала - наличие достаточного набора тестов. В данном случае повышается важность интеграционных тестов.
Важный момент: в процессе подготовки к разбиению монолита или серьёзному рефакторингу может возникнуть вопрос-предложение - а давайте все выкинем и напишем заново. Так вот - одним из базовых критериев для ответа на этот вопрос также является покрытие тестами. Очевидно, не единственным, но важным. Другие критерии - объем техдолга, соответствие текущей архитектуры и целевой.
Еще кейс - разработчик «боится» рефакторить код, т.к. он слишком сложный или затрагивает слишком много зависимостей. С тестами решиться на рефакторинг намного проще.

Вывод простой - пишите тесты, это страховка при рефакторинге)

#refactoring #unittests #microservices
September 26, 2023
Всем привет!

Я упоминал в посте про JMS vs Kafka про ESB - Enterprise Service Bus. Она же Корпоративная Сервисная Шина.
Какие плюсы и минусы данного решения?

Плюсов я вижу три:
1) унификация API в пределах компании
2) единая точка мониторинга и контроля всех интеграций
3) больше возможностей для переиспользования уже существующих API

Минусы такие:
1) т.к. ESB - это отдельная система, то ее разрабатывает как правило отдельная команда, которая быстро становится узким местом. Особенно при внедрении микросервисной архитектуры и резком увеличении числа интеграций
2) ESB как правило вносит дополнительные задержки, что особенно критично при синхронном взаимодействии
3) как правило ESB предлагают коммерческие компании, что приводит к vendor lock. Слезть с такого решения будет сложно
4) неочевидная штука - с одной стороны команда ESB унифицирует все API. Но с другой - API становятся перегруженными. Там будут какие-то стандартизированные для всех поля, общие базовые типы, которые во многих случаях будут избыточными
5) также ESB из-за стандартизации затрудняет развитие API. Т.к. унификация, а кроме того больше команд участвует в согласовании - три вместо двух

Вывод: на данном этапе развития ПО идея ESB выглядит избыточной.

Но что же с плюсами, как добиться того же результата с API точка-точка и микросервисной архитектурой?
1) унификация вещь полезная, главное чтобы она не была избыточной. Архитектор может выставить требования по API, DevOps - встроить их проверку в pipeline.
2) единая точка контроля - в случае облачной среды такой точкой может быть Istio или k8s. Либо ставить proxy на границах сред.
3) для переиспользования можно использовать каталог API. Да и не всегда переиспользование полезно, см. выше про избыточное API. Также отдельное API для каждого потребителя позволяет лучше контролировать доступ к данным

#api #esb
September 29, 2023
October 2, 2023
Всем привет!

Сегодня хотел бы поднять такую тему - что должно быть в проекте сервиса (в исходниках)?
Кроме собственно кода и файлов с настройками.

1) readme.md
Наличие: обязательно.
Содержимое: описание проекта, его идентификатор (для DevOps pipeline или в вашем реестре сервисов), описание вариантов сборки (отладка, запуск модульных и интеграционных тестов, чистовая сборка), структура каталогов, возможно важные особенности сборки

2) .gitignore
Наличие: обязательно

3) test.http
Наличие: крайне желательно
Содержимое: список URL для отладки на localhost в формате, который понимает IntelliJ IDEA

4) IDEA run configuration
Наличие: крайне желательно
Да, конфигурации тоже можно сохранять. Особенно полезны для больших проектов, допускающих отладку отдельных частей для скорости.

5) скрипты и настройки CI
Наличие: крайне желательно
Часто скрипты меняются вместе с кодом, лучше держать их вместе

6) todo.md
Наличие: желательно
Содержимое: техдолг с точки зрения разработчиков сервиса. Я специально делаю акцент на происхождение, т.к. еще есть внешний техдолг от архитекторов, сопровождения, ИБ, его обычно заводят в JIRA. Почему бы и внутренний техдолг не завести в JIRA? Можно, но есть два риска: если работа идет не по Scrum - задача потеряется в недрах системы, если работа идет по Scrum - слишком рьяный Scrum мастер в рамках гигиены JIRA может настоять на удалении задачи как непонятной\неактуальной. Храня же техдолг вместе с кодом мы можем свободно уточнять формулировки, разбивать на более мелкие задачи для последующего переноса в JIRA с взятием в ближайший спринт.

7) Maven или Gradle wrapper
Наличие: желательно, чтобы не зависеть от наличия утилиты в среде сборки

8) профили checkstyle и ему подобных утилит статического анализа кода
Наличие: возможно, если требуется подстройка для вашего сервиса

9) статическая часть папки .idea
Наличие: возможно по договоренности с командой

10) settings.xml для Maven
Наличие: возможно если они меняются от проекта к проекту

11) документация, естественно через автогенерацию
Наличие: возможно, особенно для библиотек

P.S. Еще может возникнуть вопрос про схемы API - их лучше держать отдельно, чтобы физически разделить интерфейс и реализацию.

#sources
October 3, 2023
Всем привет!

Хорошая подробная статья про кодировки в Java и что изменилось в Java 18 https://habr.com/ru/companies/jugru/articles/709952/
Еще порадовало, что кто-то заморочился и сделал вот такой навигатор: https://habr.com/ru/articles/147843/ )))

А вывод следующий: в существующих Java сервисах надо явно указывать кодировку, как я и говорил ранее: https://t.me/javaKotlinDevOps/193 В новых написанных на Java 18+ в большинстве случаев можно положится на кодировку по умолчанию - UTF-8. Если конечно вы не храните данные в другой кодировке) Или не работаете с Windows и ее консолью и файловой системой. Или кто-то в вашей команде
не проводит отладку на Windows.

#java #encoding
October 5, 2023
Всем привет!

Я уже писал о проблеме интероперабельности Kotlin-Java касающейся null safety - https://t.me/javaKotlinDevOps/190
Есть и еще одна, коллега столкнулся с ней недавно.
В Kotlin четко разделяет изменяемые и неизменяемые коллекции на уровне типов, по умолчанию предлагая делать их неизменяемыми.
В Java наоборот - по умолчанию все коллекции изменяемые, хотя неизменяемые создать тоже можно, например, Collections.unmodifiableXXX, но это не единственный способ.
Итого: предположу, что в Kotlin условно 90% коллекций немодифицируемые, в Java - наоборот 90% модифицируемые.

Что же будет при передаче коллекции из Kotlin в Java?
Да, хорошая практика независимо от языка - не менять передаваемые в метод параметры. Но эта практика не стимулируется к использованию Java.
Да, иммутабельность можно проверить, но делается это довольно криво и я подозреваю делается редко: https://ru.stackoverflow.com/questions/608545/collections-unmodifiable-как-определить
Отсюда получаем, что с большой вероятностью возможна ошибка UnsupportedOperationException на вставке в эти коллекции в Java.

#java #kotlin
October 7, 2023
Всем привет!

Проверяемые исключения - еще одна вещь, которой отличаются Java и Kotlin.
В Kotlin их нет, и вот тут описано почему https://kotlinlang.ru/docs/reference/exceptions.html
Там же есть ссылка на диалог таких известных людей как Bruce Eckel и Anders Hejlsberg на эту тему https://www.artima.com/articles/the-trouble-with-checked-exceptions
Вот тут есть его русский перевод https://habr.com/ru/articles/221723/

В целом соглашусь с основным аргументом авторов: с ростом кодовой базы - а тут важно помнить, что кроме кода сервиса мы еще используем кучу библиотек включая вездесущий Spring Framework - проверяемых исключений становится слишком много. А их выборочная обработка в бизнес приложении, которое активно использует внешние библиотеки, часто не нужна. Т.е. есть цепочка вызова из к примеру 10 сервисов, а обработка исключений - в одном из них, максимум в двух. В остальных случаях их приходится пробрасывать. Отсюда приходим к throws Exception. А это явный антипаттерн.
Следовательно, в большинстве случаев вред от проверяемых исключений перевешивает пользу. Хотя идея - если рассматривать ее именно как идею - красивая: объявляем в API не только типы входящих и исходящих параметров, но и потенциально возможные ошибки. Вот только удобной реализации пока никто не придумал)))

#java #kotlin #exceptions #checked_exceptions
October 9, 2023
Объявляете ли вы проверяемые исключения в Java в своем коде?
Anonymous Poll
36%
Да
64%
Нет
October 11, 2023
Что делаете с проверяемыми исключениями из Java Core и внешних библиотек?
Anonymous Poll
38%
Честно пробрасываю все
54%
Сразу же ловлю и обрабатываю
23%
throws Exception скрипя зубами)
October 11, 2023