Эргономичный код
794 subscribers
76 photos
3 videos
20 files
385 links
Канал о разработке поддерживаемых бакэндов - про классическую школу TDD, прагматичное функциональное программирование и архитектуру и немного DDD.

Группа: https://t.me/+QJRqaHI8YD

https://azhidkov.pro
Download Telegram
Привет!

Я тут такую штуку в Котлине откапал.
Для локальных и возвращаемых из приватных функций анонимных объектов можно обращаться к поля

    fun test() {
val tests = (1..2).map {
createTest(mapOf("id" to 10))
}

println(tests[0].id == 10L)
}

private fun createTest(map: Map<String, Any>) = object {
val id: Long = map["id"] as Long
}


Что тут просиходит:
1. У меня есть функция createTest, которая возвращает анонимный объект с полем id
2. Я генерирую список таких объектов и сохраняю его в tests
3. Я беру первый элемент из списка и беру у него поле id. И всё работает. Но если попытаться проставить тип для функции или коллекции - там можно вставить только Any (aka Object) и всё ломается.

У меня есть сомнения в целесообразоности использования такого трюка, но код ручного маппинга 1-N в дерево неизменяемых объектов он мне сделал чуть понагляднее

#kotlin@ergonomic_code
👍3
Привет!

У меня две новости. Обе хорошие и обе объясняют затишье в канале:)

Новость первая - в июле у меня родился второй ребёнок.
Поэтому времени на ЭП стало меньше. Минимум на ближайший год, а то и два.

Новость вторая - мой доклад "Spring Data JDBC. Четыре года в проде, полет отличный" взяли в программу Joker 2024.
Поэтому сейчас всё ЭП-время уходит на подготовку доклада и до октября в канале я буду постить только ссылки на видосы, которые смотрю, пока гуляю с ребёнком:)

Если собираетесь на Joker и ещё не купили билеты - не торопитесь, в прошлый раз мне чуть меньше чем за месяц до конфы дали промокод на скидку 20%

#talks@ergonomic_code
🔥172👍2
Ну и собственно очередной досмотренный видос - Test automation without a headache: Five key patterns.

Мне понравился, рекомендую.

Паттерны:
1. разделяйте ожидания, workflows и взаимодействия - эту штуку я с наскоку не понял, но кажется в ней что-то есть, буду разбираться.

2. Симулируйте только те штуки, которые вы полностью понимаете. Это классика из Growing Object-Oriented Software, что мокать можно только собственные классы.

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

4. Оптимизируйте для решения проблем, не для скорости написания тестов. Тут докладчик говорит об оптимизизации времени поиска причин падения теста, а я бы ещё добавил и оптимизацию времени поддержки тестов при рефакторинге - в идеале оно должно равняться нулю

5. Реорганизуйте тесты для discovery. Посыл вроде как "организуйте тесты не воркуг юзер стори/итераций, а вокруг функциональности". Но лично я ни разу не видел первый вариант.

Есть ещё версия на ютубе с субтитрами и короче на 25 минут

#talks@ergonomic_code #tdd@ergonomic_code
Привет!

Прочитал Good to Great: Why Some Companies Make the Leap...And Others Don't

В книге описывают результаты исследования, где взяли 11 контор, стоимость акций которых 15 лет росла как рынок, а с определённого момента 15 лет подряд росла минимум в три раза быстрее рынка;
Для этих контор взяли 11 контор из тех же индустрий, которые 30 лет росли не лучше рынка;
И ещё взяли 6 контор, у которых был переломный момент, но 3ёх-кратный рост продлился менее 15 лет.

А потом попытались понять, чем 11 "good-to-great", контор похожи между собой и отличаются от остальных.

И вроде как поняли и описали что надо делать, чтобы забацать "great company".

И среди прочего, как это не удивительно, надо собрать правильных людей.
А правильные люди - это не те, которые прямо сейчас обладают нужными вам скиллами, а те, которые обладают характером, нужным для вашей организации.
Second, widen your definition of “right people” to focus more on the character attributes of the person and less on specialized knowledge. People can learn skills and acquire knowledge, but they cannot learn the essential character traits that make them right for your organization


И на мой взгляд это даёт ответ на вопрос, что надо спрашивать на собесах - алгоритмы и технологии или книги и петпроекты и прочие вопросы "за жизнь".

#books@ergonomic_code #hr@ergonomic_code
🔥2
Привет!

Пошёл полистать Lean Architecture в поисках инфы как писать юзкейсы и вообще форматов описания требований.

И потихоньку переполз на DCI.

И в это прочтение у меня три новых мысли возникло.

Мысль первая

Тезис, что ООП - это то как мир представляют нормальные люди - фикция.

ООП, по определению объекта (состояние + поведение), предполагает активность объектов.

Например счёт может (сам) сделать перевод. Или напечатать себя.

Но обычные пользователи (в лице моей жены) не мыслят категориями, что счёт сам переводит деньги или сам себя печатает. Они (она) мыслит скорее категориями 19-ого века, где есть карточка с информацией о счёте, и служащий банка, который выполняет её поручения по переводу денег или печати счёта.

Наверное поэтому, "полнокровная" доменная модель не вытеснила анемичную доменную модель в мейнстриме. Тупые сущности и умные сервисы - это то, как наши пользователи думают о наших информационных системах.

Мысль вторая

Возможно, за всей этой движухой лежит желание получить полиморфное поведение - удобно же иметь PrintableAccount, который можно использовать в обработчике "Напечатать выписку" и не парится когда, появляется новый тип счёта.

Но это не удобно, когда появляется новая операция над счётом (закрыть, например) - тогда придётся писать код закрытия счёта в пачке классов, а не одной функции.

Это типовой пример Expression problem (aka Data/Object Anti-Symmetry из 6ой главы чистого кода).

И, наверное, ООП и правда стрельнуло только на волне GUI - там как раз чаще появлялись новые типы, чем новые методы.

Но на бэке информационной системы, намного чаще появляются новые операции, чем типы.

Поэтому "полнокровная" доменная модель ведёт не только к тому, что изменения приходится вносить в множество мест, но ещё и к огромным с десятками методов для совершенно разных контекстов объектов с низким cohesion и высоким coupling.

Мысль третья

DCI - это ещё одна попытка решить expression problem.
И если у вас её нет (а у меня нет - очень редко появляются иерархии типов сущностей) - то и DCI не надо.

#whynotoop@ergonomic_code #dci@ergonomic_code #lean_architecture@ergonomic_code
👍4
И в догонку накопал жёский наборос
It is high time to ‘kill’ Clean Architecture, the Hex, the Onion and the VSA and come up with something better

One of the most common ‘architectures’ among aspiring software designers is either the Clean Spaghetti Architecture or the Hexagonal Bubble Architecture. I don’t blame them for using these antipatterns. At least they are trying something new. They are going out of their comfort zone, ready to make mistakes. Here is a visual representation of both these architectures

В целом-то я с ним согласен, что без разделения бизнес-логики и ввода-вывода или хотя бы наличия двух и более реализацией гейтвеев/портов обе этих архитектуры приносят только лишний шум.

Но вот чтобы так жёска и прям с порога - это надо иметь смелость:)

PS> сам пост ещё не прочитал

#clean_architecture@ergonomic_code
Этот неловкий момент, когда не хватает ширины UltraWide экрана.

#изжизниспикера
Привет!

Тизер из доклада - я кажись нашёл железобетонный аргумент и пример в пользу ФА.

Это графы вызовов методов одной и той же реальной функции реального коммерческого проекта.

Слева - сделано "как обычно".

Справа - по ФА/ЭП.

Правый граф в проде (давно уже).

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

И правый граф работает в 300 раз быстрее (я замерял).

Оба графа сделаны на Java 8 + Spring Boot.
Левый граф сделан на Hibernate, правый - Spring JDBC Template, с небольшими вкраплениями Hibernate

Если и после этого люди ещё будут сомневаться нужно ли им ФП/ФА - я умываю руки.

#functional_architecture@ergonomic_code #whyfa@ergonomic_code #whynotjpa@ergonomic_code
👍10
Привет!

В комментах к прошлому посту "внезапно" (🤦‍♂️) выяснилось что разные люди под "ФП" понимают разное.

Поэтому накатал коротенький микропост о том, что я имею ввиду говоря ФП

#terminology@ergonomic_code #fp@ergonomic_code #functional_architecture@ergonomic_code #oop@ergonomic_code #ergo_approach@ergonomic_code
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥3
Привет!

Написал тест, что миграция вставляет коробочные данные, нашёл баг, что забыл прописать sql-файл с миграцией в ченджсет, пофиксал, сэкономил часик-другой отладки на стенде, профит.

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

#tdd@ergo_approach #case@ergo_approach
💯11
Эргономичный код
Если собираетесь на Joker и ещё не купили билеты - не торопитесь, в прошлый раз мне чуть меньше чем за месяц до конфы дали промокод на скидку 20%
Привет!

В этот раз публичного промокода не дали, но подсобить всё равно могу - приходите в личку, если планируете покупать билет.
Привет!

Я пару лет назад пинал декомпозицию по фичам, за то, что
Когда же вы сядете и попытаетесь декомпозировать систему по фичам, у вас тут же возникнет множество вопросов:


"А фича - это вообще что такое?", "Как мне из требований получить набор фич?", "Судя по примерам, фича - это таблица. Мне что, заводить по пакету на каждую таблицу?", "А что делать с таблицами связками?", "Что делать с функциями, которые затрагивают две и более таблицы - в какой пакет их помещать?", "А что делать с функциями, которые работают не с таблицами, а с REST API?", "А с S3?", "А куда мне положить DSL создания Excel файлов для нескольких фич? В utils?"


И тут я кажись познал дзен. Фичи надо искать не в требованиях, домене или таблицах/сущностях.
Их надо искать в порядке и этапах реализации требований и т.д.

Я это ещё не отрефлексировал толком и может на самом деле идея тупая, но, кажется, когда начинаешь работу над задачей - очевидно, это какая-то новая фича, или доработка какой-то существующей фичи.
В первом случае - вы заводите новый пакет/директорию, во втором случае - дописываете код в старых пакетах.

Однако в целом я всё ещё не сторонник тотальной вертикальной декомпозиции в духе вертикальной архитектуры, декомпозиции по фичам или моей же декомпозиции по объектам (раз, два).
Потому что по моему опыту они ведут к слишком высокой сцепленности (явной или не явной) между слайсами/фичами/объектами.

Но вот использовать декомпозицию по фичам как вторичную - после разделения на слои приложения и домена (what-the-system-does и what-the-system-is в терминах Lean Architecture) - кажется вполне себе вариант.

#design@ergo_approach
3👍2
Привет!

В комментах задали вопрос:
А есть какое-то описание принципов обработки ошибок в ЭП? Типа где обычные исключения, бизнес исключения, где возврат значения (Result?) и почему и т.п.


И ответ получился размером с небольшой пост, так что решил запулить его в канал.

Прямо сейчас стратегия обработки ошибок не то, что не описана, но даже не детализирована.

Но общее видение такое:

Я исхожу из того, что существует 4 типа ошибок:

1. Ошибки оборудования
2. Ошибки программиста бэка
3. Ошибки программиста фронта/поломки обратной совместимости
4. Ошибки, которых не должно быть.

Ошибки оборудования и внешних сервисов
Это отказы жёстких дисков, сети, внешних сервисов и т.д. Их чинят девопсы. Или программисты внешних сервисов. И для починки нужен редеплой (вплоть до физического развёртывания нового сервера) оборудования или внешнего сервиса

Ошибки программиста бэка
Логические ошибки - NullPointer, IllegalArgument, IndexArrayOutOfBounds и т.д. Их чинят разрботчики бэка и для починки требуется редеплой бэка.

Ошибки программиста фронта
Все ошибки когда запрос кривой - вообще не HTTP, HTTP, но не ложится на модель бэка, на модель ложится но является внутренне противоречивым (этого лучше избегать). Их чинит разработчик фронта и для починки требуется редеплой фронта.

Ошибки которых не должно быть (ака доменные ошибки)
Это когда софт работает "нормально", но пользователь с его помощью пытается сделать то, чего нельзя. Их чинит юзер, без какого-либо редеплоя.

Возьмём пример из Trainer Advisor, где при создании приёма есть проверка на то, что нельзя создать для одного терапевта приём, пересекающейся с существующим. Но для лучшей иллюстрации давайте представим, что речь идёт не о терапевте, а о зале - нельзя создать два приёма в одном зале, которые пресекаются по времени.

И вот сейчас у меня это реализовано максимально дёшево - я проверяю наличие приёмов на запросе создания и если он есть - показываю ошибку юзеру

Более дорогой вариант позволил бы избежать избежать этой ошибки для одного терапевта - если бы я проверку выполнял фоном в процессе заполнения формы и просто блокировал бы кнопку подтверждения в случае попытки создать пересекающийся приём, то терапевт физически не смог бы сделать фигню (считаем, что терапевты не владеют curl-ом)

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

А ещё можно сделать обмен данными с форм создания/редактирования, чтобы... Ладно, в общем я утверждаю, что вероятность подобных ошибок можно довести до столь малой, что их можно "упросить" до ошибок оборудования или разработчика бэка.

И вот исходя из этой классификации у меня общая стратегия следующая:
1. Ошибки оборудования и разработчика бэка - по максимуму игнорим. Они вылетают исключениями из библиотечного кода и прямой наводкой летят в глобальный обработчик ошибок, который покажет юзеру "У нас проблемы, работаем, попробуйте позже"
2. Ошибки разработчика фронта - в идеале аналогично, но только лучше веб-фреймворк настроить так, чтобы он сам с ними разбирался.
3. А вот "доменные" ошибки - в идеале должны возвращаться в виде Result-а и контроллер/фронт должны показать юзеру понятную ошибку и дать инструкции по её устранению

Однако есть нюанс - Spring не дружит c Result. И если вернуть ошибку в виде Result, а не исключения - он транзакцию не откатит. Это можно захачить кастомной аннотацией, но не хочется.

Поэтому текущие правила работы с ошибками у меня такие:
1. Ошибки оборудования я стараюсь по максимуму игнорить.
2. Ошибки разрботки бэка (помимо тех, что рантайм мечет) я мечу стандартными error, check, require и т.д. и они улетают наружу 500-ками
3. Бизнес-логика возвращает ошибки Result-ом (в TA такого ещё нет)
4. Оркестрация выбрасывает ошибки (в том числе Result от бизнес-логики) исключениями
5. Контроллеры ловят исключения в Result и разбирают его в функциональном стиле
6
Привет!

Список рекомендованных анкл Бобом книг
Я прочитал всё кроме Fundamental Algorithms и Analysis Patterns - есть ли в мире больший задрот, чем я?🤣🤓

#books@ergonomic_code
🥰3
Привет!

Недавно зарелизали Postgres 17.
Для меня актуально - как всегда всякие оптимизации и больше поддержки json-а
🔥5
Привет!

Пока готовил доклад наткнулся на прикольную штуку: Cognitive Complexity - метрику оценки сложности кода, которая явно ближе к правде, чем Cyclomatic complexity.
Она, на мой взгляд, тоже не идеальна, так как не учитывает наличие и количество изменяемого состояния (а это важно) и позволяет "замести" сложность под вызовы функций, но даже в текущем виде полезна и подсвечивает места, с которыми точно надо что-то сделать.

И для Идеи даже есть плагинчик, который прям в редакторе рисует сложность методов. Только для Котлина он не учитывает ветвление потока управления даже в базовых map, filter, forEach и т.д., поэтому может сильно приврать в сторону простоты. Но если в коде откровенное кровавое месиво - он это подсветит.

#tools@ergonomic_code #craftsmanship@ergonomic_code
👍6🔥2
Привет!

У меня сегодня был прогон доклада с экспертом - Максом Моревым - и на обсуждении вспомнили почему в Красной книге (второй библии ДДД) примеры на Java:
First of all, and sad to say, I think there has been a general abandonment of good design and development practices in the Java community. These days it may be difficult to find a clean, explicit domain model in most Java-based projects.


Прежде всего, к сожалению, я думаю, что в Java-сообществе наблюдается общий отказ от хороших методов проектирования и разработки. В наши дни сложно найти чистую, явную модель предметной области в большинстве Java-проектов.

И наш с Максом опыт говорит о том же.

Так как вы в этом канале - эта цитата не про вас - и если в какой-то момент вам покажется, что вы придумываете сложности на пустом месте, а все вокруг просто нормально работают - это не так. Вы делаете нормально - а все вокруг через чур упрощают себе решение задачи в моменте, чтобы через год получить сложный неподдерживаемый ком грязи. В котором снова сделать самый примитивный "солюшен" и ещё через год получить ещё более сложный кода.

#books@ergonomic_code
🔥7
Привет!

Эх. А вот взять бы Clojure, ClojureScript, Datomic, htmx и hiccup - и тогда ВООБЩЕ ВЕСЬ проект можно сделать на ОДНОМ ЯЗЫКЕ. Фактически в одном процессе. Как будто десктопное приложение пишешь...

#clojure@ergonomic_code
👍4🤯2🥴2
Привет!

Готовность два часа - все готово к эфиру
7🔥7👍3