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

Чтобы закончить тему с TDD, ну хотя бы на ближайшее время закончить, хочу подчеркнуть его плюсы и порекомендовать книгу.

Плюсы:
1) точно достигните требуемого покрытия кода тестами. Мне сложно представить откуда может взяться непокрытый тестами код если приложение создано по TDD начиная с первой строчки
2) писать тесты будет не так тяжело, как при типичном подходе. Когда код написан, отлажен, возможно даже прошел функциональное тестирование, а вместо создания чего-то нового или обучения приходится удовлетворять SonarQube и требования организации\тимлида - это не весело))) А тут еще выясняется, что код плохо приспособлен для тестирования. И кто знает, возможно с TDD даже появятся позитивные эмоциии, особенно когда тесты зеленеют)
3) меньше шансов, что приложение, даже если оно изначально разрабатывалось как микросервис, превратится в монолит(ик), который страшно трогать руками. Железо не гибкое, процессы и люди .. ну под вопросом. А ПО должно быть гибким.
4) не нужно объяснять PO что такое рефакторинг и почему для него выделяется столько много storypoins или человекодней. Т.к. рефакторинг станет неотрывной частью процесса разработки и затраты на него войдут в трудоемкость разработки фичи
5) полученные тесты - хороший аргумент, чтобы оставить в JavaDoc и аналитике только вещи, касающиеся описания бизнес-процесса, e2e, интеграций, т.к. все остальное будет в тестах. Причем документация с помощью тестов может разойтись с кодом только в одном случае - если на тесты забить) В отличие от.
6) возможно реализация логики улучшится. Как это может произойти. Часто в коде реализуется первая пришедшая в голову идея. А уже по результатам функционального тестирования она переписывается или что хуже запиливаюстя костыли. Это плохой подход - брать первую попавшуюся идею, но психологически бороться с ним сложно. Если же эволюционно писать тесты-код-тесты-код.. то быстрее можно прийти к пониманию, что первая реализация - не лучшая. А переписывание кода и тестов в TDD - это ок

Ну и книга. Это не Кент Бек "Экстремальное программирование: разработка через тестирование" https://habr.com/ru/company/piter/blog/326662/ как можно было подумать. Хотя и ее тоже можно порекомендовать, классика как никак.
Но недавно вышла новая книга Боба Мартина, он же «дядюшка» Боб "Идеальная работа. Программирование без прикрас" https://habr.com/ru/company/piter/blog/679378/. Первая часть книги как раз рассказывает как влится в TDD. Даже видосы есть с реализацией разных простых алгоритмов по TDD подходу.
Мне очень зашло, рекомендую, книгу вместе с видосами. Да, если вступление к книге покажется слишком официозным - переходите сразу к первой части.

Если книжки, статьи на разные темы в рамках Java\Kotlin\DevOps интересны - пишите, буду рекомендовать.

#books #tdd
Всем привет!

Хочу вернуться к теме "правильных" модульных тестов и подчеркнуть пару важных моментов.

1) должен быть быстрый набор тестов, который можно запускать после каждого изменения в коде. Почему это важно: после небольшого изменения в коде всегда понятна причина падения. После нескольких изменений подряд - уже сложнее. Быстрый - понятие нечеткое, но пойдем от обратного - 10 минут точно не быстро) 5 - тоже многовато IMHO. Идеально - минута, две.

2) как я уже писал - тесты должны быть антихрупкими. Хрупкие тесты с течением времени приводят к такому же результату, как и их отсутствие. Тесты часто падают, их отключают или не запускают, рефакторинг делать страшно, код объявляется legacy и переписывается.
Как этого можно добиться:
- не писать тесты на код, который постоянно меняется. Это один из возможных вариантов, не панацея!) Если это не бизнес-логика - это допустимо. В этом случае модульные тесты можно заменить на интеграционные, проверящие более высокоуровневый результат, которые реже меняется.
- не проверять в тесте детали реализации, тестировать результат, который потребитель тестируемого метода ожидает от него. Хорошая статья на эту тему - https://habr.com/ru/company/jugru/blog/571126/ Тестируя только результат мы теряем точность теста, но увеличиваем антихрупкость. Это необходимый компромис. Исключение: сложные утилитные методы, где алгоритм - порядок вызовов - важен.

3) покрытие кода тестами - не панацея. С одной стороны покрытие 30-50% - плохо, т.к. показывает, что много кода не покрыто тестами. С другой стороны покрытие 100% не говорит, что код хороший. Почему:
- не добавляяя Assert, добавив E2E тесты и закрыв их в try catch можно достичь очень хороших результатов по покрытию)
- важно различать Line Coverage и Condition (Branch) coverage. Первое считает процент покрытых срок, второе - процент протестированных путей, по которым можно прийти от начала к концу метода. В случае SonarQube тут все хорошо - он считает свою метрику, которая совмещает обе https://docs.sonarqube.org/latest/user-guide/metric-definitions/ В теории если покрыты все условия, то и по строчкам кода должно быть 100%. Или в проекте есть код, который не используется. В общем метрике SonarQube можно верить)
- предположим мы написали на первый взгляд полноценный набор тестов, с Assert-ми, все как положено. Покрытие 100% по новому коду. Но есть метод с побочным эффектом - он не только возвращает результат, но и сохраняет его внутри класса для отчетности или сравнения в будущих вызовах. Если этого не знать \ забыть - получим неполноценный тест. Конечно, есть Single Responsibility, неожиданные побочные эффекты это плохо, при TDD забыть про только что написанный код сложно, но в других случаях ситация может быть на практике. Другой кейс - тестируемый метод может вызывать библиотечную функцию, внутренности который мы не знаем. Соответственно, все возможные комбинации параметров для нее тоже не знаем. Не факт, что все такие комбинации нужно тестировать в конретном тесте - мы же не тестируем внешние библиотеки. Но факт, что какие-то важные кейсы для нашего бизнес-процесса можно упустить.

4) принцип Single Responsibility для теста звучит так: тест отвечает за одну бизнес-операцию, единицу поведения. Соотношение 1 тестовый класс = 1 тестируемый объект - не правило. Соответственно, в названии тестового класса не обязательно привязываться к названию тестируемого класса, тем более, что его в будущем могут отрефакторить.

5) ну и финальный момент - серебрянной пули нет, перед написанием тестов надо думать, что и как тестировать и выбирать наилучщий вариант.

P.S. Также хочу посоветовать хорошую книгу про тесты от автора статьи из 2) - https://habr.com/ru/company/piter/blog/528872/

#unittests #books
Всем привет! Уже упоминал книгу Идеальная работа Роберта Мартина https://habr.com/ru/company/piter/blog/679378/ Дочитал до конца и вот более полный отзыв. Вначале о хорошем: главы про применение TDD на практических задачах с livecoding - must read. Хороша глава про принцип YAGNI - you aren't gonna need it, особенно про то, как скорость современных компьютеров сделала этот принцип более актуальным. Хорошо написано про структуру presentation layer для упрощения тестирования этого слоя. Шаблоны тестирования там же. Хорошо про вязкость процесса программирования - скорость сборки и прогона тестов. Интересные данные про влияние парного программирования на скорость разработки. Только из-за этого книгу могу рекомендовать к прочтению. Но есть на мой взгляд и минус. Сквозь всю книгу проходит тезис про ответственность программиста за код. Вообще говоря мысль правильная. За любую работу нужно отвечать и делать ее качественно. Смущают три момента. 1) пафос, с которым это подаётся. Сразу вспоминается «Даешь пятилетку за четыре года». Или такой персонаж как Тони Робинсон. Делай хорошо, не делай плохо. Да, все тезисы обоснованы, но я бы добавил больше юмора, практических примеров и меньше пафоса. 2) ко многим вещам ты приходишь с опытом. Поэтому требовать одинаково с сеньора и джуна нельзя. Автор про это говорит, один раз, и все. Для кого тогда эти главы? 3) для доказательства хороших идей подтасовки не нужны. Пример с программистами из Volkswagen, которые обманули программу измерения выхлопов, выглядит именно подтасовкой. Вкратце - чтобы пройти новый экологический стандарт для дизельных двигателей руководство компании дало указание разработчикам так настроить программу работы двигателя, чтобы в тестовом режиме все было ок, хотя по факту он выделял больше вредных веществ, чем разрешено. Но штука в том, что с внедрением этого стандарта старые машины продолжают ездить по улицам. И писать что из-за плохих программистов машины причиняют вред здоровью людей... ну такое. Но, повторюсь - читайте главы про TDD, простоту кода, тестирования БД и UI, парное программирование и настройку проекта. #books #tdd
Всем привет! Ещё одно ревью на книгу, которую упоминал: Владимир Хориков Принципы юнит-тестирования. https://habr.com/ru/company/piter/blog/528872/ Можно сказать уникальная книга про то, как писать юнит тесты. Я знаю таких всего две - эту и Шаблоны тестирования XUnit. Must read. Некоторые моменты можно обсуждать, но откровенных косяков или воды не нашёл. Что запомнилось.
1) разделение кода на четыре группы: бизнес-логика, контролёр в широком смысле этого слова как код, обрабатывающий входящие запросы, тривиальный код и переусложненный код, в котором есть и внешние зависимости и бизнес-логика. От последнего нужно избавляться через рефакторинг, сводя его к бизнес-логике или контроллерам. На бизнес-логику пишем модульные тесты, на контроллеры - интеграционные. На тривиальный код ничего не пишем)
2) не нужно писать отдельные тесты на каждой класс бизнес-логики, нужно тестировать процессы и стремиться к максимальному покрытию. Для бизнес-логики
3) использование минимум mock в модульных тестах, в идеальном случае их вообще не должно быть. Т.е идея в том, что классы бизнес-логики получают и возвращают DTO, которые можно создать в тесте без всяких mock
4) в случае интеграционных тестов mock нужно делать на неконтролируемые нами внешние зависимости, типа очередей, внешних АС, email транспорта. БД в случае если это не наша частная БД. Если же у нас микросервис со своей БД, которая обновляется вместе с приложением и внешние клиенты в нее не ходят - ее mock-ать не нужно, более того и заменять ее HBase тоже не нужно, т.к иначе мы не сможем полноценно оттестировать работу с БД, да и возникнут накладные расходы на поддержание двух версий скрипов. Это как раз то, где можно дискутировать, но милая мне нравится.
5) чем меньше в приложении слоёв, тем лучше, проще тестировать
6) логирование может являться контрактом, например по требованию сопровождения или если на его основе работает мониторинг, тогда его тоже нужно mock-ать и тестировать.
7) организовать отправку событий, например, логирования, сообщений в Kafka или SMTP можно через стандартизацию механизма событий в доменной модели и обработку событий в слое «контроллеров».
8) если вернутся к модели тестов AAA - Arrange, Act, Assert, то Arrange и Assert в одном тесте может быть много, а вот Act должен быть один, иначе у нас проблемы с нашим Java API. Исключение - интеграционные тесты где сложно поднять контекст.
9) методы с инициализацией тестов типа @Before плохи тем, что сложно читать текст, т.к часть Assert в другом методе. Поэтому лучше использовать отдельные говорящие private/protected методы с параметрами, чтобы можно было настроить тестовые данные под каждый тест
10) название тестового метода должно читаться как фраза, разделять можно подчёркиваниями в случае Java, фраза не обязательно должна быть шаблонной как в BDD, краткость более важна. Кстати, в Kotlin слова в названии теста можно разделять пробелами.
11) интеграционные тесты дают хорошее покрытие, за счёт этого увеличивают защиту от ошибок в коде. Поэтому их лучше всего писать на позитивные сценарии, покрывающие максимум кода и те негативные сценарии, которые не удалось покрыть юнит тестами
12) антихрупкость тестов, о которой я уже писал, становится особенно важна по мере развития проекта. Т.е после некоторого времени более важно, чтобы тесты были антихрупкими, чем чтобы они находили все потенциальные ошибки. Т.к для последнего есть QA и приёмочные тесты
Я перечислил лишь то, что запомнилось, интересным идеей в книге намного больше. Интересных и практических. Повторюсь - must read!
#books #unittests
Всем привет!

По следам отличной книги Владимира Хорикова Принципы юнит-тестирования, см https://t.me/javaKotlinDevOps/50, решил погрузится в тему и прочитать еще и классику жанра - Шаблоны тестирования xUnit. Рефакторинг кода тестов https://www.litres.ru/dzherard-mesarosh/shablony-testirovaniya-xunit-refaktoring-koda-t-48637685/
К слову, на английском книга выложена в виде сайта со списком паттернов тестирования http://xunitpatterns.com/
По сути книга и является большим справочников паттернов. Автор - человек дотошный: любую типовую практику, используемую для написания тестов он вынес в шаблон с подробным описанием истории возникнования, вариантов релизации и примеров, мотивирующих на использование шаблонов.
Вот примеры шаблонов из книги, на которые хотел бы обратить внимание:
1) 5 видов моков http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html - от Dummy object, который ничего не возвращает, не делает и не проверяет, а нужен лишь для передачи как параметр, до Fake Object - облегченной реализации боевой логики, без обращений во внешние системы и БД. Видел ссылки на эти 5 типов в других статьях и книгах.
2) Test Utility Method - одна из базовых практик при рефаторинге тестов, вынесение повторяющего кода в отдельные методы. Это может быть код настройки системы, код проверки результата с assert-ми и код очистики ресурсов. Очень полезная штука, с какого-то момента после рефакторинга вынесенных методов для их переиспользования мы получаем некий тестовый язык, по сути свой DSL, и это без всяких Cucumber-ов и прочих BDD. http://xunitpatterns.com/Test%20Utility%20Method.html

Остальное не влезло в пост телеги, см. на дзене https://dzen.ru/media/id/62fe543369f7d3314503a87d/recenziia-na-shablony-testirovaniia-xunit-6367bbba7d9dc52b40aa3ff3

#unittests #books
Всем привет!

Прочитал еще одну книжку: "Распределенные системы. Паттерны проектирования" https://habr.com/ru/company/piter/blog/442514/
Главное, что хочу отметить: в книге никакого хардкора нет, читается легко.
Еще плюс - в конце многих глав есть простые примеры как развернуть реализацию паттерна в k8s. Можно и нужно экспериментировать)
Что характерно - автор участвовал в создании k8s, поэтому почти все примеры разворачиваются в k8s, даже Redis и Kafka.

В книге 3 части.

Паттерны уровня pod-а. Спойлер - речь про sidecar-ы.
sidecar-ы делятся по назначению:
1) добавление нового функционала к основному сервису - это собственно sidecar
2) прокси к шардированному сервису - Ambassador. Шардированному - это важно, в этом случае нельзя просто разбросать запросы по round robin, как может сделать Service из k8s
3) адаптация формата данных основного сервиса - Adapter.
Всегда думал, что сайдкар это просто сайдкар, а оказывается есть 3 вида)

Паттерны проектирования обслуживающих систем. Название странное, да)
Здесь сборная солянка из репликации, шардирования, алгоритма выбора лидера и Function As A Service (FaaS).
Репликация - это дублирование для отказоустойчивости и производительности, шардирование - создание отдельных экземпляров для обработки части данных, если весь набор не вмещается на одной машине. Можно совмещать оба подхода. Для это есть отдельный паттерн Scatter\Gather, распараллеливающий обработку "тяжелой" задачи на несколько серверов, после чего агрегирующий результат. Пример: поиск по большому объему текста. Есть хорошее замечание, что накладные расходы на распараллеливание растут с числом потоков и имеется предел, после которого ускорения не будет.
Хорошие примеры по каким критериям можно шардировать данные.
"На пальцах" описано выбор лидера на примере распределенных блокировок с помощью etcd. etcd к слову используется в k8s как хранилище состояния кластера.
Хорошо аргументировано когда полезен подход FaaS. Спойлер - когда запросов мало и для них дорого держать сервис постоянно работающим.

Третья часть - паттерны потоковой обработки. Потоковая обработка - это асинхронные часто многошаговые задачи, когда ответ в режиме онлайн не требуется. Сейчас продвигается их замена на стримингом на основе Kafka, Kafka Streams и Spark, который способен выдать ответ в режиме онлайн, но есть кейсы, когда это не нужно. Типичный пример потоковой задач: периодическое построение отчетов, периодическая выгрузка данных.
Обозреваемые паттерны: Copier (параллельная обработка с разными параметрами, например, рендеринг видео с разных разрешениях), Merger (объединение нескольких очередей в одну), Filter (отбрасывание части задач), Splitter (разделение потока в зависимости от настроек задачи: уведомление клиента по sms или по email), Sharder (аналог Map в Map\Reduce, параллельная обработка), Reduce (объединение с агрегацией), Join (объединение с ожиданием всех).

Маленькое замечание: паттерны из последней части применимы не только в облаке, но и при многопоточке в Java. Книжка про распределённые системы, поэтому в ней любая функция выносится в облако. Нужно ли так делать всегда - нет. См. https://t.me/javaKotlinDevOps/59 Ключевые моменты: разные команды, возможность переиспользования, сильно различающийся функционал.
Еще интересный момент - приводится пример использования Job-ов из k8s. Пример хороший, но важной замечание - со Spring-ом так делать не рекомендую, сервис, который запускается 1 минуту, работает 1 минуту и останавливается - не рационально.
Также отдельный большой вопрос - нужно ли все "пихать" в облако. Краткий ответ - нет. Да, облако дает возможность хранить данные: см. StatefullSet https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/. Да, облако, особенно c Service mesh, дает продвинутые возможности маршрутизации. Но часто репликация и шардирование данных настолько сложны, что облачные механизмы тут не помогут, а тогда возникает вопрос - а нужно ли в таких случаях облако вообще?

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

#books
Всем привет!

Сегодня короткое ревью на книжку Непрерывное развитие API. https://habr.com/ru/company/piter/blog/472522/
Несмотря на многообещающее название - не понравилась.
Слишком менеджерская и "капитанская".
По технике практически ноль.
Приведу несколько идей, достаточно полезных, чтобы примерно было понятно, о чем речь:

1) рассматривайте API как продукт, есть компании, где API - единственный продукт: Twillo, Stripe
2) в первую версию API надо вкладывать больше усилий, потом можно ее непрерывно улучшать небольшими шагами, Lean, Agile, все как мы любим)
3) документация для API важна, особенно примеры использования, также для больших организаций полезен реестр API и инструменты для тестирования типа тех, что предоставляет Swagger https://editor.swagger.io/
4) тестируйте создаваемое API со стороны клиента, это поможет лучше его спроектировать
5) мониторьте боевые сервера API как на предмет ошибок, так и для сбора общей статистики по числу запросов, времени выполенния, типичным последовательностям запросов. Возможно постоянно повторяющиеся последовательности вызовов можно заменить одним.
6) с ростом организации централизовано реализовывать API невозможно, поэтому большУю часть решений нужно отдавать в команды. Централизовано имеет смысл отбирать список допустимых протоколов\технологий, и, возможно, согласование и тестирование API
7) ключевые решения по API должны примимать опытные разработчики
8) во внешнее API стоит вкладывать больше усилий, чем во внутреннее
9) автоматизация тестирования API требует больших затрат на разработку и поддержку как и любая автоматизация, но в целом полезна
10) API должно помогать достижению целей организации, можно использовать KPI и OKR для определения, достигает ли API поставленных целей. Вот на этом шаге и бросил читать)

Итого: верхнеуровнево есть правильные мысли, но с практическая ценность маленькая. Не рекомендую.

#books #api
Всем привет!

Прочитал книгу Лемер "Масштабируемый рефакторинг". В целом понравилась, с важным дисклеймером.
С одной стороны основные мысли в книге вполне себе "капитанские". С другой - о них очень легко забыть)))

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

Тезисы:
1) т.к. рефакторинг займет длительное время - нужен план. План позволит:
а) видеть весь объем работ и правильно оценить: хватит ли выделенного бизнесом времени
б) найти забытые задачи либо самостоятельно - анализируя план - либо с помощью смежных команд, опубликовав его

2) рефакторинг нужно делать для кода в вашей области ответственности. Если за код отвечает другая - не надо его трогать без согласования

3) нужно понять - не является ли код deprecated или неиспользуемым. Часто код забывают удалять, все его использование ограничивается модульными тестами

4) нужно изучить - почему код стал таким. Зачем?
а) возможно выяснится, что "это не баг, а фича"
б) когда станет понятно, на почему возникли те или иные компромиссы в коде - это изменит отношение к коду. Все же приятнее улучшать сложный legacy код, а не "переписывать этот го..код"

5) нужно ответить себе на вопрос - почему код нужно рефакторить именно сейчас. А возможные ответы на этот вопрос приводят к необходимости метрик для бизнеса, о которых я уже писал https://t.me/javaKotlinDevOps/197

6) важно собрать команду с двумя типами участников: full time разработчики и эксперты. Для этого нужно договориться с руководителями о их полном или частичном подключении к задаче рефакторинга. Искать стоит людей, либо заинтересованных в результате рефакторинга, либо тех, кому надоела рутина и хочется, что-то поменять

7) нужен канал или страничка, где регулярно будут публиковаться прогресс рефакторинга. В случае, если прогресс будет - это может мотивировать на подключение к рефакторингу новых участников. Прогресс нужно сопоставлять с планом, при необходимости вносить корректировки. Также нужен митап или чат, где заинтересованные лица смогут задать вопросы.

8) прогресс нужно контролировать еще потому, что ближе к концу, когда основные изменения уже сделаны, есть риск снижения темпов. Из-за выгорания или расслабления - ведь основные изменения уже сделаны. Что тут можно сделать:
а) собираться командой в неформальной обстановке
б) рассказывать о своих достижениях для вливания "новой крови"

9) после окончания рефакторинга важно проконтролировать, что все потребители знают об изменениях и либо уже используют их, либо запланировали обновление. Речь может идти о переходе на новое API, новый фреймворк, новый DevOps, новую версию библиотеки. Для ускорения процесса перехода нужны:
а) рассылка
б) демонстрация
в) инструкция
г) чат, куда можно задать вопросы
д) возможно регулярный митап для ответов на сложные вопросы

#books #refactoring #book_review
Всем привет!

Наткнулся сегодня на еще одну книжку про AI. Что опять?)
Для начала у нее интересное название - "Охота на электроовец" https://markoff.science/#book
Но есть еще две причины, по которым я решил про нее написать.
1) книжка научно-популярная - история развития AI и обзор текущего состояния. До сих пор не встречал таких. При этом написана программистом (бывшим программистом судя по текущей должности). Кажется, для вкатывания в тему, если есть пробелы\проблемы в математическом образовании - неплохой вариант.
2) автор - Сергей Марков - из России, что встречается не часто. Более того - автор живет в России на данный момент. Т.е. его можно в теории встретить на какой-либо конференции и позадавать вопросы. Ну и проблем перевода точно не будет)

Скачать можно бесплатно, бумажную версию скоро выпустят тут https://dmkpress.com/catalog/computer/data/978-5-93700-333-1/
Я лично планирую прочитать.
Да, о минусах - иллюстрация на обложке для электронной версии .. ну такое. Ну и 2 тома, 1200 страниц - не всякую дорогу осилит идущий)

P.S. Угадайте, в какой компании работает автор?)

#ai #books