maxcode.dev
142 subscribers
11 photos
16 links
Download Telegram
⚡️Что такое O(n) на практике

А теперь давайте подумаем, сколько времени будет работать такой цикл, в котором мы идем до этого числа.

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

Примерно 20 лет назад прирост тактовой частоты процессоров остановился на отметке около 4 ГГц. Поэтому последние 20 лет мы можем достаточно точно отвечать на вопрос, сколько работает обычный цикл for примерно на любом устройстве. Для этого достаточно вспомнить, что такое гигагерц.

✓ гига — греческая приставка, означающая 1 млрд (10 ** 9)
✓ герц — единица измерения. 1 Гц = 1 / с (один делить на секунду)

Например, частота обновления экрана 60 Гц означает, что картинка на экране меняутся 60 раз в секунду.

Соответственно, 1 ГГц ≈ 1 млрд операций в секунду. Сейчас даже не так важно каких операций.


console.time("⏱️");
for(let i = 0; i < 1e9; i++);
console.timeEnd("⏱️");


Можете запустить у себя в браузере, например. Цикл с миллиардом шагов работает примерно 1 секунду.

Сколько понадобится времени, чтобы дойти до Number.MAX_SAFE_INTEGER?

✓ MAX_SAFE_INTEGER = 2 ** 53
✓ За секунду мы делаем примерно 2 ** 30 операций
✓ Итого такой цикл займет 2 53 / 2 30 = 2 ** 23 ≈ 8 млн секунд
✓ Переведем в минуты/часы/дни/месяцы: 8000000 / 60 / 60 / 24 / 30 ≈ 3 месяца

То есть вся эта проблема возникнет только если мы будем крутить холостой цикл на протяжении трех месяцев. А если в цикле что-то будет происходить, то еще дольше. То есть да, в какой-то момент цикл зависнет на одном значении i. Но этот момент произойдет через несколько месяцев непрерывной работы.
👍11
🤓 Мораль

Очень полезно понимать ограничения концепций, с которыми мы работаем (IEEE 754, IEEE Standard for Floating-Point Arithmetic). Но еще важнее понимать физические ограничения (тактовая частота процессора), при которых эти проблемы могут возникнуть примерно никогда.

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

Кстати, в ЕГЭ по математике (в базовом, для гуманитариев) есть такая задача, где нужно сопоставить величину и ее значение. Как вы понимаете, если это есть в ЕГЭ, значит, в какой-то момент пришло осознание, что не все одиннадцатиклассники с таким справляются 🙄🔫
👍6🌚1
💣 Где читать теорию по TypeScript?

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

Зачем это нужно? Почему так сделали?

🤔 Почему Promise<T> параметризуется одним типом, который описывает значение value, а не двумя, например, Promise<T, E> с типами для value и reason?
🤔 Зачем в Record<string, number> нужно два типа в дженерике, если ключ у объекта это все равно всегда строка?
🤔 Почему это вообще называется дженерик? Это индийские медицинские препараты, что ли?
🤔 Зачем писать foo<T extends X>(arg: T), если можно просто написать foo(arg: X)?
🤔 Почему для arr: number[] = [] arr[10] имеет тип number, хотя там может быть undefined, если в массиве меньше элементов?

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

Тайп гарды объясняют на ООП-примерах с птичками и собачками. Я не знаю. Что вы чаще делаете на джаваскрипте? Пишете классы или используете методы массивов filter и every, которые активно используют тайп гарды?

Я накидал примерный план того, какая теория нужна для решения задач, и написал вводную часть про дженерики: стандартные дженерики для коллекций (Array, Record, Promise) и дженерик-функции (какую проблему они решают, зачем нужен extends и почему вообще в качестве названия фичи языка используется, блин, прилагательное).

Вот статья про использование джененириков в функциях: https://maxcode.dev/roadmaps/typescript/generic-functions

И, соответственно, список задач, на которых можно отрабатывать теорию https://maxcode.dev/roadmaps/typescript
👏11🔥62
🤦🏻‍♂️ Собеседования 2025

Я сейчас собираю подборки задач с собеседований, например, в Яндекс (https://maxcode.dev/interview/yandex).

Для этого отсматриваю записи реальных собеседований. И вот принес вам одно с вайбами из новогодней серии Черного зеркала (хорошая серия, кстати).

Ссылка на собеседование: https://www.youtube.com/watch?v=RCNQWtd1GOg

Это пример того, как в 2025 проходят собеседования некоторые кандидаты. Вдесятером. Создается звонок на много человек, все слушают вопросы в прямом эфире, а потом кидают в чатик ответы, откуда главный герой их зачитывает.

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

С другой стороны — непрофессиональное собеседование с заходами а-ля «что нового появилось в ES2015» (в 2025 году) и закрытыми вопросами (Работал с гитом? Да. Следующий вопрос).

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

В очередной раз имеем подтверждение тезиса, что в 2025 году пройти собеседование намного проще, чем попасть на него.

Да, когда вы откликаетесь на вакансию, вы видите сотни откликов от других кандидатов. Но если вы попали на собеседование, то вы конкурируете вот с этим. И тут достаточно нормально ответить на вопросы и показать знания.
👍181🔥1
🛠️ Полифил Object.groupBy

Записал видео, где реализую свою версию groupBy. Писать полифилы полезно с точки зрения обучения. Одной задачей сразу можно покрыть несколько тем:

1. Функциональное программирование: метод принимает функцию
2. Прототипное наследование: метод возвращает null prototype object
3. Итераторы: groupBy принимает iterable, а не только массив
4. Современный синтаксис: nullish coalescing assignment
5. Производительность: если написать неаккуратно, то получаем решение за O(n²) вместо O(n)
6. Полезные фичи TypeScript: дженерик с несколькими параметрами, extends, Record и Partial

Видео: https://www.youtube.com/watch?v=lrlG3Nu6cgo
🔥101🙏1
Еще один полезный навык — работа с источниками. Тоже на примере Object.groupBy покажу.

1. Спецификация EcmaScript

Когда мы пользуемся каким-то стандартным методом, мы не особо задумываемся: что еще можно туда передать? что будет, если передать что-то не то? какие операции происходят неявно? какими словами можно писать поведение функции?

2. Реализация v8 (Chrome)

Самая непонятная реализация, которая еще и написана на языке torque (вообще v8 — это двигатель внутреннего сгорания, torque — это вращающий момент). Ее сложно читать, но тут интересно, какие именно оптимизации движка используются. Например, groupBy принимает iterable, но если приходит массив, то мы идем по индексам.

3. Реализация JavaScriptCore (Safari)

Тут уже более понятный код. Есть особенности, связанные с удовлетворением спецификации и переиспользованием кода (используются специальные внутренние функции типа @putByValDirect и @toPropertyKey), но код написан на обычном джаваскрипте и понятнее, чем спецификация или реализация v8.

4. Стандартные типы TypeScript

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

Одно из них — что интерфесы «мержатся», то есть обяединяются. Если у вас в одном месте в коде написано interface A { x: number }, а в другом interface A { y: string }, то тайпскрипт будет проверять, что у A есть оба поля.

Таким же способом в тайпскрипте добавляются типы новых фич языка. Когда выходит очередная версия EcmaScript, разработчики тайпскрипта просто добавляют новый файл с типами, где описывают новые методы). Выглядит это так:

- es5.d.ts — описан конструктор Object и методы прототипа, которые были еще до ES6
- es2015.core.d.ts — новые фичи ES6 (например, getOwnPropertySymbols для только что появившихся символов)
- es2017.object.d.ts — появляются методы, возвращающие итераторы (key, values, entries)
- es2019.object.d.ts — статический метод fromEntries
- es2022.object.d.ts — статический метод hasOwn
- es2024.object.d.ts — статический метод groupBy

А потом тайпскрипт автоматически мержит все эти типы и мы получаем тип Object со всеми методами, которые появлялись в языке в разное время!
🔥10🤯41
(простите за длинный текст, это сразу за предыдущие полгода)

Готовил на выходных серию вводных видео по асинхронности (ниже будет ссылка). И чтобы особо не наврать и в целом быть более точным, пошел сверяться со спецификацией. Решил начать с определения.

A Promise is an object that is used as a placeholder for the eventual results of a deferred (and possibly asynchronous) computation.


Формулировка deferred (and possibly asynchronous) меня особенно заинтриговала.

Во-первых, слово defer на всю спецификацию встречается всего 15 раз. И всё в других контекстах. Хотелось бы конкретики, английский не родной ещё.

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

Я пошел узнавать, и это привело меня к трем мыслям.
2
Мысль первая. Роль личности в истории

Эпоха развития веба после 2012 года хорошо задокументирована. Например, в репозитории tc39/notes можно найти стенограммы всех митингов с обсуждением будущих фич джаваскрипта (которые теперь стали существующими фичами, разумеется). Гитхабу на тот момент всего 4 года, между прочим. С тех пор прошло еще три раза по столько.

Но формулировка из спецификации имеет свои корни не оттуда, а из репозитория domenic/promises-unwrapping, где по git blame можно отследить как определение эволюционировало от Promise objects represent the eventual result of an asynchronous operation к A Promise is an object that is used as a placeholder for the eventual results of a deferred (and possibly asynchronous) computation.

Автор репозитория Domenic Denicola — ключевой человек, который затащил промисы в стандарт в том виде, к которому мы привыкли. С точки зрения популяризации и работы с сообществом я бы сравнил его с вашим любимым Dan Abramov. И, конечно, он затмил ключевого человека, стоявшего за первой реализацией промисов в экосистеме JS — Kris Kowal (библиотека q с самой популярной ненативной реализацией промисов была заархивирована в прошлом году).

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

Но сначала посмотрим, чем занимался Доменик в 2013 году.
🔥3
Для справки попросил ChatGPT указать основные достижения Доменика и сравнить его с другими прелдставителями индустрии
Мысль вторая. Исторический контекст

После изучения репозитория я, конечно, пошел на youtube. Там, естественно, много видео про промисы за 2013 год, где Доменик ездит по коференциям и рассказывает, что промисы это круто.

Мне понравилось выстыпление Redemption from Callback Hell, где он на пару с еще молодым, но уже отсвечивающим лысиной Майклом Джексоном объясняет, какую проблему решают промисы.

Это тот самый Майкл Джексон, который напишет react-router (и будет ломать его с каждой новой мажорной версией), запустит курсики по реакту, а потом разработает Remix и продаст его Shopify (без шуток очень круто продать компании не продукт, а фреймворк).

Я уже в канале выше публиковал архивный доклад про реакт из 14 года. Очень интересно послушать, какие проблемы платформы тогда решал React с точки зрения опытного разработчика (последние годы реакт решает проблемы, которые создал сам).

С промисами похожая штука, но есть нюанс. А именно — кто целевая аудитория доклада.

Мне сейчас трудно представить человека с небольшим опытом, который бы проникся предпосылками доклада. Их две.

⓵ Авторы критикуют CPS (continuation-passing style). Это то, что вы называете колбэками. Я разделяю критику, но тут важно понимать такую штуку.

Промисы не решают проблему колбэков, они фокусируются на работе с асинхронными значениями (промис — обертка над значением по определению). На они применимы к 95% задач, связанных с асинхронностью на клиенте.

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

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

② Много времени в рассказе посвящено объяснению, как работают resolve и reject, которые по сути являются аналогами return и throw.

Подвох в том, что к моменту изучения промисов у человека очень мало, если вообще есть, опыта работы со throw. Аналогия не сработает. Объясню почему.

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

Во-вторых, иллюстрируя аналогии, авторы показывают примеры синхронного кода. На джавскрипте никто так писать не будет. Зато так пишут, например, на C#. В 2013 году для фронтендеров джаваскрипт, как правило, не был первым языком программирования. Поэтому пример был релевантным.

Вообще это реально взрыв мозга. Я осознал, что ломаю мозг людям, когда мы нормально решаем задачки, а потом я внезапно говорю, что вот есть промис, у него есть метод then. В нем два колбэка. Зачем два? К чему это разветвление? Пускай код теперь не синхронный, но почему он должен быть еще и не линейным? Аналогия return и throw заложена автором идеи, нужно ее использовать (но непонятно как).
👍4
Мысль третья. Про деньги, конечно

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

Он вышел на пенсию (и переехал в Японию, как Dan Abramov или PewDiePie)

https://domenic.me/retirement/

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

В комментариях твиттер первыми подсовываем мне реплаи от Jake Archibald, Addy Osmani и Kent C. Dodds, про важность каждого из которых можно написать по гигантской статье (может быть, напишу).

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

It’s hard to tell how much of this is due to the evolution of Google and the macroeconomic environment—is platform-building a ZIRP phenomenon?


Проще говоря, мы столько классного сделали, потому что мы такие талантливые или потому что деньги дешевые были?

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

ZIRP — zero interest-rate policy. Это жизнь с исторически сверхнизкой ключевой ставкой. Доменик пишет, разумеется, про США. Но и в России тоже такое было, пусть и не так долго.

Вы сейчас жалуетесь на дорогие квартиры и высокие цены. С 17 по 20 год вы на это не жаловались. Как ни странно, не потому, что трава была зеленее и вы были моложе, а потому что Центробанк всеми силами стремился к таргету 4% инфляции и на 4 года его достиг.

Набиуллина со своим решением проблемы Impossible trinity и выбором в пользу таргетирования инфляции пришла летом 2013 года. В 2014 году вы сами знаете, что началось, и движение к таргету отложилось на 3 года. Ситуация повторилась в 2022 году. В этот раз таргет уже уехал вправо на 5 лет.

Но вернемся к Доменику. Одиннадцать с половиной лет в гугле в команде хрома — это не только масштабное влияние на экосистему веба, но и опционы до миллиона долларов в год на L7 грейде.

За 11 лет это может быть восемь знаков (10+ млн долларов). Fuck You Money — когда ты можешь сказать нет на любое предложение. FIRE — когда ты можешь выйти на пенсию в 35.

Я не работаю в Америке и мои цели менее амбициозные. Тем не менее, учитывая, что мы примерно одного возраста, не могу не думать об этом. Доменик пишет:

But I’ve decided to embrace a different approach: I’m retired! I no longer need to work for money, and I’m going to take on the responsibility of figuring out what that looks like.


FIRE по-американски: ты 10 лет работаешь в FAANG, твоя зарплата за счет опционов растет to the moon. В какой-то момент ты увольняешься, потому что зарплата хоть и гигантская, но она теряет смысл, потому что сбережений уже хватит на всю жизнь.

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

Мне грустно, что последние 5 лет я не могу инвестировать в S&P500, а сижу в боковике MCFTR. Даже если очень много работаешь, очень тяжело стать мультимиллионером, когда американсий рынок за 6 лет растет в 2 раза, а российский буквально на 0%.

Так что с одной стороны спасибо Доменику за промисы. И поздравляем с долгожданным выходом на пенсию! Но не от всего сердца 🥲
4👍3🔥1
Теория по промисам

Как вы знаете, у меня собран самый лучший список задач по промисам. Но давно нужна была теория (а mdn никто читать не хочет). У меня из материалов была только обзорная шпаргалка по промисам.

Люди годами пользуются промисами, но не могут ответить на вопросы:

⓵ Почему статусы fulfilled и rejected, а в конструкторе — resolve и reject
⓶ Как catch понимает, что произошло в начале цепочки, если он в конце
⓷ Что будет, если catch и finally оказываются в середине цепочки
⓸ Зачем нам понадобились микротаски, если уже были обычные таски
⓹ Как реализовать класс Promise самому 1 в 1 как в спецификации

Я знаю, что вам не нравятся скучные часовые видео. Поэтому я сделал 4 скучных 15-минутных видео.

▶️ Бесплатный плейлист на Youtube

В первом сезоне — приватные поля класса промисов, всё про состояния промисов, детальное объяснение работы методов then и catch.

Дальнейшие планы — реализация полифила Promise, как правильно понимать метод finally, отличия промисов от колбэков и историческая перспектива, особенности работы с async-await и демо с event loop с неочевидными примерами.

Кажется, из этого в ближайшие месяцы реалистично можно рассчитывать на лайв-кодинг класса Promise.
🔥13