Alex Gusev @flancer
Я кодирую, потому что я кодирую…
Information
- Rating
- Does not participate
- Location
- Рига, Латвия, Латвия
- Date of birth
- Registered
- Activity
Specialization
Fullstack Developer
Lead
From 3,000 €
JavaScript
HTML
CSS
Node.js
Vue.js
Web development
Progressive Web Apps
PostgreSQL
MySQL
GitHub
Никак не бьётся, полагаю. Шутка в тему (y)
В этом документе я попытался сформулировать базовые принципы, на которых я строю свою разработку. ESM и без транспиляции - вот это база. Детализация этих принципов "ещё в пути".
Если коротко, то чтобы можно было строить достаточно большие JS-приложения (для меня это 100-200 таблиц в БД) нужна декомпозиция всей кодовой базы на достаточно малые фрагменты (желательно два-три экрана кода без документации), описание интерфейсов этих фрагментов и инструмент по склейке всех фрагментов обратно в единую кодовую базу с учётом описанных интерфейсов (у меня это - Контейнер Объектов). Не надо держать в голове сразу всю кодовую базу, достаточно отдельных рабочих фрагментов и их зависимостей. Контейнер ориентируется по их идентификаторам и сообщит при старте, если чего-то там ой.
Если есть какой-то небольшой проект на JS, могу помочь настроить Контейнер Объектов, чтобы он автоматически находил нужные исходники, и объяснить, какие идентификаторы зависимостей указывать, чтобы Контейнер возвращал нужный объект в нужном lifestyle. Для начала желательно чистый nodejs - будет сильно проще. Также опыт работы с DI в других ЯП сильно приветствуется. Но нужно будет "сменить веру" - вообще отказаться от статических импортов в своём коде ;) Даже для node'вских модулей и пакетов. Статикой подгружается только сам Контейнер. Дальше всё через динамический импорт. Все привычные инструменты, завязанные на статических импортах, придётся выкинуть.
А Вы можете привести пример ЯП, который даёт такую гарантию на 10К контрактов? Причём не для теоретического "коня в вакууме", а для реального приложения со вводом-выводом, сетевым взаимодействием и пользовательскими данными? С учётом, что для веб-приложений это гарантия консистентности всего стека - от поля в форме на странице и до колонки в таблице в БД?
Нет, предлагаемый мной подход не даёт никому никаких гарантий. Пробуйте, если хотите, на свой страх и риск. Я долгое время имел дело с платформой Magento (порядка 4М SLOC во второй версии, и это без огромного количества плагинов к ней). Она никому ничего не гарантировала, но при этом как-то работала. На популярных use-case'ах - очень хорошо, на экзотических - очень плохо. Зачастую, после обновлений, приходилось брать в руки "напильник" (отладчик) и находить и править баги (не все, а только те, которые мешали жить пользователям). Я это называю "Magento way". В том числе и поэтому у меня любовь к "неизменённому коду" - приходилось отказывать от хороших, но обфусцированных плагинов, из-за невозможности оперативно интегрировать их в приложение в случае изменений (а они есть всегда) платформы или других плагинов.
Более того, я гарантирую, что когнитивный контекст каждого участника разработки уникален и отличается от контекста другого участника. Поэтому и нужны все эти митинги и документация, чтобы хоть как-то приводить все эти контексты в хоть какое-то соответствие. Лично для меня "источником правды" является код - что он делает, то и есть (отладчик в помощь, если непонятно). Да и то - нужно ещё посмотреть на окружение, в котором запускается этот код. А в документации может быть написано что угодно. Хорошая документация лишь помогает понять код "от общего к частному" (иерархически выстраивает когнитивный контекст, который адекватен текущей ситуации и помогает понять существующий код).
Да и вообще, я не считаю добром "тотальное описание типов". Я считаю добром "эволюционную устойчивость кода". Думаю, что это в какой-то мере антонимы, особенно на больших системах с кол-ом контрактов 10K+.
у меня IDE есть:
Код остаётся в исходном виде. И это делает его "прозрачным, доступным и удобным в сопровождении". Вы просто начинаете мыслить в тех же категориях, что и среда исполнения, а не среда написания.
Да, возможность написания плагинов "в одно лицо" (в полтора - с применением LLM), а потом объединение различных плагинов от различных лиц в одно приложение - один из фокусов развития платформы. Архитектурно - чистая калька с Magento, на которой я довольно долго сидел. А уж сколько я тамошних плагинов заинтегрировал друг с другом...
Так уж получилось, что я - веб-программист и именно fullstack. Я писал статью для людей со схожими интересами :) Про недостатки JS (и про многое другое) я не написал, потому что это не является моей целью. Мне важно было сформулировать наиболее значимые принципы, которые я использую в веб-программировании, а не достоинства-недостатки языка.
Интерфейс, по факту, это документирование контракта: имена функций и описание входных-выходных аргументов. Вот пример описания интерфейса в ES6:
И его имплементации:
Этот код работает прямо сейчас в одном из приложений.
Насчёт контроля типов со стороны языка вы абсолютно правы. Прежде чем писать без типов на JS нужно научится писать с типами, на той же Java. Я кодил с типами больше 10 лет. Мне уже можно без типов.
"Сказка в статье" - это построение когнитивного контекста для людей и LLM. Люди могут выбирать принимать этот контекст или нет. У LLM такого выбора нет :)
Вы, похоже, не совсем понимаете семантику слова "несколько". Очевидностей может быть более одной и я вам на это указал явным образом. В одной из них 99% пользователей чего-то-там не занимаются сборкой проектов. Вы же продолжаете упорствовать в том, что вы правы. Я уже с вами согласился - в своей системе ценностей вы правы. Если вы продолжите упорствовать дальше, то я с вами соглашусь во всём.
Ну а мне очевидно, что "пользователи" звучали в контексте "проект". Особенно после:
Мы с вами, очевидно, просто находимся в разных очевидностях. И - да, из вашей очевидности совершенно неочевидно следствие коллеги @SserjIrk про 99%, что вы и подтвердили. Полностью с вами согласен, что для ts-разрабов "до времени сборки проекта абсолютно не всё равно". Полностью согласен с коллегой @SserjIrk, что для 99% пользователей проектов до времени сборки нет никакого дела. Лично для меня не проблема существовать одновременно в нескольких очевидностях.
Ну так-то, по большому счёту, можно любой язык в любой другой транспилировать. Не всё, с какими-то ограничениями и потерями, но можно. Я с GWT (Java-to-JS) больше 15 лет назад плотно общался. За это время, уверен, искусство транспиляции одного исходного кода в другой выросло значительно.
Проблемы начинаются, когда узнаёшь, что то, что тебе надо, в целевом языке делается на раз, а в исходном противоречит философии языка. Вот тут - да, тут - вилы. А присвоения, ветвления и циклы в большинстве языков одинаковы.
С того, что 99% пользователей не занимаются сборкой проектов - они потребляют уже результат сборки (например, JS-код в браузере). Или вы только разработчиков за пользователей считаете? Для них - да, для них время сборки - проблема.
Поглядел на фото Альтмана в посте и почему-то вспомнил эту женщину в контексте вашего комментария.
Ну ладно, хоть про норвежцев с индусами не оспариваете :)
Механизм, работавший веками, сейчас движется по инерции? Вы таки зачем думаете сильные мира сего наперегонки строят себе дата-центры? Вот не дадут лично вам доступ к ИИ - и? Будете на смартфоне персональную модель запускать?
Рулят те, кто понимает, как работает механизм, который совершенствовался веками и всё ещё продолжает совершенствоваться. Я не говорю, что я понимаю как он работает. Я говорю, что механизм, работавший веками, не сломается в одночасье. А то, что он порочен, лишь наше собственное отражение. Каждый из нас добродетелен без меры, а к власти приходят сплошь порочные. Может какая-то из добродетелей у нас лишняя?
Общество планеты Земля не может торговать с другими планетами. Оно суверенно, но не нищает.
С этим согласен полностью. Но на интеллектуальный прорыв эта мысль не тянет.
А я бы назвал "созидание и обмен". Но я не Дарвин и даже не Ливингстон :)
В общем, мне кажется, что мысль "чем сильнее разделение на богатых и бедных в какой-нибудь человеческой популяции, тем она в среднем богаче" - это для успокоения нищих. Типа, в Норвегии разрыв между бедными и богатыми меньше, чем в Индии, но в среднем индус богаче норвежца.
Переспал я с этой мыслью и пришёл к следующему. Я разделяю у себя код на две большие группы: DTO и обработчики. DTO порождаются через фабрики (обработчики). Обработчики зависят от других обработчиков. Некоторые обработчики не имеют своего собственного внутреннего состояния для сохранения результатов работы (обработчики-функции), некоторые имеют (обработчики-объекты). Первые принимают на вход набор параметров, обрабатывают их (в том числе и с использованием обработчиков-зависимостей) и выдают результат. Вторые делают то же самое, но при этом изменяют своё внутреннее состояние (типичный пример - кэширование результатов обработки).
Так вот, когда у нас единый Composition Root у нас все обработчики-функции, зависящие от других обработчиков-функций, будут в приложении иметь жизненный цикл синглтон. Т.е., нам достаточно иметь по одному экземпляру таких обработчиков на всё приложение. Данные (в виде DTO) независимо двигаются по иерархии обработчиков и возвращаются в виде результата, нигде не оставляя следов.
В случае с обработчиками-объектами всё сложнее. Если это объект уровня приложения (например, кэширует в памяти обращения к файловой системе), то он тоже синглтон, а если уровня, например, обработки отдельного HTTP-запроса (потока вычислений), то это уже инстанс (порождается на поток).
Проблема в том, что набор зависимостей в конкретном обработчике (функции или объекте) - это по сути тоже состояние. Если все зависимости - функции, то все могут быть синглтонами. Но как только какая-то из зависимостей является объектом потокового уровня, то вся иерархия обработчиков, строящихся на этой зависимости должна пересоздаваться для каждого потока обработки (HttpContext <= Model <= Operation <= Service <= ...).
JavaScript асинхронный и однопоточный (если не считать worker'ов) и в рамках этого одного потока выполнения программы я не вижу особого смысла пересоздавать иерархию обработчиков на каждый поток обработки (HTTP-)запроса. Разумнее передавать обработчикам (функциям и объектам уровня приложения) контекст в качестве параметра (пусть это и не DTO, а FTO).
В worker'ах же Composition Root отдельный и независимый от Composition Root основного приложения. Таким образом, Composition Root всё равно должен быть один на поток выполнения (thread, worker) и никакой иерархии Composition Root'ов я там не вижу.
Т.е., вот здесь:
> (не)связанный с родительским Composition Root
нужно без скобочек: несвязанный. Я в курсе, что есть реализации, позволяющие строить иерархии Composition Roots, но в своей парадигме разработки ПО я считаю это ненужным.
Лично я использую аналог HttpContext"а для передачи объектов между обработчиками (контроллерами) - положил, взял. Но можно для каждого потока обработки инициировать пустой Composition Root (контейнер объектов или что там аналогичное для AC?), (не)связанный с родительским Composition Root и это уже будет IoC с возможностью создания новых объектов при необходимости. Тем не менее, возможность порождения объектов в таком контексте всё-таки должна быть, чтобы считаться IoC. Без такой возможности HttpContext слабо отличается по своим свойствам от какого-нибудь in-memory storage.
Так что - да, локальный, потоковый Composition Root может иметь место в IoC.
Насчёт того, что это контекст - согласен. Насчёт того, что это IoC - нет. Суть IoC - в передаче управления созданием объектов в программе в отдельное место (Composition Root). Различные части программы не создают сами нужные им зависимости, а получают их тем (инъекция) или иным способом (сервис-локатор) из этого места.
В случае с HttpContext и Thread.CurrentPrincipal я полагаю, что подобные объекты создаются не централизовано, а в местах обработки соответствующих запросов, после чего передаются в качестве параметра или свойства объекта.
Да, это похоже на Ambient Context, но не хватает глобальности, чтобы им быть. Два разных объекта HttpContext не пересекаются по своим свойствам и могут существовать в приложении одновременно (в nodejs могли бы).
По сути, коллега @Deosis прав: Ambietn Context - это такой Сервис Локатор, через который части программы имеют доступ к Composition Root (или к хранилищу результатов его работы). А если предположить, что в этот контекст может писать не только Composition Root, а вообще любая часть программы, то он прав и во второй части - "возвращает значение в зависимости от фазы луны".
Вот хороший рассказ про программистов. Надо лишь сделать поправку, что Грин был романтиком, поэтому ограничился только двумя итерациями и в конце дописал получение выигрыша. В реале же итераций гораздо больше и никакой гарантии выигрыша.
Поэтому вопрос "зачем?" чертовски занимателен.