Путь айтишника
130 subscribers
865 photos
1 file
482 links
Путь становления айтишника глазами практикующего специалиста. Личный опыт, полезные инструменты, разбор технологий, план развития и взгляд на индустрию изнутри.
Download Telegram
Иногда самые интересные инженерные решения появляются не на этапе проектирования, а уже когда система почти готова — именно так у меня и получилось с микросервисом по обработке чеков.
Пока разбирался с архитектурой, наткнулся на интересную идею. После основной нейронки рекомендуют запускать ещё одну, которая проверяет результат её работы. Подход называется LLM-as-a-Judge.
Логика простая. Промпт — это не программа, а скорее набор рекомендаций. Нейронка может интерпретировать их по-своему: придумать новый код ошибки, добавить несуществующую деталь устройства или сделать ещё что-нибудь творческое. Как поётся в песне: «Фантазёр, ты меня называла».
Первые версии промпта для проверяющей модели оказались слишком суровыми. Почти на каждый ответ она выдавала что-то вроде: «Представленные рекомендации недостаточно обоснованы и не опираются на фактические данные и подтверждённые взаимосвязи». Да здравствует математическая логика. В итоге пришлось смягчить требования: теперь она отбрасывает только откровенные фантазии, а не пытается переписать весь результат. Фронтендер уже начал писать код под мой сервис. Посмотрим, как всё это покажет себя в реальной работе.
Параллельно решил попробовать Codex для написания скриптов на PowerShell. Остался приятно удивлён. Для системного администратора это очень сильный язык: работа с базами данных, почтой, сетью, журналированием, многопоточностью — всё есть из коробки. Отдельный плюс — PowerShell уже встроен в Windows вместе с IDE. Если вы администрируете Windows-серверы или просто хотите автоматизировать домашний компьютер, язык точно стоит изучить. Осваивается он достаточно быстро, а пользы приносит много.
Ещё посмотрел курс «Летняя сессия Яндекса» по AI. В основном рассказывали про экосистему Яндекса и её продукты. Если сравнивать с ChatGPT или DeepSeek, то какого-то принципиального технологического разрыва я не увидел. Зато у Яндекса хорошо выстроена именно экосистема: можно собрать полноценный проект, практически не выходя за пределы их сервисов. До Amazon Web Services по масштабу ещё далеко, но развивается очень активно. Поэтому курс могу смело рекомендовать: https://www.youtube.com/@YandexCloudPlatform/streams
Всё больше убеждаюсь, что основная работа при внедрении ИИ — это не выбор модели. Намного важнее построить вокруг неё правильный процесс: проверить входные данные, проконтролировать результат и не дать нейронке слишком увлечься собственной фантазией. Тогда она действительно начинает экономить время, а не создавать новую работу.
👍3❤2
Иногда кажется, что самая сложная часть корпоративной системы — написать код. А потом начинаешь выяснять, кто какую полку в Wiki должен видеть, и внезапно оказываешься где-то между Keycloak, OLAP-кубом и многомерной вселенной, в которой возможно вообще всё.
У нас есть большая корпоративная система Service Desk (SD), и захотели к ней прикрутить документацию в виде Wiki. Выбор пал на BookStack https://www.bookstackapp.com/. BookStack мне понравился своей концепцией книжного шкафа: есть шкаф, на нём полки, в полках книги, у книг главы и страницы. Всё довольно наглядно и хорошо ложится на корпоративную документацию.
Но дальше захотелось, чтобы пользователь один раз авторизовался в Service Desk и с этой же учёткой автоматически входил в BookStack. Это называется SSO — Single Sign-On, или «единый вход». Между SD и BookStack в качестве прослойки используется Keycloak https://www.keycloak.org/, который и должен реализовать всю эту историю через протокол OpenID Connect (OIDC).
Keycloak оказался системой весьма замысловатой и, мягко говоря, не самой дружелюбной. Например, пользователи и клиенты — совершенно разные сущности, как группы и роли. Самое интересное, что сущности с похожими названиями есть ещё и в BookStack, только означают они уже немного другое. В какой-то момент начинаешь подозревать, что главная задача Keycloak — не авторизовать пользователя, а проверить твою веру в человечество.
Но техническая часть оказалась не самой сложной. В голове у бизнеса пока есть только крупные мазки и общее желание: «нужно разграничить доступ». А как только начинаешь выяснять, какой пользователь, с какой ролью, из какого подразделения должен видеть конкретную полку, книгу или главу, возникает дикая таксономия во главе с OLAP-кубом. Получается многомерная вселенная прав доступа, в которой возможно практически всё.
Был даже созвон с нашим корпоративным архитектором. Посмотрели на эту неведомую зверушку, обсудили варианты, и архитектор взял паузу подумать, как всё это правильно реализовать. Поэтому SSO пока не доведён до конца. В очередной раз выяснилось, что настроить протокол проще, чем формализовать человеческое «ну вот эти должны видеть это, а эти вроде бы не должны».
Кроме того, было обучение по Cursor — ИИ постепенно внедряется в нашей фирме. Радикально нового я узнал мало: уже успел пройти путь от простых промптов до построения мультиагентных систем. Но несколько интересных приёмов всё-таки вынес, а один практически сразу пригодился в работе.
Сейчас пишу небольшой скрипт, который отправляет записи телефонных разговоров на расшифровку в Яндекс. Под Windows программа прекрасно работает, а под FreeBSD падала с ошибкой. Я дал Codex доступ на сервер только к определённой папке и показал ошибку. Codex сам подключился к FreeBSD, разобрался в окружении и нашёл причину: у утилиты flock немного отличаются ключи в Linux и FreeBSD.
Раньше в такой ситуации я бы «обмазался» логами, запустил удалённую отладку и потратил несколько часов, причём не факт, что сразу нашёл бы причину. Здесь Codex справился примерно за полчаса. Вот такое применение ИИ мне нравится гораздо больше разговоров про то, как завтра он заменит всех программистов: дал конкретную проблему, ограничил область работы — и получил несколько сэкономленных часов.
Похоже, с ИИ всё постепенно приходит к довольно приземлённой модели. Он не отменяет сложность систем и точно не способен придумать за бизнес, кому какую полку показывать. Зато там, где раньше приходилось часами копаться в логах и различиях между Linux и FreeBSD, уже вполне способен взять часть работы на себя. И вот это действительно полезно.
👍10
За неделю AI успел побыть у меня программистом и уборщиком файловой помойки. И неожиданно именно последнее оказалось одним из самых полезных применений. Иногда не нужно просить AI сделать стартап на миллион — достаточно заставить его разобрать папку, к которой сам боишься подходить годами.
Завершил мини-проект по загрузке записей звонков в Yandex SpeechSense и заодно познакомился с gRPC. До этого я в основном работал с REST, который древний как мамонт, но всё ещё живее всех живых. А Яндекс решил сделать API SpeechSense через gRPC — технологии уже десять лет, но для меня это оказался новый и довольно интересный мир.
Если сильно упрощать, в REST клиент и сервер обычно обмениваются JSON: формат понятный человеку, но довольно многословный — имена полей повторяются в каждом объекте, а данные передаются текстом. В gRPC контракты сервисов заранее описываются в .proto-файлах, данные сериализуются через Protocol Buffers (Protobuf) и передаются поверх HTTP/2 в бинарном формате. В итоге меньше данных гоняется по сети, есть строгая типизация, а клиент и сервер заранее знают, чем именно должны обмениваться.
Мне gRPC понравился именно этой строгостью: контракты определены заранее, меньше пространства для самодеятельности, данные передаются компактнее. Но хоронить REST я бы точно не стал — он будет жить ещё очень и очень долго. Просто теперь у меня появился ещё один инструмент и понимание, зачем он вообще нужен. Вот хорошее видео «gRPC для новичков» https://www.youtube.com/watch?v=DGBLwNN-YM4
Ладно, программирование — это всё хорошо. А может AI решить какую-нибудь совершенно рядовую задачу прямо на моём компьютере? У меня есть папка Docum, которая за годы превратилась в натуральную файлопомойку. Разобрать её руками можно, но страшно даже представить, сколько времени уйдёт. И тут я вспомнил про подход PARA.
Смысл PARA простой: вся информация раскладывается всего по четырём папкам. Projects (Проекты) — то, над чем работаю сейчас и у чего есть конкретный результат и финишная черта. Areas (Сферы) — постоянные зоны ответственности без даты окончания: работа, финансы, обучение, Telegram-канал. Если это нужно регулярно поддерживать в актуальном состоянии — ему сюда.
Resources (Ресурсы) — статьи, документация, инструкции, книги и прочие материалы, с которыми сейчас ничего делать не нужно, но они могут пригодиться потом. Archives (Архивы) — завершённое, устаревшее и больше не требующее внимания. Получается простой фильтр: есть конкретный результат — Projects, постоянная ответственность — Areas, может пригодиться потом — Resources, закончилось или потеряло актуальность — Archives.
Я указал Codex на папку Docum и сказал упорядочить её по PARA. И вот здесь магия оказалась действительно полезной: он разобрал файловые завалы по содержимому и разложил всё по четырём категориям. Теперь на верхнем уровне ровно четыре папки, текущие проекты находятся быстро, а всё давно ненужное спокойно лежит в архиве и не мозолит глаза.
На этом я не остановился и таким же образом упорядочил по PARA заметки в Obsidian, заодно добавив иконки. Забавно, что одним из самых полезных применений AI оказался не написанный им код, а банальная уборка файловой помойки. Подробней о PARA можно прочитать здесь «Четыре папки против хаоса: PARA — это просто» https://singularity-app.ru/blog/metod-para/
Похоже, самые полезные сценарии AI начинаются не там, где он должен сделать за тебя что-то великое. А там, где можно наконец отдать ему работу, которую ты сам откладывал годами.
👍6
Есть ощущение, что ИИ действительно ускоряет работу — ровно до того момента, пока сам понимаешь, что он делает. Стоит потерять контроль, и вместо экономии времени можно получить очень убедительное решение совсем не той задачи.

Всю неделю внедрял Cursor в свою работу. Думал, что после WebStorm придётся долго привыкать, но переход оказался довольно безболезненным. Хотя разница между ними хорошо показывает два разных подхода: IDE, где почти всё есть из коробки, и редактор, который сначала представляет собой скелет, а потом обрастает нужными плагинами.

Cursor — это ИИ-редактор на базе Visual Studio Code, поэтому первое время непривычно было работать с Git, а сильнее всего мешало отсутствие привычной панели для npm-скриптов. В WebStorm нужный функционал обычно уже есть — остаётся его настроить. В VS Code логика другая: доставляешь плагины и собираешь редактор под себя. Поставил всё необходимое, например Better Scripts для npm-скриптов, и проблема исчезла.

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

Но волшебная палочка быстро сломалась об Yandex SpeechSense. Там достаточно сложная система управления правами и доступами, и даже с MCP автоматически настроить необходимые права не получилось. Пришлось читать документацию и выполнять настройку вручную — по старинке. Хорошее напоминание, что ИИ особенно полезен, когда сам понимаешь внутренний процесс и можешь быстро заметить, что он пошёл куда-то совсем не туда.

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

Параллельно разбирался с Keycloak (https://www.keycloak.org/) — бесплатной open-source системой управления доступом и идентификацией пользователей (IAM). У нас в компании много разных систем, и очень хочется, чтобы пользователь входил во все под одной учёткой. Это и есть единый вход — SSO. В Keycloak кроме SSO есть многофакторная авторизация, вход через социальные сети, защита от перебора паролей и интеграцией через Active Directory.

Основная проблема — Keycloak сам по себе достаточно сложный. Там уйма похожих терминов и сущностей, которые поначалу легко перепутать: пользователи, клиенты, роли, группы и всё остальное. Если приложение одно и авторизация простая, тащить ради него Keycloak, скорее всего, не стоит. А вот когда приложений много и требования к авторизации становятся замороченными, уже есть смысл задуматься о централизованном решении.

Я помню, как мы отделом писали собственную авторизацию: сначала на ролях, потом на атрибутах. Даже относительно простой вариант занял где-то два-три спринта. Поэтому управление доступом только со стороны выглядит задачей уровня «ну тут логин, пароль и несколько ролей». Чем глубже начинаешь разбираться, тем быстрее выясняется, что простая авторизация почему-то заканчивается отдельной системой.

В итоге за неделю получилось два похожих вывода. Cursor хорошо ускоряет работу, пока понимаешь, что именно он делает, а Keycloak хорошо решает авторизацию, когда действительно понимаешь, зачем тебе вся эта сложность. Инструменты становятся полезными не тогда, когда делают всё сами, а когда ты понимаешь, где их использовать и где вовремя остановиться.
👍4
Самые интересные задачи в IT обычно начинаются со слов «да тут всё просто». На этой неделе я дважды наступил на эти грабли: сначала с локальными LLM, потом с логированием скачиваний в NextCloud.
Решил проверить, насколько хорошо LLM работает локально. А вдруг наступит чебурнет? Железо у меня дома не самое выдающееся: RTX 3050 8 ГБ + i5-12600 + 48 ГБ RAM. Установил LM Studio и довольно быстро выяснил, что 8 ГБ видеопамяти для нейронок — это маловато. Поэтому выбрал две модели, которые туда влезают: qwen2.5-coder-7b-instruct для программирования и универсальную qwen3.5-9b.
Сама по себе LLM умеет немного: получила текст, вернула текст. Чтобы началась «магия», нужна обвязка — LLM harness, который позволяет модели работать с файлами, запускать команды и использовать другие инструменты. В качестве такой обвязки взял OpenCode https://opencode.ai/, подключил локальные модели и попросил написать простейшую программу — генератор паролей.
qwen2.5-coder долго думала и родила чудо-юдо в одном файле. Дизайн — без слёз не посмотришь, зато относительно работает. qwen3.5-9b тоже долго думала и выдала результат заметно красивее. Всё хорошо, только не работает. Для сравнения дал ту же задачу Cursor — он быстро сделал красивый и рабочий вариант.
Чуда не произошло. Если хочется качественно и быстро, придётся либо радикально обновлять железо, желательно парой мощных видеокарт, либо работать по подписке. Я пока за подписку: к хорошему быстро привыкаешь.
На работе тем временем появилась задача, которая звучала ещё проще: в NextCloud нужно увидеть, какой пользователь скачал какой файл. По собственному опыту знаю — самые сложные задачи начинаются именно с одной такой фразы.
Сначала попросил Cursor написать расширенный лог для Nginx. Он написал, только в лог начало попадать вообще всё. Результат мне сильно не понравился, поэтому пришлось самому вспоминать, как устроено логирование в Nginx. Посмотрел два видео:
"Логирование в Angie" https://www.youtube.com/watch?v=UDpeDHQmcic
"Директива location в Angie" https://www.youtube.com/watch?v=gZKc5Qe2NAY
После этого переформулировал задачу для Cursor: реализовать условное логирование в JSON только скачанных файлов по HTTPS и WebDAV, с размером больше нуля, отдельно сохранять пользователя, имя и путь к файлу, а результат писать в downloads.log. Уже лучше.
На следующий день выяснилось, что логи должны храниться централизованно в Elastic. Создал индекс, отправил туда данные и настроил Dashboard в Kibana. Казалось бы, задача решена. Но поиск по имени файла оказался самой незначительной проблемой.
Пользователи NextCloud у нас авторизуются через Active Directory под доменными учётками. Удобно для людей, не очень удобно для логов: вместо Иванов И.И. там лежит UUID вроде 0e9584bb-c1cd-4d44-ab94-a3e7a7b4c998. А в Dashboard хотелось видеть всё-таки людей, а не наборы символов.
Оказалось, в NextCloud есть таблица сопоставления UUID и пользователей. Раз в сутки я начал отправлять её в отдельный индекс Elastic, а затем пропускать логи через pipeline, который сопоставляет UUID с именем пользователя и записывает его в итоговое поле. После этого в Kibana добавил фильтры по пользователю и имени файла.
И наконец стало видно, кто, какие файлы и сколько раз скачивал — с графиками и диаграммами. Задача «просто настроить логирование» закончилась Nginx, NextCloud, Active Directory, Elastic, pipeline и Kibana.
Наверное, в этом и есть основная разница между демо и реальной инженерной работой. В демо всё решается одним промптом или одной настройкой. В реальности основная работа начинается сразу после слов «ну тут всё просто».
👍3
Иногда полезно залезть в область, в которой вообще не собираешься становиться специалистом. Просто чтобы лучше понимать, что происходит рядом с твоей основной работой, и не воспринимать чужую профессию как магию.
Неожиданно для себя решил немного заняться дизайном. У меня есть годовые цели, и мы с ChatGPT посмотрели на календарь: скоро новый год, а к некоторым из них я даже не приступал. Одна из таких целей — дизайн.
Формально дизайном я занимаюсь давно: открытки к 8 Марта, простые сайты и прочее высокое искусство. UX/UI-дизайнером становиться не собираюсь — это отдельная и довольно сложная профессия. Но фронтендеру базовое понимание дизайна полезно: поправить компоновку, цвет, шрифт или готовый макет, не дёргая дизайнера из-за каждой мелочи.
Поэтому нашёл курс по Figma и повторил первый макет. Сама Figma оказалась не такой страшной. Когда-то я работал в CorelDraw и Adobe Illustrator, поэтому логика знакома: группировка, выравнивание, цвет, обводка, размеры. Главная проблема таких программ для меня всегда одна — надо учить горячие клавиши, иначе однотипные действия начинают съедать уйму времени.
Параллельно читаю «Типы в языках программирования» Бенджамина Пирса и дошёл до ООП.
Обычно ООП объясняют через животных: есть класс «Животное», от него наследуются кошка и собака, одна мяукает, другая лает. Все всё поняли, аплодисменты, расходимся.
Но внутри никаких кошечек и собачек, конечно, нет. Пирс показывает объектную модель без привычной магии классов. Оказывается, основные механизмы ООП можно собрать из более простых конструкций языка:
объект → запись функций;
метод → функция;
переменные экземпляра → ссылки (ref);
инкапсуляция → замыкание;
наследование → расширение записи методов;
self → рекурсивная передача объекта самому себе.
То есть ООП не требует специальных «объектно-ориентированных» конструкций языка. Классы — во многом удобная оболочка над функциями, записями, ссылками и рекурсией.
После такого разбора на привычное ООП начинаешь смотреть немного иначе.
И ещё немного компьютерной археологии.
Все наверняка слышали про WebAssembly (Wasm) — низкоуровневый формат, позволяющий выполнять в браузере код, скомпилированный, например, из C, C++ или Rust. Благодаря Wasm и другим технологиям браузер давно умеет гораздо больше, чем просто показывать HTML.
Например, прямо в браузере можно запускать старые программы и операционные системы. Нашёл Windows 95:
https://www.pcjs.org/software/pcx86/sys/windows/win95/4.00.950/
Для кого-то это музейный экспонат, а я на этой Windows учился в ВУЗе. Довольно странное ощущение: система, которая когда-то была обычным рабочим инструментом, теперь запускается в браузере как часть компьютерной истории.
Дизайн, устройство ООП и Wasm вроде бы находятся довольно далеко друг от друга. Но смысл для меня один: иногда полезно выходить за пределы своей специализации и разбираться, как устроены соседние области.
Не для того, чтобы срочно становиться дизайнером, теоретиком языков или разработчиком компиляторов. А хотя бы для того, чтобы следующий незнакомый инструмент уже не выглядел магией.
👍3
Начал проходить курс «Computer Science во Frontend». Первая тема — массивы. Казалось бы, что там вообще может быть сложного? Но довольно быстро выясняется, что привычный Array в JavaScript — это достаточно хитрая абстракция, а не просто кусок непрерывной памяти, как массив в C. Обычно этого совершенно достаточно, пока не приходится лезть в бинарные данные.
И вот тут появляются типизированные массивы — TypedArray. Это структуры JavaScript для работы с числами одного типа в непрерывной области памяти. Сама память хранится в ArrayBuffer, а Uint8Array, Int32Array, Float64Array и другие типы позволяют смотреть на неё по-разному. В отличие от обычного Array, размер такого массива фиксирован, а один и тот же ArrayBuffer можно одновременно интерпретировать разными способами.
Но если требуется прочитать бинарный файл какого-нибудь неведомого формата, веселье только начинается. Одни данные могут занимать один байт, другие два или четыре, а многобайтовые числа ещё и записываются в разном порядке: little-endian или big-endian. Для такой работы есть DataView, который позволяет читать из ArrayBuffer значения нужного типа, с нужного смещения и с нужным порядком байтов.
С текстом отдельная история. UTF-8 и UTF-16 используют переменное количество байтов для представления символов, поэтому просто читать файл по одному байту и считать, что каждый байт равен одному символу, не получится. Для декодирования текста есть отдельные инструменты вроде TextDecoder, а TypedArray и DataView остаются уровнем ниже — они работают непосредственно с байтами и числами.
Получается довольно занятная схема: сначала считываем бинарный файл в область памяти ArrayBuffer, а потом накладываем на неё нужные представления. Причём при работе с многобайтовыми типами приходится учитывать смещения и выравнивание данных. В какой-то момент понимаешь, что фраза «просто прочитать несколько байтов из файла» звучит примерно так же оптимистично, как «просто подключить ещё один сервис».
Если сократить всю тему до одной схемы, получится примерно так:
const buffer = new ArrayBuffer(16);

const uint8 = new Uint8Array(buffer);
const uint32 = new Uint32Array(buffer);
const dataView = new DataView(buffer);

Все три объекта смотрят на одни и те же 16 байт памяти, но каждый по-своему. Uint8Array видит последовательность байтов, Uint32Array — 32-битные беззнаковые числа, а DataView позволяет самому выбирать тип, смещение и порядок байтов. Память одна, способов её интерпретировать несколько — и вот тут тема массивов внезапно становится заметно интереснее.
Конечно, совершенно не обязательно каждый бинарный формат разбирать вручную. Есть готовые библиотеки вроде https://github.com/keichi/binary-parser, которые позволяют описать структуру данных и получить готовый объект. Протокол Protobuf https://protobuf.dev/, который используется в том числе вместе с gRPC, тоже работает с компактным бинарным представлением данных. Под капотом всё снова сводится к байтам, длинам полей и правилам их интерпретации.
В общем, я начал тему с мыслью: «Что может быть сложного в массивах?» Оказалось, довольно много, если опуститься на уровень памяти и бинарных данных. Обычный Array позволяет обо всём этом почти никогда не думать, и в этом как раз его удобство. Но стоит заглянуть уровнем ниже — и за привычным [] внезапно обнаруживается целый маленький мир.
👍3
Чем дальше прохожу «Computer Science во Frontend», тем чаще оказывается, что привычные вещи устроены совсем не так просто, как кажутся сверху. Массив уже оказался не совсем массивом, теперь добрался до RxJS и устройства памяти процесса. В обоих случаях примерно одна история: пока работаешь через удобную абстракцию — всё понятно, а стоит заглянуть внутрь, начинается самое интересное.
Начал проходить миникурс по RxJS https://rxjs-course-avy.web.app/. В React основной подход довольно понятный: изменилось состояние → компонент перерендерился, а асинхронность обычно закрывается через Promise, async/await, useEffect, TanStack Query или внешний state-менеджер. В Angular потоки встроены в экосистему гораздо глубже, поэтому здесь приходится немного перестраивать привычный способ мышления.
Многие данные во frontend — это вообще не одно значение, а поток значений во времени: ввод пользователя, HTTP-запросы, события роутера, WebSocket, таймеры. Observable позволяет такие потоки объединять, фильтровать и преобразовывать через map, switchMap, combineLatest, debounceTime и другие операторы. Современный Angular при этом разделяет задачи: Signals удобны для состояния UI, а RxJS остаётся сильным инструментом для сложной асинхронности.
По собственному опыту особенно хорошо смысл потоков становится понятен, когда появляется два-три зависимых асинхронных запроса, а предыдущие ещё надо уметь отменять. Можно собрать это на Promise и async/await, но довольно быстро начинается ручное управление состояниями, отменами и гонками. С потоками такая логика иногда получается заметно чище, хотя первое время на RxJS смотришь примерно как на отдельный язык программирования внутри TypeScript.
Параллельно в курсе «Computer Science во Frontend» дошёл до сегментов памяти программы. Со школы помню классическую картинку: есть код программы, данные, куча растёт вверх, стек растёт вниз — всё просто и понятно. И это даже не то чтобы неправильно. Просто реальная память процесса, как обычно, решила не ограничиваться школьной схемой и добавить немного подробностей.
Память процесса представляет собой набор областей виртуального адресного пространства. Есть сегмент кода text с машинными инструкциями, .data для инициализированных глобальных и статических переменных, .bss для неинициализированных, а дальше располагается heap для динамической памяти. Отдельно существует область mmap, куда могут отображаться файлы, анонимная память и динамические библиотеки вроде libc.so.
Ещё выше в классической схеме находится stack: там живут данные вызовов функций, локальные значения, адреса возврата и служебная информация. Стек обычно растёт в сторону уменьшения адресов, а расположение самих областей может меняться при каждом запуске из-за ASLR (Address Space Layout Randomization — рандомизации размещения адресного пространства). Поэтому память процесса — это не один большой непрерывный кусок RAM, а организованное виртуальное пространство, которое операционная система отображает на реальную память по мере необходимости.
Ещё со школы помню страшилку: «Не кладите, дети, много данных в стек — переполнится и всё сломается». И здесь тоже всё немного интереснее: в стеке лежат далеко не только восьмибайтовые указатели, а целые стековые кадры функций, локальные значения и служебные данные. Поэтому условные 8 МБ стека выглядят огромными ровно до момента, пока не появляется глубокая рекурсия или большой локальный массив.
В общем, очередная тема из серии «вроде всё знал, пока не начал разбираться». С RxJS оказывается, что асинхронность удобнее воспринимать не как набор отдельных операций, а как поток событий. С памятью — что привычные стек и куча являются только частью гораздо более сложной картины. Похоже, весь Computer Science примерно так и устроен: сначала тебе дают простую модель, а потом постепенно объясняют, сколько всего она от тебя скрывала.
👍4
Продолжаю смотреть курс «Computer Science во Frontend». Дошёл до многопоточности. Ну какая многопоточность в JavaScript? Это не про нас. JavaScript однопоточный. Многопоточность — это где-то там, в больших и мощных Java и Go. Но, как обычно, есть нюансы. Причём сразу два: есть Web Workers, которые предоставляет окружение, и есть асинхронность, которой тоже нужно как-то грамотно управлять.
Посмотрел на пример с Web Worker, где автор создаёт два потока, — без слёз на этот код не посмотреть. Worker выполняет отдельный JS-файл, а если хочется собрать всё динамически, начинаются развлечения: функцию превращаем в строку, оборачиваем в Blob, создаём URL и только потом отдаём Worker. Извращение ещё то. Поэтому если мне действительно понадобится удобная многопоточность, я лучше возьму Go. В JavaScript это инструмент скорее для отдельных специфических задач, чем повседневная модель программирования.
А вот управление асинхронностью оказалось намного интереснее. Мне понравилась структура со страшным названием «асинхронный семафор (барьер)». Идея очень простая: есть несколько независимых операций, и продолжить работу можно только после завершения всех. Например, при запуске проекта нужно подключиться к базе данных, прочитать YAML-конфиг и поднять Redis. Только когда готовы все три компонента, выполняем callback. (см. рисунок)
В примере есть три флага. expectedFlags хранит полный набор условий, а readyFlags — уже выполненные. Каждый участник после завершения работы «поднимает» свой флаг, а барьер проверяет, собрались ли все. Set здесь особенно удобен: повторный barrier.set('database') ничего не сломает и не увеличит счётчик, потому что одинаковые значения в Set не дублируются. Просто и достаточно элегантно.
У меня когда-то была аналогичная задача на фронте: выполнить код только после того, как всё загрузилось и инициализировалось. Проект был древний и страшный, а я помню, как извращался с Redux. Решение в итоге собрал из говна и палок. Сейчас смотрю на барьер и думаю: вот же оно. Иногда полезно изучить название конструкции, которую ты когда-то самостоятельно изобрёл в гораздо более страшном виде.
Заодно посмотрел блок по функциональному программированию. Раньше все эти функторы, монады и каррирование выглядели как тайное знание бородатых программистов на Haskell. А потом выясняется, что половиной этих идей мы спокойно пользуемся в обычном JavaScript. Функции высшего порядка принимают и возвращают функции, замыкания сохраняют окружение, каррирование разбивает функцию на цепочку вызовов, а композиция позволяет собирать сложное поведение из простых функций.
Даже привычные Array и Promise можно рассматривать с этой точки зрения. Array через map ведёт себя как функтор, а через flatMap — как монадическая структура: значение находится в контексте «много значений». Promise — это контекст «значение появится позже», а then позволяет преобразовывать его, не вытаскивая из этого контекста вручную. Maybe/Optional описывает другой контекст — «значение может быть, а может и не быть»; в обычном JS отдалённо похожие задачи решают ?. и ??.
И вот тут я наконец понял основную идею функционального программирования. Значение помещается в некоторый контекст, а функции последовательно преобразуют его, стараясь не вытаскивать наружу и не мутировать состояние вручную. Звучит просто ровно до того момента, пока не узнаешь, что называется страшным словом «монада». Но без десятка нормальных примеров понять это почти невозможно. Но когда начинаешь видеть знакомые Array, map, flatMap и Promise, вся эта страшная теория внезапно становится намного ближе к обычному JavaScript.
Так что вывод получился забавный. В JavaScript вроде бы нет привычной многопоточности, а функциональное программирование кажется чем-то академическим и далёким. Но начинаешь копать — и выясняется, что асинхронные барьеры, функторы и монады уже давно находятся рядом. Просто раньше я пользовался частью этих идей, не зная, как они называются.
👍3