💡 Способы создания более сложных абстракций из простых в ООП и функциональном программировании сильно пересекаются:
- Наследование - для ООП кажется, что все понятно, но применять наследование нужно не для расширения абстракции, а для сужения, что в ФП достигается типами, а для построения более сложных абстракций из более простых в ФП используют композицию, замыкания, функции высшего порядка (обертки, декораторы).
- Композиция - в ФП композиция везде, а вот в ООП обычно недооценена, реализуется через создание экземпляра одного класса внутри конструктора другого, композиция создает меньше зацепления и зависимости, проще тестировать, когнитивная нагрузка меньше.
- Агрегация - похожа на композицию, но ответственность за инстанцирование не на классе-владельце, т.е. создание экземпляров, вынесено в другие абстракции, а агрегирующий класс получает их уже готовые, чаще всего через конструктор и объединяет.
- Миксины - примешивать к готовым экземплярам ссылки на другие, это хаос, в ФП такого нет, и три предыдущие способа гораздо предпочтительнее, но если нет никакого более красивого выхода, то можно применить, как временное решение, создающее техдолг.
- Ассоциация - иногда под этим термином понимают взаимодействие абстракций, это нормально, но иногда это значит внешнюю агрегацию, в худшем случае - через миксин, в лучшем - через сеттер, так что, это тоже создает зацепление и технический долг.
- Делегирование - это подвид композиции, когда интерфейс внутренней абстракции полностью реализуется наружной, по сути это прямая замена наследования, без использования наследования и проблем, связанных с ним.
⭐️ Good and bad cases for TypeScript union types based on JavaScript V8 optimizations
👍 Good cases for union types:
- Union of strings instead of enum:
type Direction = 'north' | 'south' | 'east' | 'west';
- Union of numeric as status or result code:
type StatusCode = 200 | 201 | 204 | 400 | 500;
- Union with shared properties:
type MailTarget = User | Company; (both with email)
- Union with common method:
type Thenable = Promise | Query; (both with then method)
👎 Bad cases for union types:
- Polymorphic object shapes causing depots:
type Something = User | Socket | string;
- Requiring extensive "if"-logic and type checking:
type Input = string | number | boolean;
- Inconsistent return types:
function getData(id: number): string | string[];
- Mixed primitives and objects:
type Value = number | { value: number };
- Сontradictory members:
type Person = { name: string; } | { name: number[] };
- Union types that include any:
type FlexibleType = number | any;
- Incompatible contracts:
type Handler = (() => string) | ((event: Event, data: any) => void);
🎁 Empty value for primitive types and reference types:
- Use null for empty reference types: Object, Function, Array, etc...
- Use undefined for empty primitive types: string, number, boolean, bigint
- Avoid mixing symbols with other types in unions
Начинающий программист совершенно уверен в том, как все должно быть, но не знает как и шагу ступить в конкретной ситуации. А опытный программист не уверен в том, как все на самом деле, но точно знает, как действовать в конкретной ситуации.
Концепции, парадигмы, паттерны и принципы, построены как мифы и верования. И самое парадоксальное в мифе, что он работает, дает хорошие результаты в ежедневной практике. Не нужно думать о нем, как о чем-то мистическом, выдуманном, отвлеченном. Если программист избавится от мифологического сознания, например перестанет верить в микросервисы, в nodejsоднопоточный, в скалярные типы данных, перестанет писать на примесях и мидлварах, то картина мира рассыпается и он не может ничего написать. Чтобы опять начать что-то создавать, нужно построить другой миф. Только ощущение, что у тебя сложилась картина мира, позволяет перейти от теории к практике. Но любая картина мира состоит из разрозненных фактов, которые переклеены мифом, иначе, бы мы утонули в зазорах между знаниями, как в пустотах между электронами и атомами. Без мифа мир страшен, непредсказуем и неуправляем. Опытный программист просто имеет несколько мифов в голове и может между ними переходить, некоторые могут даже конструировать свои, и такое конструирование может быть только интуитивное, если его начать логически строить, то никто не поверит. В мифе должны быть нестыковки, странности, даже противоречия, вот это заходит в голову идеально. Это инструмент, который позволяет действовать эффективно, скрывать сложность, давать целостную картину мира, но не слишком идеальную, чтобы было место для развития.
🏛️ Проблема сложности, которую решают микросервисы, на самом деле решается проектированием структуры кода на среднем уровне, т.е. люди от функций и классов хотят перескочить сразу к архитектуре, минуя модули, слои, подсистемы. Если код хорошо структурирован на среднем уровне благодаря:
- системам модульности,
- внедрению зависимостей и инверсии управления,
- архитектурным границам и слоям,
- декомпозиции абстракций,
- separation of concerns,
- information expert,
- контрактному программированию,
- управлению, сокрытию и изоляции сложности,
- разделению прикладного и системного кода,
то такое приложение можно в течении нескольких часов собрать в 2, 3, 5, 105 инстансов, заменив взаимодействие между их структурными компонентами на RPC и трансляцию событий. Так, что модули и подсистемы знать не будут, что они запущены не в одном процессе. А если код «рыхлый», то его и микросервисом не изолировать, у такого сервиса будет большой внешний трафик, потому, что зацепление на чужие данные и чужую логику высоки. Так что, «распиливание» это только распиливание бюджета команд и бюджета на инфраструктуру. Обойти вопрос хаоса на среднем уровне при помощи чуда не выйдет. Чтобы построить Application архитектуру, нужна качественная структура, а чтобы перейти к Solution и Enterprise архитектуре, нужна качественная Application архитектура. Попытки перескочить от функции, цикла и массива к Solution архитектуре приводят к появлению монстров типа облачных функций, микролитов, моносервисов и скоро мы увидим Variable as a Service, а потом гору этих абстракций, вываленных на уровень Solution, не сгруппированных и не изолированных в структурные единицы управления сложностью. Чуда не будет, ни кто не решит за нас вопрос перехода от отдельного кирпича к небоскребу, нужны промежуточные структурные единицы.
👉 Patterns 2024 Training: https://github.com/HowProgrammingWorks/Index/blob/master/Courses/Patterns-2024-ru.md
IT образование: для себя определил, что неприемлемо брать деньги с начинающих, с людей, которые еще не знают что им нужно, которые вообще слабо ориентируются в отрасли. Обратите внимание, большинство трешевых ИТ-школ и "инфобизнесменов" специализируются именно на начинающих, им же можно рассказывать про циклы месяц, про переменные разжевывать и про операторы еще месяц, массивы учить и уже через полгода написать какой-то несчастный тудулист или чатбот, в виде простыни кода, и они рады, у них есть результаты, ну вот реально что-то получилось. А попробуйте продать что-то мидлам и синьорам, у них уже сформировалось мировоззрение, есть свое видение, им доказать ценность вашего курса на порядки сложнее. А потом они будут требовательными в процессе обучения, окажется, что нужно быть специалистом высокого уровня, тратить много времени, чтобы удовлетворить их, ответить на вопросы, не засыпаться от реальных продуктовых примеров кода, которые они приносят на занятия для разбора. А начинающих, ну я готов допустить, что можно брать с них деньги за наставничество, за проверку работ, за ревью кода, но не за учебный материал, которого и так море в интернете. Но такое наставничество, это удел джунов, которые еще год назад сами были такими же, и не может быть бизнесом, оно так же полезно этим наставникам для их роста, как и для начинающих. По-хорошому, вообще непонятно, кто кому платить должен. В идеале, сам процесс должен выходить в ноль по затратам сил и пользе с обоих сторон и может идти как взаимозачет. Посмотрите на всех приличных людей, которых вы знаете в ИТ-образовании, они концентрируются на обучении людей, которые уже работают и могут заработать на свое образование. Ни в коем случае не учите начинающих, которые берут кредиты на обучение, которые одалживаются или тратят последние деньги на то, ценность чего еще сами не понимают. Они еще 100 раз передумают, бросят, опять начнут, переметнутся на другой язык, поймут, что душа лежит к какой-то третьей экосистеме, и главное, что первые полгода они не могут определить качество обучения. И вот тут из кустов появляются создатели "образовательного контента", высокохудожественные читатели документации и прочие мошенники. Люди, учите друг-друга, не давайте этой нечисти парить голову начинающим, помогите им проложить роадмап в профессии, материала море, до первой работы можно вполне на нулевом бюджете доучиться, вкладывая только время и силу воли.
🙊 The Law of Demeter (LoD): Don’t talk to strangers
If you see something like this:
- 👉 connection.parent.service.storage.db.saveRecord
- 👉 this.action.scenario.bot.telegram.sendMessage
- 👉 service.find('logger').kind('error').stream.write
You have technical debt! It's time to plan refactoring. Each entity (unit/instance) should have only limited knowledge about others (only "closely" related to the current one). Each entity should only talk to its friends (immediate friends): don’t talk to strangers!
Вut don't do it in a panic and do not mix refactoring with other issues (like new features implementation). Just note that and plan resource allocation.
Почему нужно избегать union-тайпов?
-
Каждый раз, когда юнион куда-то приходит аргументом, нужно делать if, чтобы понимать, как с ним работать, кроме случая, когда все классы/типы, входящие в юнион имплементируют один и тот же интерфейс и нас интересует обращение именно через этот интерфейс, зачем тогда юнион, используйте этот интерфейс вместо него, ну если в юнион не входит undefined, null, unknown и т.д.
-
Юнионы приводят к мегаморфной форме обращения к объектам в V8, и это замедляет код, не сметртельно, но это неприятно и проще всего всего забыть их. Но для чего же они тогда вообще нужны? Для совместимости с JS, если в нем можно передать что-угодно аргуметом, то это нужно меть возможность как-то выразить. Это не значит, что это хорошо и так нужно писать кода, это добавили как возможность, а не как обязанность )
-
Это часто ведет к нарушению SOLID:SRP (принципа единственной отвественности), потому, что как может метод, например, получать сокеты или таймеры на выбор и делать разные вещи в зависимости от этого, это же маразм, нарушает SOLID:LSP (принцип подстановки), иногда нарушает GRASP:InformationExpert, явно повышает Coupling.
Вместо этого нужно всегда использовать маленькие интерфейсы, заточенные под узкую задачу, помним про SOLID:ISP (принцип разделения интерфейсов) и могут быть optional аргументы, для этого не нужно делать union с null.
Here is special repo for course and links about rethinking GRASP, SOLID, GoF patterns, for Frontend (browsers) & Backend (node.js, other runtimes) development with JavaScript and TypeScript.
- Github: https://github.com/tshemsedinov/Patterns-JavaScript
- Initial Twitter article: https://x.com/tshemsedinov/status/1815304825719881808
Article about Promise.withResolvers()
https://x.com/tshemsedinov/status/1814658617821307282 with examples and use cases.
✨🐢 NodeJS 2024 🚀✨ Interview questions and recommendations for app and system backend developers.
- 55 questions for Applied Node.js backend engineer
- 60 interview questions for system Node.js backend engineer Link: https://github.com/tshemsedinov/NodeJS-Interview-Questions
Here's what I suggest learning and practicing.
- Github repo: https://github.com/tshemsedinov/Programming-Knowledge
- Initial Twitter article: https://x.com/tshemsedinov/status/1809633457754034433
Asynchronous programming interview questions and recommendations for JavaScript and TypeScript developers: https://github.com/tshemsedinov/Async-Interview-Questions
💥 Увольте своих HR если они говорят вам, что нужно усилить фильтры формальными требованиями и ужесточить скрининг.
— А кто же будет проводить скрининг?
— Сами кандидаты. Попробуйте:
Опубликуйте больше подробностей о проекте и вакансии, о компании и коллективе. Сделайте опросник для самостоятельного оценивания уровня кандидатов. Набросайте типовых issue, которые будут встречаться в работе. Опишите того, кто вам нужен. Кандидаты сами в состоянии оценить себя, потянут ли они. HR, в большинстве случаев, просто искажает коммуникацию между специалистами, не давая действительно важной информации проникнуть к кандидатам, а действительно стоящим кандидатам проникнуть к команде. Ни какие формальные критерии не могут быть адекватными. Не бойтесь, в конечном счете, обмануть невозможно, какой смысл кандидату врать, если все выяснится очень быстро. Да, будут сбои в такой системе, будут появляться неадекваты, но разве они не проходят формальные фильтры? А в подавляющем большинстве случаев люди способны себя оценить адекватно.
Куда идет Metarhia: В мире Java и C# все решают платформы и большие технологические стеки типа hibernate. Где-то они идут как зависимость, но безальтернативная, а где-то как часть платформы, но основной смысл - в них все продумано, есть решения для всего и программирование переходит на уровень прикладных задач, потому, что все системные уже решены в платформе. В мире node.js такого нет, тут десятки тысяч библиотек, которые не согласованы друг с другом, но их в принципе можно заставить работать вместе. Вот программисты и занимаются 80% времени тем, что их связывают, налаживают, оборачивают и согласовывают. То логгер отпал, то конфиг поскользнулся, то роутинг разлетелся, то валидация отпала. Все это из-за версий, прекращения поддержки библиотек, появления новых, изменения в политике библиотек, их же авторы делают под себя и свои нужды, которые меняются. Собирать заведомо несовпадающие пазлы при помощи ножниц и клея - такая себе затея, в каждый момент времени она решаема, но все рассыпается в руках, поддерживать это очень сложно. Метархия готовит инфраструктуру, в которой вам нужно будет знать минимум и настраивать минимум, даже знаний по ноде нужно будет в десятки раз меньше, чем на чистой ноде писать, не говоря уже про разные зависимости. Все писало стак, чтобы было нужно минимум документации, минимум примеров. Но забудьте про то, что вы сможете решить задачу так, как вы привыкли ее решать, для всего есть готовые решения, их минимум, но все их нужно освоить со старта, и это составляет целостный инструментарий для проектов класса "информационная система", то есть, есть база данных, есть апи и доменная логика. Таких систем самое большое количество, конечно можно делать и игры и колаборативное редактирование и стриминг и чаты и уже даже для публикации контента многое сделано, но есть основное направление платформы и она будет затачиваться под 3-4 типа приложений, их уже сейчас можно делать в 100 раз быстрее, чем на фрейворках типа nest.js, потому, что мы берем идею цельной платформы их Java и C#, но не берем весь кровавый энтерпрайз, а делаем все легковесным, даже DDD и чистая архитектура у нас реализуются максимально компактно и без оверинженеринга. Доку можно прочитать за 30 минут всю https://github.com/metarhia/Docs До конца месяца будет готов пример достаточно сложного приложения и видео-курс с разбором его разработки за один день одним человеком.
Технологический стек Metarhia теперь с документацией и готовится курс быстрой разработки и примеры приложений. https://github.com/metarhia/Docs
- Первый сервер приложений для Node.js с масштабированием потоками
- Поддержка изоляции выполнения между пользователями и запросами
- Автоматическая маршрутизация API-вызовов к API эндпоинтам (нет необходимости добавлять маршруты вручную)
- Тайм-ауты и очередь выполнения запросов с ограничением ожидания
- Схемы для контрактов API, авто-валидация структур данных и моделей
- Поддержка нескольких протоколов: HTTP, HTTPS, WS, WSS
- Разные стили API: RPC через AJAX и через Websocket, REST и веб-хуки
- Обновление кода и SSL сертификатов на лету, без перезагрузки процесса
- Автозагрузчик модулей и graceful shutdown с хуками start и stop
- Авто-генерация пространств имен для кода и зависимостей
- Минимум как прикладного кода, так и кода платформы, нет зависимостей
- Слоеная архитектура: api, бизнес-логика, доступ к данным, системный код
- Песочницы для улучшенной безопасности и изоляции контекстов выполнения
- Предотвращение загрязнения глобального контекста, загрязнения ссылками
- Встроенный балансировщик с перенаправлением на порты воркеров
- Кеширование в памяти для API и статических файлов
- Автозагрузка конфигов с вариантами для разного окружения
- Слой доступа к базе, совместимый с PostgreSQL, с защитой от инъекций
- Персистентные сессии с аутентификацией, группами или анонимные
- Буфферизованный лог (ленивая запись) с ротацией (хранение N дней)
- Интегрированный нативный тест раннер node.js и табличными тестами
- Утилиты для файлов: загрузка, скачивание, partial-content и потоки
- Планировщик для выполнение задач в определенное время
🙈 Синтаксический мусор — это как синтаксический сахар, только мусор, это все, избыточное, что не способствует выразительному и хорошо читаемому синтаксису, все, что не содержит полезной информации и нужно только ради формальности, лишние абстракции, модули, прослойки, классы, декораторы и аннотации, файлы, конфиги, и все, что дублируется, копипастить, все время повторяется по шаблону. Синтаксический мусор, это то, из чего на 80% состоит шаблон приложения из интернета или тот, что вы тянете из проекта в проект.
😜 Есть такой эффект, и я иногда чувствую вину за его распространение: человек только осваивает язык и платформу, но попадает на сложную лекцию о том, как все устроено внутри, о недокументированных функциях, оптимизациях, высоконагруженных и распределенных системах, многопоточности, сборке мусора, метапрограммировании, ивентлупе и т.д. и вот ему дают задачу (или он сам себе ее ставит) например: «сделать апи для получения с сервера списка городов» и тут он вспоминает про асинхронные генераторы, бекпрешер в стримах, про инверсию управления и начинается... нужно все сделать по науке, чтоб миллион пользователей пришло и что будет, если каждый 10 раз в секунду будет выкачивать список, в котором сотни тысяч записей, а может быть давайте не в JSON слать, а придумаем для этого бинарный протокол и сэкономим на кавычках и запятых, будем строки разделать байтом FF, и обязательно предусмотреть отмену получения списка и докачку, если соединение разорвалось с того же места и чтоб кеширование работало, но была возможность принудительно кеш обновлять... А если справочник меняется, то нужно вебсокетами присылать изменения, нужно придумать в каком формате, и таймаут держать в памяти, что если докачка не началась в течении 5 минут, то уже не начнется, а если соединение восстановлено, но мы подключились к другому процессу или потоку, а если сервер перегрузился нужно же... и это бесконечно, так складывание двух чисел можно защищать от влияния космических лучей... я уже не говорю про микротесты и нагрузочные тесты, которые непременно нужно нужно гонять пару часов. Ну... все это... конечно способствует освоению платформы, это интересно и через пару лет может дать полезные результаты и я не хочу отговаривать от экспериментов, но признайтесь себе, сколько будет пользователей, данных и интенсивности через год, два, три, прикиньте грубо, ну на 2 можно умножить и дать себе отчёт в действительных целях своего R&D.
Тут несколько способов добавления таймаутов к асинхронному API (на примере fetch
), конечно fetch
поддерживает AbortController
, но не все знают про AbortSignal.timeout()
и есть API без такой поддержки, так что сравнить есть что. https://github.com/HowProgrammingWorks/AbortController
Пока делал лекцию по WebAssembly для курса по Node.js с использованием Rust, C++, WAT, AssemblyScript, обнаружил, что wasm-pack имеет невменяемый и глючный загрузчик на 3 кила, вот тут загрузчик на 10 строк для ноды https://github.com/HowProgrammingWorks/WebAssembly Более универсальный вариант опубликован уже в npm, лежит тут: https://github.com/tshemsedinov/wasm-import
🚀 Весной я сделал чеклист по портированию проектов на новые версии #NodeJS — прекращение поддержки 14.x и рекомендации по внедрение всех новых возможностей из 16.x, 18.x, 20.x — https://github.com/tshemsedinov/Drop-Nodejs14 ⚡️ Сейчас уже начал готовить такой же документ по прекражению поддержки 16.x, т.е. переходу возможности, поддерживаемые в 18.x и 20.x ⚡️ Замечу, главный упор тут, не появление новых возможностей в новых версиях, а именно прекращение поддержки старых версий, которые мешали использованию новых возможностей.
Наилучший из мне известных способов вставить SQL в код на JavaScript с использованием шаблонных строк, подстановкой переменных в формате ${name}
и именованных параметров через ${'name'}
и передачей параметров в виде объекта-коллекции. Код и тесты (они же примеры вызова) тут: https://github.com/metarhia/metasql/pull/273/files
const query = db.sql`
SELECT * FROM "City"
WHERE "cityId" < ${5} AND "name" <> 'La Haye-en-Touraine'
ORDER BY name LIMIT 3
`;
const cityCodes = await query6.dict('name', 'cityId');
// { Alexandria: '3', Athens: '4', Paris: '1' }
Можете сравнить с пердыдущим примером билдера. Ленивый учит дважды: если кто учил ORM, чтобы не учить SQL, то будет учить и то и другое.
Самый простой пример билдера запросов с поддержкой цепочек вызовов (чеининг) и контракта Thenable: https://github.com/HowProgrammingWorks/Thenable/blob/master/JavaScript/a-query.js
const sql = await new Query('cities')
.where({ country: 10, type: 1 })
.order('population')
.limit(10);
Каким должен быть хороший учебный проект? Для разработчиков информационных систем или data-aware (а это большинство программистов), систем где есть базы данных, пользовательский интерфейс, сервер приложений и взаимодействие по сети, модель предметной области и бизнес-процессы. Сюда прекрасно ложится и веб и энтерпрайз. Так вот, учебный проект лучше начинать не позднее чем через полгода после начала обучения и он должен быть сложный и желательно групповой, делаться несколько лет, постепенно с усложнением, включать не менее половины из: логирование, конфигурация, аутентификация и система прав, отложенные задачи, роутинг запросов, генерация отчетов, миграции, юнит-тесты, интеграционные и системные тесты, автоматическая сборка, телеметрия и сбор статистики, потоковое вещание, очереди, балансировка или оркестрация, интеграция с другими системами, интернационализация, резервное копирование и восстановление, распределенное хранение и обработка данных, коллаборативное редактирование, парсинг, система плагинов, многопоточность или асинхронность, многоуровневое кэширование, сессии, несколько разнотипных СУБД (например postgresql и redis), кодогенерация, обработка метаданных и метапрограммирование, многослойность, изоляция (запросов, пользователей, модулей и т.д.), межпроцессное взаимодействие и удаленный вызов процедур, транзакции и блокировки, рассылка почты и прочих нотификаций.
- Примериы consistent return с использованием
void
как для возврата значений черезreturn
, так и дляcallback
: https://github.com/HowProgrammingWorks/ConsistentReturn/tree/main/JavaScript - Eslint правила и примеры: https://eslint.org/docs/latest/rules/consistent-return
По паттерну Middleware нужно отдельно пояснить, он не только приводит нас к race condition, а точнее и к конфликтам по данным и к конфликтам по control flow, но еще и всячески усиливает зацепление (coupling) кода:
⚠️ Провоцирует практику примесей (mixins), например: res.sessionStarted = true; и потов в куче мест if res.sessionStarted или res.userName = 'idiot';⚠️ Провоцирует протекание абстракций - когда мы залезаем во внутренности req и res и правим их состояние, а также состояние их частей, сокетов, заголовков и т.д. не через внешний интерфейс, т.е. методы, не по контракту, а патчами, обертками, в общем, таким образов, например, ws (библиотека вебсокетов) патчит http сервер и внедряется в его середину для перехвата апгрейда до вебсокетов. Очень хорошо, что так можно сделать в JavaScript, это позволяет быстро решить любую проблему, но такое решение нужно хорошо продумать, покрыть тестами, и вероятность, что оно развалится все же велика. В системном коде это еще ок, а вот в продуктовом нужно максимально снижать протекание, полностью, конечно, его вообще невозможно уничтожить, см. "Закон дырявых абстракций".⚠️ Провоцирует reference pollution и использование шаренного состояния: ссылки на req и res расползаются по разным частям программы, например: serviceName.method(req, res, ...); или на части req и res, пример: serviceName.method(req.socket, ...); или так: outside.emit('request', req); много есть способов: const f1 = method.bind(req); или const f2 = method(req)(res); и еще сотни.⚠️ Провоцирует состояние гонки (race condition): через использование структуры данных за пределами middleware и потом использование такой структуры внутри нескольких middleware сразу или за счет того, что ссылки на req и res попали в другие части программы и оттуда меняется их состояние уже без привязки к next, например по setTimeout кто-то сделал таймаут отправки результатов или на по приходу какого-то события из стримов req и res кто-то хеадеры пишет, а потом другой мидлвар уже не может хеадеры записать. Это только самые частые проблемы, вы разве не сталкивались, когда мидлвары переставляют местами, чтобы найти такую последовательность, чтобы оно таки запустилось, так вот это плохая практика, она только скрывает гонку, но при нагрузке она может вылезти.⚠️ Провоцирует писать толстые контроллеры и смешивать в них разные слои, ну мы видели все, такой эндпоинт, в котором все сразу, и работа с http протоколом, и бизнес-логика и обращение к базе данных через SQL и запись кеша в redis и отправка задачи в очередь и работа с файловой системой, да что угодно, все простыней... нет, конечно, никто так писать не заставляет, просто не все в курсе, что нужно делать слои, и выделять работу с базой в репозиторий и т.д., точнее, знают многие, но мало кто может так делать.⚠️ Повышает зацепление (code coupling) - все части кода становятся более зависимы друг от друга благодаря всему вышеописанному, и пошевелишь одно, а ломается в другом месте.
💡 Самые распространенные: паттерны проектирования для JavaScript и Node.js
- 🧩 EventEmitter (он же Observer), встроен в ноду, а на фронте полифил или EventTarget,
- 🧩 Proxy - встроен в язык, перехват обращений к объекту,
- 🧩 Strategy - у нас это просто Object или Map - коллекция функций, классов, прототипов, замыканий и т.д.,
- 🧩 Facade - упрощенный интерфейс к сложной системе, много где используется, например http2.createSecureServer скрывает от нас как TLS, так и HTTP, потоки, сессии и другие механизмы,
- 🧩 Adapter - обычно функция-обертка, wrapper, примеры: promisify, callbackify, или можно написать полифил fetch, использующий внутри XMLHttpRequest, это будет адаптер, скрывающий сложность, но не фасад, потому, что за фасадом скрывается не один интерфейс, а несколько или целая подсистема,
- 🧩 Factory - Фабрика, это паттерн, который создает экземпляры класса, но в JS фабрика может порождать экземпляры прототипов, замыканий,
- 🧩 ChainOfResponsibility - обычно используется его псевдо-аналог Middleware, который приводит к конкуренции за шареный стейт и так ведет нас к состоянию гонки, про оригинальный ChainOfResponsibility всем стоит почитать, чтобы перестать использовать Middleware,
- 🧩 Decorator - встроеный в язык, при чем в JavaScript и в TypeScript, спецификации отличаются, но суть так же, добавляет поведение без наследования, за счет метаданных,
- 🧩 Singleton - это у нас просто объект, для этого даже класс создавать не нужно, а глобальная уникальность экземпляра может достигаться при помощи экспорта из модуля,
- 🧩 Revealing constructor - открытый конструктор, например, передавая метод write в конструктор Writable через options мы можем получить Writable с переопределенным методом без наследования, так же и с передачей функции в конструктор Promise, мы так привыкли к этому, но в других языках это традиционно делается через наследование.
Есть два типа людей:
- Научился писать круды — устроился формошлепом.
- Научился делать масштабируемые, надежные, высоконагруженные распределенные системы — устроился формошлепом.
Если Вы учите программирование и рассчитываете работать в типичном продукте или аутсорсе, в стартапе или фрилансе, то вот на чем можно сэкономить. Но это не касается тех, кто хочет стать системным программистом и работать в технологической компании. Так вот, чтобы быстрее учиться и что скорее всего никогда не понадобится в реальном продуктовом коде:
- Алгоритмы и задачи с литкода или кодеварс вам не нужны. Но нужен навык простого процедурного и ООПшного кода + GRASP и SOLID.
- Всякие учебные задачи, типа todo листа, калькулятора, крестики-нолики. Нужно делать более комплексные вещи, полноценный проект.
- Бесконечное смотрение видео тоже ни к чему не приведет, нужно получать ревью кода, желательно от наставника или от друзей.
- Задачи на системный дизайн не нужны, Это знания, которые нужны лиду, архитектору и CTO и валидны только при закреплении на практике.
- Микрооптимизация, типа сравнения по производительности object[key], obejct.key и Object.assign. В начале пути одна задача - понятность кода.
- Не нужно заучивать все паттерны (GoF и еще сотню), в зависимости от языка и фреймворка вам понадобится всего 2-3 шаблона проектирования.
- Не старайтесь изучить внутреннее устройство event loop, garbage collection, goroutine scheduler, это спрашивают на собесах, но не нужно в работе.
- Не ведитесь на крутые темы, типа высоконагруженных, распределенных и супер-защищенных приложений, вас к ним еще долго не допустят.
- Не зацикливайтесь на языке, язык гораздо проще тулинга, поднажмите на git, github, линтеры, ide, docker, ci и тестирование, тулы для отладки.
- Ничто так не отвлекает от изучения программирования, как ВУЗ и не внушает ложного чувства уверенности, как ИТ-курсы от инфожуликов.