Pull to refresh
41
0
Роман Лиман @kagetoki

F#/C# developer

Send message

Спасибо, кэп. Видимо, мне надо развернуть свою мысль, чтоб донести: при прочтении создается впечатление (лично у меня, по крайней мере), что ребята думают, будто микросервисы сами по себе привносят порядок, и что концы отыскать в них гораздо проще, как и "накатить небольшой скриптик". Сложность поиска концов зависит не от монолитности/микросервисности, а от качества проектирования и организации кода в этой большой системе. Если клепать их в прод "из-под ножа" и потом накатывать скриптики туда же — это будет хорошо работать первые несколько месяцев, пока контекст целиком помещается в голове хотя бы у кого-нибудь. Когда система разрастется, всплывет сразу целый ворох проблем с eventual consistency, несоблюдением контрактов, multiple sources of truth, потерянными сообщениями в очередях, невоспроизводимостью окружения и тд, если сразу не прилагать колоссальные предупреждающие усилия и не внедрять правильные инженерные практики.


Как они собираются внедрять такие практики, нанимая за год 600 программистов, я не знаю, но с интересом бы посмотрел. Если у них уже на данный момент не работает хотя бы ~2000+ программистов, кризис роста обеспечен. Если 2к уже есть, то при 75% аутсорса лох в этой схеме я, пушто там опилки от бюджета больше моей годовой зарплаты должны быть.

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

О сколько вам открытий чудных готовит просвещенья дух

Ну теперь есть райдер от жидбрейнсов, а в последних версиях команда сильно работала над улучшением перформанса компилятора и тулинга.


Так что можно обратно на F#!

It's pretty close to free monad. You can check free monad recipe out. Also there's turtle program interpreter example.
Someone please correct me if I'm wrong, but free monad adds another layer of abstraction on top of interpreter: when in interpreter you have an exit node in you initial instruction tree, free monad has an additional type with 1 node for instruction (without exit node) and 1 exit node.

Во-первых, можно использовать все то же самое (но лучше этого не делать)
Во-вторых, функции, особенно в F#, очень легко композируются.
Если делать именно dependency injection, то для этого помогает partial application, т.е вы просто указываете в вашей функции аргументами все необходимые зависимости, а реальный инпут указываете в самом конце:


let getUser getUserDtoFromDb mapUserDto userId =
    userId |> (getUserDtoFromDb >> mapUserDto) // пихаем айдишник в функцию
//созданную на лету из наших двух с помощью оператора ">>"

let getUserImpl = getUser MyRepo.getUser MyMapper.mapUser // передаем зависимости (2 параметра из 3 необходимых, и получаем функцию, которая принимает айдишник и возвращает юзера из базы

Но гораздо лучше делать dependency rejection и луковую архитектуру. Функции все так же будут легко композироваться, но будут более гранулярными и легче тестироваться.

Я абсолютно согласен с вами касательно семантики исключений, goto и читабельности.
Раз уж вы решили использовать монады Result & Maybe, и вас при этом беспокоит невысокая читаемость конструкций работы с ними, в силу особенностей языка C#, у меня возникает вопрос: рассматривали вы для себя возможность перехода на F#, и если да, то почему воздержались?


Работать с монадами там гораздо проще, как и определить конструкцию Result (которая уже есть в стандартной библиотеке начиная с версии 4.1):


type Result<'Ok, 'Error> =
      | Ok of 'Ok
      | Error of 'Error

и благодаря Computation Expressions "зараженность" резалтами больше не выглядит страшно:


let createUser userDto =
    result {
         let! validatedDto = validate userDto //в случае Error возвращается ошибка, в случае Ok исполняется дальше
         let! userId = create validatedDto
         return userId
    }

то же самое с Maybe.


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


На эту тему есть отличная книга Скотта Влашина

Вы сейчас воды налили два ведра, совершенно непонятно, что конкретно вы хотели сказать. Я встречал людей, которые использовали подобные термины, имея ввиду примерно следующее:


  • Отказался перерабатывать нахаляву — плохо относится к компании.
  • Не хочет больше подтирать за одним и тем же сотрудником — нет эмпатии/соучастия
  • Требует законный отпуск, хотя просят не уходить — не может войти в положение, выслушать
  • Устал от бесконечных костылей, говорит, что время инкрементального рефакторинга давно упущено, теперь только выбросить и переписать, а продолжать так жить больше нельзя — истерит и закапывается в перфекционизм, не может предложить решение
  • Разработчик объясняет срыв сроков тем, что руководитель проекта постоянно меняет требования в последний момент — не может взять ответственность на себя

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

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

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


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


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


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


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

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


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


В общем, не рокет сайнс это никакой, думать надо головой и мат модели строить правильно, требования зарания выяснять, опять же. А не "там походу разберемся".

Это я к тому, что вероятно, неверно относить язык к тому или иному виду типизации

Язык либо динамически типизирован, либо статически. Он не может "плавать" где-то посередине. В языке могут быть различные средства для того, чтобы что-то почерпнуть из другого мира, например dynamic в C#. Но от того, что в сишарпе есть dynamic он не стал динамически типизированным.
Еще, например, в С# есть неявное приведение типов. Но оно работает, только если заранее описать, как оно должно работать, оно не работает для всего и всегда. Да и делается обычно это для каких-то совсем очевидных случаев, вроде int -> long.


Между тем, утиная статическая типизация это все равно жесткая типизация, абсолютно не важно, что она не номинальная. Она все равно жестко следит за соблюдением объявленного контракта, и если этот контракт нарушить, то ошибка проявит себя еще до запуска приложения: код не скомпилируется.


Как бы вы ответили на вопрос: какой язык лучше, со слабой статической типизацией, или сильной динамической?

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

Нет, не у всех, я видел контрпримеры, как живьем, так и просто очень качественный код чужих проектов. Большинство факапов, которые я видел, вообще можно было бы избежать, тупо следуя самым базовым принципам DDD. Да что там ДДД, достаточно было просто сделать нормальную слабосвязанную систему, правильно разграничив ответственности. Но нет, об этом думать нам слишком долго, нам поформошлепить надо побыстрее, пользователи уже 10 минут новых фич не получали.
Если плохо понять природу проекта или плохо уметь проектировать, конечно же, получится все ровно так, как вы описываете. Сначала будет трата времени на бойлерплейт, потом на борьбу с ним и болезненное натягивание совы на глобус.
Кстати, именно так и происходит, когда за архитектуру берется мидл, даже сообразительный — просто опыта не хватает.

Что касается неряшливого прототипа — тысячу раз видел, как такой прототип внедряется, девелоперы заранее предупреждают, что он дерьмовый, и бизнес говорит «дадада, обязательно перепишем, и не раз». Спустя 2 года это дерьмо все еще в продакшне, обвешано миллионом костылей, и чем дальше, тем хуже и тем больше все ссут его переписывать, потому что уже никто не знает, как оно работает и что отвалится, если убрать.

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

Как тут уже не один раз заметили, типы в первую очередь рождаются в голове, и только потом переносятся в код. И если они родились убогими, то там уже ни динамика, ни статика не спасут.

Отдельно добавлю, что во многих мейнстримных языках (C#, Java, Go etc.) система типов слабая, не хватает банальных типов-сумм, я уже молчу про HKT. Тем не менее, это уже лучше, чем ничего.
Объекты могут незначительно различаться, но при этом все равно быть успешно распарсены одной JS функцией

придется обертывать в try/catch практически весь код, а не предположительно наиболее уязвимые участки кода

Для условно «целых» чисел, не содержащих разделители, эта проблема неактуальна.

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


"Наши самолеты в целом более менее скорее летают, чем падают. Предположительно большинство из них долетит до места назначения, наиболее похожие на критически уязвимые места в коде мы с большой вероятностью обернули в try/catch".

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


Вы упускаете суть строгой статической типизации: она защищает от ошибок проектирования. Если система типов имеет еще и алгебраические типы данных, то при правильном проектировании она защищает от ошибок бизнес-логики. Это очень хорошо описано в книге Скотта Влашина "Domain Modeling Made Functional", настоятельно рекомендую.

А для чего придуманы знаки препинания и заглавные буквы?


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

В любой программе есть непредсказуемая и предсказуемая части. Даже если вы обрабатываете "непредсказуемый" поток данных, эти данные, во-первых, имеют какие-то общие черты (иначе почему вы вообще их обрабатываете вместе?), а во-вторых, эта программа имеет другую часть — собственно часть, отвечающую за саму обработку, и эту вторую часть можно спроектировать в устойчивых абстракциях. Кстати, даже в статической типизации непредсказуемую структуру можно всегда описать хэштаблицей.


Другое ее применение — там, где поток требований к программе быстро и непредсказуемо меняется, и расходы на статические описания элементов программы (никто не будет спорить, что это расходы?) себя не оправдывают. Пример — веб-разработка.

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


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

Что такое универсальные приложения? Это когда и мессенджер, и календарь, и ядро ОС немношк?




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


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

После многих споров на тему статика vs динамика, у меня создалось устойчивое впечатление, что разработчики делятся на 2 лагеря:


  • Те, кто предпочитают думать, прежде чем что-то делать, а именно построить математическую модель и потом ее имплементировать.
  • Те, кому ближе философия "че тут думать, прыгать надо", а именно набросать что-то, а потом по ходу разбираться.

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

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

О каких издержках кстати речь? Что код тормозит? Или процесс разработки слишком долгий? Или памяти много потребляется?
Есть сложные задачи, в которых нет однозначного алгоритма, а его еще надо найти. Писать такие на статических языках — ад.

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

Information

Rating
Does not participate
Location
Сингапур, Сингапур, Сингапур
Registered
Activity