Алексей @jdev
Эксперт по эффективной разработке, Kotlin техлид
Information
- Rating
- 1,522-nd
- Location
- Кольцово, Новосибирская обл., Россия
- Date of birth
- Registered
- Activity
Specialization
Chief Technology Officer (CTO), Software Architect
Lead
From 350,000 ₽
Functional programming
Object-oriented design
Design information systems
TDD/BDD
Kotlin
PostgreSQL
Java Spring Framework
Linux
Git
Docker
Да, это правда. В моём случае так и есть - последние 4 года я либо делаю проекты с нуля, либо реорганизую их.
В этом случае, имхо, надо держать в голове только два стандарта - тот за который платят прямо сейчас, и тот, за который вы хотите чтобы вам платили:)
Цель для меня - психологическое благополучие - у меня низкая стрессоустойчивость, поэтому в условиях горящих сроков и критов на проде мне жить некомфортно. А цель для бизнеса - минимизация суммарной стоимости владения продуктом. Структурный дизайн - это часть моего комплексного подхода к разработке бакендов. И опыт показывает что на горизонте от 6 месяцев работы этот подход позволяет снижать трудозатраты на разработку, в том числе за счёт снижения количества багов.
Нет, это действительно проблема и я её решаю описывая свой подход и рассказывая про него. Но опять же опыт показывает, что даже джуны за 2-3 месяца могут понять как писать такой код и потом их надо только на ревью минимально контролировать.
Значит у вас всё хорошо со скоростью и качеством разработки:)
Если вы любите хайлоад, то лучше меня знаете, что все оптимизации делаются на основе конкретных цифр, конкретного кода. Поэтому в общем случае и правда не ясно.
Конкретно в моей практике нехайлоада (там где вопрос производительности вообще возникает) производительность только улучшается, так как в 100 случаях из 100 она упирается в неэффективную работу с БД.
В оригинальных книгах речь не о сбалансированной форме дерева, а о балансе уровня абстракции основных ветвей.
Тут вам, возможно, будет интересен другой мой доклад - Рациональный подход к декомпозиции систем на модули или микросервисы. Я там как раз рассказываю о своём подходе к декомпозиции системы на минимально сцепленные модули-объекты. Правда я сейчас отказался от него:)
Да это так, но тут сильно повлияла специфика Joker - там ПК очень любит "мясцо"
Написать гайдлайн по проектированию кода трансформаций я тоже собираюсь. Пока что планирую взять за основу Stratified Design. Он хорошо расписан в книге Grokking Simplicity и есть доклад на эту тему от автора книги (сам не смотрел ещё)
К такому проекту я и на пушечный выстрел не подойду:)
Но если проект на 3M строк и 100 разработчиков попилен на 30 сервисов по 100К кода и покрыт качественными тестами - каждый сервис мигрируется также в пределах рабочего дня. Тот же проект, когда он был уже на 50К строк Котлин кода (и я его уже этом размере попилил на два сервиса) на минорные версии обновлялся за минуты.
Я в прошлом году переводил проект меньше раз в 7 (~300 Котлин файлов), но порядка на 2-3 быстрее:
Для перехода я:
очевидным образом, обновил версии
заменой по проекту поправил javax.* -> jakarta.*
Поменял com.github.tomakehurst:wiremock-jre8:2.35.0 -> com.github.tomakehurst:wiremock-jre8-standalone:2.35.0. Без этого были проблемы со спекой сервлетов, что ли
В конфиге Spring Security заменой по файлу поправил antMatchers -> requestMatchers
Руками поудалял ConstructorBinding
Больше всего времени ушло на то, чтобы реанимировать вытягивание доков к эндпоинтам в сваггере из котлин доков. Для этого пришлось руками добавить зависимость runtimeOnly("com.github.therapi:therapi-runtime-javadoc:0.15.0"), а допетрить до того, что этот джарник пропал из зависимостей (без ошибок) пришлось самостоятельно
всё. Все 100+ тестов (преимущественно пользовательских/внешних/функциональных/е2е) прошли, приложение благополучно задеплоилось на тестовый стенд. И потом обошлось без факапов в проде
Всё это я сделал часа за два грустным вечером 5-ого января:)
Возможно секрет в том, что проект у меня был на Spring Data JDBC:)
Блин, это я уже после правок поворошил блог и опять линку сломал 🤦♂️ снова поправил
О каких именно sql-е и идеях вы говорите?
Процедурное программирование. Я так называю подход, когда классы сущностей (данных) изменяемые и имеют только геттеры/сеттеры, а вся логика - в сервисах/юзкейсах
Я на Паскале писал ток лабы и курсовик в первом семестре первого курса, у меня с тех пор от собственного кода вьетнамские флешбеки, которые в моей голове намертво приколочены к BTP - короче я. ничего. не. забыл :)
На хабр пришло поколение людей, которые родились позже (де-факто) смерти Borland Turbo Pascal:)
В мокей картине мира Functional Core/Imperative Shell = структурный дизайн + неизменяемая модель данных.
Т.е. преимуществ нет, а большая для вас сложность закралась где-то в моём объяснении и/или вашем восприятии:)
В общем если вы владеете FC/IS - вы умеете и балансировку делать:)
Ещё, кстати, мысль дилетанта - а оно (архитектура) вам вообще надо?:)
Бакендеры заморачиваются, потому что бакэнды живут годами и десятилетиями. И покопавшись в говне мамонта невольно начинаешь задумываться о том, чтобы в следующий раз позаботиться о себе и сделать понятную и поддерживаемую кодовую базу.
А игры, кажется - максимально быстро запилил, зарелизал и ещё до релиза забыл про первую игру и начал пилить вторую.
Спасибо, поправил, на всякий случай продублирую:
Как я превратил легаси-проект в конфетку за полгода. Том 1
Как я превратил легаси-проект в конфетку за полгода. Том 2
Микроретро Проекта Э
Я правильно понимаю, что второй кейс с функцией назначения водителя вы тоже занесли в "примитивные программы, типа CRUD"?
В таком случае да, СД покрывает только их.
Но тогда боже упаси меня писать то, что вы называете сложными программами:)
Не
заниматься ерундойделать оверинжиниринг, как пацаны из третьего кейса:Не выдумывать, что у вас будет ввод и из консоли, и из смс и вы всё это будете разруливать в одной кодовой базе. Сделайте бэк с игрой, смс-шлюз который дёргает бэк и настольное приложение которое дёргает бэк
Переводы команд на разные языки вынесите в ресурсы. Соответственно, когда приходит команда - идёте в файлик с ресурсами и получаете каноничное имя
Возьмите РСУБД (на такую игру - точно хватит) и храните данные только там. Следите за тем, чтобы это решение не текло через АПИ кода, хранящего состояние игры
Безусловно, бывают случаи, когда есть вариативность в технологиях - тогда вводите интерфейс между кодом управления и кодом реализации io и всё.
Но вообще, см. коммент выше - в докладе и статье я держал в уме разработку бэков информационных систем. Разработка игр, как и фронта, как и АСУТП, как и встроенного софта - имеют свою специфику и не факт, что идея сбалансированной формы там применима.
Я к геймдеву вообще никакого отношения не имею, поэтому возможно фигню сморожу, но у меня такое мнение - львиная доля информации по архитектуре (включая мой доклад/статью), посвящена архитектуре (бакэндов) информационных систем. А "естество" бакенда сильно отличается от "естества" игры и поэтому эта информация плохо ложится на реалии разработки игр.
Опять же, для бэков у меня есть решение. Актуальную версию пока не расписал, но она базируется на алгоритме декомпозиции на базе эффектов (это тоже статья по мотивам доклада и этот доклад уже есть в публичном доступе на Ютубе).
Да, согласен, но когда реально делали проект я этот момент упустил (видимо и правда был с похмелья:) ), а сейчас хотелось оставить код максимально приближенным к реальному.
А за BoxesAggregate - спасибо. Вы, кажется, наконец-то мне объяснили идею "по агрегату на запрос", которую я полгода правильно понять не мог и из-за этого отвергал. Сейчас она заиграла для меня новыми красками, возможно утащу к себе.
Последние 4 года 90% моих тестов такие и есть - интеграционные, с БД в тестконтейнере и запросами по ХТТП.
И так как я работаю по ТДД и запускаю тесты по нескольку раз в минуту, мне пришлось научиться делать такие тесты более быстрыми, чем тесты на моках.
Два оснонвых секрета:
1) Не использовать @DynamicPropertySource, потому что это приводит к инвалидации контекста в кэше и запуску контекста для каждого тест-кейса
2) Использовать RAM-диск для постгреса.
Вместо DynamicPropertySource я использую такой трюк:
А чтобы посадить Postgres на рам-диск - такой:
В результате, у меня на i7-8700, 32 RAM, SSD интеграционные тесты выполняются от 14мс при тесте с моками в 163 мс:
А в проекте со скрина, я пошёл ещё радикальнее - отказался от @SpringBootTest и запускаю приложание руками, а в локальной разработке сначала ищу предзапущенную БД:
Это позволяет сэкономить ещё пару секунд на инициализации тестов, что имеет существенное значение, когда ты делаешь зелёным один тест кейс.
А вот ещё вспомнил пост
Я сам ограничился этим :)
Мне кажется тут надо смотреть не на количество клиентов, а на количество компаний/разработчиков.
Но вообще я вполне допускаю, что JPA стандарт только в моём инфопузыре и объективно она не так популярна, как мне кажется.
А и самое главное забыл - SDJ интегрируется с MyBatis из коробки:)
То есть вы можете писать в БД через SDJ, со всеми его плюшками, а читать через MyBatis со всеми плюшками ещё и MyBatis.
Не совсем понял ваш посыл.
Константин, проводил свои исследования в 60-ых - лет за 40-50 до хайпа ФП.
Я бы не стал связывать языки с поддержкой парадигмы и разработку в соответствии с парадигмой - на любом (Тьюринг полном) языке можно писать в соответствии с любой парадигмой, вопрос только в бойлерплейте. При том ФП парадигма + более-менее подходящий более-менее мейнстримный язык - это дешевле, на мой взгляд, чем ФП парадигма + чисто функциональный язык под который сложно найти разработчиков, библиотеки, документацию ко всему этому и решения не самых тривиальных проблем.
Но тут мы уходим в холивар, что такое ФП. В котором даже кложуристы с хаскеллистами не могут решить кто из них Труъ ФП.
Полагаю, основная причина - потому что в ВУЗах учат C/Java/Python etc.
Ненене, я в своём уме, я бы никогда не стал этого делать:) Этот пример не про то, что ФП быстрее, а про то, что ФП "понятнее" для компилятора, из чего я делаю предположение, что оно и для человека понятнее.
Ну тут мне кажется мы снова упираемся в вопрос, что такое ФП.
Например
Для меня - ФП. Нужна ли для докторская для чтения этого кода? Нет
Так Константин ровно то и проделал. Взял кучу программ с известной стоимостью, посмотрел что общего между дешёвыми программи и в чём разница с дорогими. Увидел что разница - в функциональной архитектуре.
Другое дело, что исходных данных нет - это да. Но так я и говорил о Гипотезе:)
Вы меня не верно поняли:) Я делаю вывод на основании эмпирического исследования Константина и собственного опыта. А эксперты и книги - это шло в разделе "Косвенные доказательства".
С реальным миром (с состоянием) невозможно работать в чистом функциональном стиле. Именно эту проблему и решает функциональная архитектура - разделяет императивный код, который работает с реальным миром и чистый код, который работает с красивыми моделями.
Соответственно у вас должен быть какой-то инфраструктурный код, который занимается работой с очередью - с одной стороны помещает данные в очередь, а с другой стороны - достаёт.
А вот что будет перед и после этого кода - зависит от задачи.
Как вариант, у вас на обеих сторонах может быть по координатору
Координатор на входе получает запрос по хттп идёт в БД, достаёт оттуда неизменяемую структуру данных, передаёт её в чистое ядро, получает результат обратно и перекладывает его в инфраструктурный модуль для публикации в очередь.
Координатор на выходе получает запрос от инфраструктурного кода и делает всё тоже самое, вплоть до публикации в следующую очередь.