Продолжаю работу с ментатами 🤓
Поймал себя на том, что стал замечать как те или иные рекомендации по хорошему стилю кодирования сводятся к каким-то более сильным базовым вещам. Видел эту идею у вас в блогах, что под множеством всяких разнообразных советов и рекомендаций лежит условный десяток базовых сильных вещей, на основе которых уже и составляют эти советы и правила.
Появилось чувство, что одну из таких идей я осознал. Ну либо просто смог под разными советами увидеть общую основу...
Коллеги довольно благосклонно воспринимают всякие нововведения вроде map-reduce и производных от них, потому что эти вещи делают код чище. Логика локализуется в одном месте и становится прозрачной. Вместо нагромождения вызывающих друг друга методов появляются отдельные, легко тестируемые чистые функции, а так же универсальные (и тоже чистые) элементы dataflow: цепочки и разветвители.
...Тут 3 проблемы:
- нужно поддерживать больше состояний из-за Cancel();
- при вынесении подзадач в dataflow-классы нужно НЕ ЗАБЫТЬ вызвать Cancel() для них (а потом приходят Crash-репорты, потому что забыли);
- LLM-ки нещадно жгут токены и эпично галюцинируют пытаясь это всё распутать в репозитории на 15Гб.
Решение: LLM-ки видят dataflow Args->Result + единственное явное состояние TaskOwner. Порой даже агент запускать не надо - код пишется клавишей Tab.
Насколько же легко стало это тестировать!!!
Сколько комбинаций можно построить на остнове предыдущих приёмов..
Для меня здесь программирование превращается в математику. О чём я почти забыл...
Моя консольная песочница для алгоритмов, переписанная со стиля "говнокод" на монады...
Когда я первый раз читал про призрачное состояние и погрешность спецификации, казалось — ну, теория, бывает. Потом открыл наш код и нашёл всё это буквально за полчаса. Причём в местах, где мы ходим каждый день и считаем их «нормальными». Это и есть самое неприятное в таких вещах: они не выглядят как проблема, пока не смотришь специально...
В некоторых частях рабочего проекта, куда еще не пришла строгая система типов, я все-же предпочитаю использовать контрактный подход: явно указывать инвариант, следующий за предусловием/спецификацией функции. Как минимум это дисциплинирует разработчиков: когда ты понимаешь, что есть явный риск исключения, ты и тест напишешь, и себя еще проверишь. :)
Спасибо за предоставленную паузу - использовал её с пользой: закончил последний курс и получил диплом программиста. Также за это время прошёл отбор в Школу 21. Кстати, нашёл там ту самую эхо-камеру, про которую вы рассказывали :)
Однако сейчас мне придётся уйти от вас на длительный срок, так как ухожу в армию на срочную службу.
Основное архитектурное решение, которое я плохо понимаю на своем проекте - выделение адаптеров в отдельные микросервисы.
Адаптеры делают буквально то, чем называются - инкапсулируют логику обращения ко внешним АПИ, приводят данные к доменной модели и фиксируют контракт для основного сервиса-потребителя. Это, конечно, хорошо, но совершенно не понимаю смысла выделения их в отдельные микросервисы - из-за этого возникают проблемы следующего характера...
1. При смене внешнего АПИ все равно происходит дрифт контракта, например, при смене способа пагинации.
2. Адаптер не отдает данные as is, он их еще и фильтрует, тем самым размывается ответственность и бизнес-логика между разными сервисами.
3. Ну и лишний сетевой хоп, со всеми свойственными ему проблемами.
Я в принципе пока рефакторил, понял все основные паттерны взаимодействия компонентов, и ещё раз убедился что неизменяемое состояние решает 90% проблем. Вообще всё же главные инсайт был, что всё таки разделение данных и операций над ними это удобнее. Если у нас нет структурных инвариантов то каждый well typed instance какого то типа у нас валиден, и тогда зачем нам прятать данные, мы же в любом случае хотим из них либо что то посчитать, либо замэпить на уровень ниже/выше и тд. И как раз таки если очень грамотно продумать систему типов/данных для какой то области, выразить через типы как можно больше и понять связь между ними, то написать функции для конкретного юз кейса это уже второе. Но очень большой упор надо конечно делать на данные, это я понял уже сейчас :) Конечно сейчас я уже понял и с ваших рассказов, что только с 3 раза получается что то работающее(условно), и сам тоже убедился :)
В принципе этими размышлениями я был занят большую часть времени....
С другой же стороны, абстрагирование от текущей реализации и примерка нового дизайна на дизайн существующий, это то что заставляет мысли немного покипеть, так как чаще всего необходимо произвести операцию встраивания какого-то изменения в существующую систему, а так как изменение происходит на более высоком уровне чем просто код, переложить эту миграцию дизайна на код не является операцией быстрой...
Post #2512
560
- ❤ 29
- 🔥 7
- ✍ 5
- 🤯 1