Я помню время, когда jQuery стоял почти на каждом сайте. Тогда еще не было фреймворков и jQuery был простым способом дотянуться до DOM: навесить обработчик, что-то показать, что-то спрятать, дёрнуть запрос. Тогда это решало большинство задач. Потом фронтенд стал сложнее. Во многом по делу, потому что приложения выросли, и React, Vue, Svelte выросли вместе с ними. Но такая сложность нужна не всегда.
Осталось много проектов, где сервер уже отдаёт готовую HTML-разметку, а на клиенте требуется лишь немного реактивности: открыть модалку, обновить счётчик, отфильтровать таблицу, переключить вкладку. Разворачивать ради этого полноценный фреймворк со сборкой и роутингом часто не хочется. А писать на чистом JavaScript быстро становится неудобно, когда нужно отслеживать состояние и синхронизировать его с DOM.
Я искал решение, которое даёт реактивность, но при этом остаётся максимально лёгким и не требует отдельного билда. Хотелось просто подключить скрипт к уже существующей серверной разметке — и чтобы она начала реагировать на изменения состояния.
Это длинный текст про то, как изнутри устроена Micra.js — маленькая библиотека, которая добавляет реактивность к HTML, отрендеренному на сервере. Я написал её, чтобы закрыть конкретную нишу: страницы и админки, где сервер уже отдаёт готовую разметку, а на клиенте нужно лишь немного интерактивности — переключить вкладку, открыть модалку, отфильтровать таблицу, сходить за данными. Раньше для этого тянулись к jQuery, потом к Alpine. Micra — это попытка дать тот же «сахар», но предсказуемо, типизированно и с учётом Content-Security-Policy.
В этой статье я хочу рассказать про инженерию: что происходит под капотом, почему именно так, и какие концепции из «большого» программирования прячутся за каждым модулем. По дороге я постараюсь объяснить вещи вроде микрозадач, абстрактного синтаксического дерева и алгоритма наибольшей возрастающей подпоследовательности.
Пару недель назад я писал, что у библиотеки появился второй читатель — языковая модель. Тогда я думал, что главная задача — научить LLM правильно её готовить. Но потом решил, что не менее полезно послушать, что LLM думает о самой библиотеке.
Я скормил llms.txt другой модели и попросил разобрать: чего не хватает «идеальному фреймворку для тех, кому React — оверхед». Часть идей взял на заметку. Примерно тогда же я доводил до конца PR с Micra в js-framework-benchmark — один из самых авторитетных бенчей JS-фреймворков, где рядом React, Vue, Svelte, Solid и другие. При ревью этого PR его автор указал на проблему, которая пересеклась с одним из пунктов от модели.
В прошлой заметке я выложил Micra.js — маленький реактивный фреймворк для server-rendered страниц. Дальше пошли две недели, за которые я выпустил несколько версий.
Добавил в Micra кросс-библиотечный бенчмарк. Постарался сделать честную страницу, где Micra, Alpine, petite-vue, Stimulus и голый ванильный JS гоняют одни и те же сценарии в соседних фреймах.
Цифры оказались приятно-неприличными. Обновить 5 строк в списке из 1000:
Я помню время когда jQuery был UI-библиотекой для работы с DOM по умолчанию. Потом появился Knockout.js, Backbone.js и так мы дошли до React/Vue/Svelte. Фронтенд стал сложнее. Но такая сложность нужна не всегда. В мире всё ещё существуют простые сайты, состоящие из нескольких страниц, небольшие SaaS, панели управления и блоги. Часто таким решениям не требуется большой и сложный фронтенд. И в этом случае тоже приходится думать с помощью какого инструмента решать задачу создания интерфейса. В своих проектах я пробовал разные подходы — от Vanilla.js до React-а и всё пытался подобрать удобное решение, позволяющее совместить в себе реактивность и возможности взаимодействия с DOM и поддержку серверного рендеринга. А еще для простых сайтов не хочется тянуть кучу зависимостей. Иногда достаточно просто подключить библиотеку извне.
Так в ходе поиска баланса я набросал Micra.js — небольшую UI библиотеку на 5 Kb. Она имеет свои плюсы и минусы, абсолютно неуникальна и не претендует на звание заменителя всех других библиотек. Скорее это сборник практик, которые сформировались в единое решение.
Что это и как работает
Micra.js — это лёгкий реактивный фреймворк, который работает через shallow‑Proxy, отслеживая только верхний уровень state. Любое изменение top‑level свойства вызывает ререндер, а вложенные объекты нужно заменять целиком. Все синхронные обновления состояния батчатся в один microtask, поэтому несколько записей приводят к одному ререндеру. При первом рендере Micra сканирует DOM, собирает директивы (data-text, data-if, data-model и др.) и кэширует их, чтобы последующие обновления были быстрыми и не требовали повторного обхода дерева.
Выражения в директивах выполняются через быстрый путь для простых обращений к свойствам или через компиляцию с глобальным кэшем для сложных выражений. Внутри выражений доступен расширенный exprState, включающий методы компонента и хелперы (prop, emit, fetch). Списки с data-each и data-key обновляются через keyed‑diff: переиспользуются существующие DOM‑ноды, создаются только новые, а исчезнувшие удаляются. Каждый data-component создаёт независимый инстанс со своим состоянием, методами, props из data-* и собственным жизненным циклом. Компоненты общаются через event bus, а доступ к конкретному экземпляру возможен через Micra.instances().
Как видите, ничего нового и уникального. Некоторые решения компромиссны. Например, проксирование top-level. Для решений типа "показать модалку", "отфильтровать таблицы" выглядит достаточно. Меньше багов, выше производительность, так как нет лишних обходов. Но я не исключаю, что позже приду необходимости deep‑proxy.
В то же время, я собрал сборник рецептов и набор компонентов, которые можно копировать и использовать. В качестве эксперимента, хочу попробовать обучить AI использовать Micra.js в качестве замены React. Задачка, конечно, требует времени, потому что кодовая база с React огромна. В то же время, это не всегда играет на руку. Примеры кода могут быть и хорошими и плохими и корректировка достигается с помощью правильный скиллов и гайдлайнов. И я думаю, что если это можно сделать с одной библиотекой, то можно и с другой.
В рамках работы над open-source проектом работал над задачкой по генерации тем. То есть в проекте было несколько тем: одни темы имели светлый и темный режим, другие - только светлый или только темный. И задача сводилась к тому, чтобы сделать для всех тем светлый и темный режим.
Вот на скриншоте показаны несколько темных тем: Dracula, Solarized, Gruvbox, Monokai, Moonlight. В теории можно взять и инвертировать светлоту, получив тем самым светлую тему. В теории теория и практика не отличаются. На практике всё совсем наоборот 😀. Чтобы объяснить почему, полезно сначала посмотреть на то, как устроена работа с цветом в принципе.
На днях с подачи приятеля подключил Garmin MCP к Claude Code. Опыт интересный. В первую очередь, потому что все эти данные мне и так известны и доступны в виде сухой статистики в Garmin Connect. Хотелось попробовать новый опыт взаимодействия.
С декабря прошлого года работаю над парой проектов, которые идут рука об руку. Про первую вкратце писал в итогах прошлого года. Про вторую не говорил, потому что показать было нечего. Теперь вот есть — TrueNorth
Недавно добавил на Syn-co.me новую функцию — поиск удобного времени для встреч. Если вы уже пользовались сайтом, то знаете: он показывает время всей команды на одном таймлайне. Просто добавляете участников с их часовыми поясами, и сразу видно, кто работает, кто спит, кто только проснулся.
Теперь при добавлении участников из разных городов, а алгоритм предлагает, когда лучше всего созвониться. Причем не просто "когда все не спят", а когда время действительно удобное для всех. Или хотя бы максимально справедливое, если идеального варианта не существует.
Проблема
Представьте: ваш коллега в Токио, партнер в Нью-Йорке, а вы в Москве. Когда в Москве 14:00 (отличное время для звонка), в Нью-Йорке 6 утра (кто-то еще спит), а в Токио уже 20:00 — конец рабочего дня.
Можно, конечно, открыть мировые часы и начать перебирать: "А если в 10 утра по Москве? Нет, подождите, тогда в Токио будет..." Через пять минут такой арифметики голова идет кругом.
Как это работает
Алгоритм проверяет каждые полчаса в ближайшие два дня и оценивает, насколько удобно это время для каждого участника. Не все часы одинаково хороши для встреч.
Оптимальное время — середина рабочего дня, примерно с 10 до 16 часов. Люди уже проснулись, выпили кофе, разобрали почту. Максимальная продуктивность.
Хорошее время — начало или конец рабочего дня. С 9 до 10 утра или с 16 до 17 вечера. Можно созвониться, но кто-то может быть не в лучшей форме.
Приемлемое время — чуть за границами обычного расписания. Например, 8:30 утра или задержаться до 18:00. Неудобно, но терпимо.
Некомфортное время — приходится серьезно менять планы. Встать пораньше в 7 утра или остаться после работы до 19:00. Такие встречи лучше избегать, но иногда выбора нет.
Алгоритм выбирает варианты, где всем хотя бы приемлемо, и сортирует их от лучших к худшим.
Что делает время действительно удобным
Тут начинается самое интересное. Я бы выделил три столпа на которых основана логика алгоритма.
Справедливость важнее общего удобства. Лучше, когда всем "хорошо", чем когда двоим "отлично", а одному "ужасно". Поэтому алгоритм в первую очередь смотрит на того, кому хуже всего. Если есть вариант, где минимальный комфорт выше — он побеждает.
Чем раньше, тем свежее. Потому что ранние встречи обычно продуктивнее поздних. Хотя и всё индивидуально, но обычно к вечеру люди устают, думают о доме, концентрация падает. При прочих равных алгоритм предпочитает утренние варианты.
Избегаем экстремальных часов. Никто не хочет созваниваться в 5 утра или в 11 вечера. Такие варианты получают серьезный штраф. Также учитывается обеденное время — встреча с 12 до 13 нравится немногим.
Гибкость как параметр
Иногда нужно быстро найти время, и все готовы немного подвинуть свое расписание. Иногда важно никого не беспокоить вне рабочих часов.
Поэтому в инструменте есть параметр гибкости — от 0 до 12 часов. С нулевой гибкостью алгоритм ищет только время в пределах рабочих часов. С гибкостью в 3-4 часа появляются варианты, где кто-то начинает чуть раньше или задерживается. Чем выше гибкость, тем больше вариантов, но тем менее комфортными они становятся.
Когда компромиссы неизбежны
Иногда приходится признать: идеального времени нет. Если участники размазаны по всему миру, кому-то точно будет неудобно.
Для Москвы, Нью-Йорка и Токио практически невозможно найти время, когда всем комфортно. Разница часовых поясов слишком велика. В таких случаях алгоритм предлагает несколько вариантов с разными компромиссами.
Может быть, один раз неудобно будет Токио, в следующий раз — Нью-Йорку. Справедливость можно обеспечить не за одну встречу, а в серии созвонов.
Про Syn-co.me
Инструмент работает прямо в браузере. Никаких регистраций, никаких серверов, никакого сбора данных. Вся информация о команде кодируется в URL — можете сохранить в закладки или отправить коллегам.
Изначально Syn-co.me создавался как простой таймлайн часовых поясов команды. "Stop doing timezone math in your head" — главная идея. Поиск времени встреч — логичное продолжение: показывать не просто "когда все свободны", а "когда всем будет действительно удобно".
Казалось бы, мелочь — подобрать время для звонка. Но эти мелочи съедают время и нервы. Особенно когда участников больше двух и часовые пояса разбросаны. Возможно, я слишком много думаю про расписания. Но когда постоянно работаешь с людьми из разных часовых поясов, начинаешь ценить инструменты, которые просто работают и не требуют усилий.
Статья «Rails for Everything» на Literally The Void отстаивает универсальность Ruby on Rails для разработки различных типов проектов, опровергая стереотип о его ограниченной применимости и подчеркивая преимущества использования полного стека Rails вместо разделения на микросервисы, в то время как комментарии на Reddit отражают разнообразие мнений разработчиков об актуальности фреймворка в 2025 году, его производительности и сравнении с современными альтернативами.
Petr.codes в «Flexible API versioning with Rails» предлагает гибкий подход к версионированию API в Ruby on Rails, рассматривая проблемы традиционных методов и демонстрируя эффективную архитектуру с использованием наследования и модулей в контроллерах, что позволяет разработчикам легко управлять изменениями между версиями API без дублирования кода.
Статья Мартейна Холса «The European Accessibility Act for websites and apps» разъясняет требования Европейского акта о доступности для цифровых продуктов, описывая сроки внедрения, технические стандарты и необходимые меры соответствия, которые должны предпринять разработчики и владельцы бизнеса для обеспечения доступности своих веб-сайтов и приложений в соответствии с законодательством ЕС.
Марк Мэнсон в статье «Why You Should Quit the News» утверждает, что следует отказаться от регулярного потребления новостей, поскольку они вызывают стресс, фокусируются на негативе и отнимают время, которое можно потратить на более ценные занятия, при этом большинство новостных материалов не имеют практического влияния на нашу повседневную жизнь.
Алекс Рассел в своей статье «If Not React, Then What?» критикует React за создание избыточного уровня абстракции над веб-платформой, что приводит к проблемам с производительностью и большим JavaScript-пакетам, и предлагает Web Components как более эффективную альтернативу, которая работает в гармонии с нативными возможностями браузера и следует принципам прогрессивного улучшения.
Ахмад Шадид выпустил гайд о новых CSS свойствах — «Relative Colors».
Mind if we count visits? It's one cookie and zero drama.