tgindex
a

Пишу о JavaScript и его экосистеме (TypeScript, React, NextJS), о процессе разработки и архитектуре. Блог: https://amorgunov.com По всем вопросам (рекламу не продаю): @saaaaaaaaasha

Последний пост
25 мая
Последнее чтение
ещё не заходили
Постов за неделю
0
Всего постов
20
Тип
открытый
Язык
русский
Категория
Технологии (по похожим)
В каталоге с
16 авг.
Подписчики
1 955
+1 за 1 дн.
Сутки
+1
+0,05%
Неделя
 
Месяц
 
Просмотров на пост
3 084
20 постов
Вовлечённость
157,7%
к подписчикам
Постов в день
0,0
всего 20
Упоминаний
0
каналов
Охват размещения
оценка
1/24сутки в ленте
1/48двое суток
1/72трое суток

Оценка по просмотрам недавних постов: пост набирает почти всё за первые сутки.

Посты

  • 25 мая1 0465422

    На прошлой неделе ездил на HolyJS. Про конференции в целом напишу отдельно - есть несколько мыслей про формат, цену билетов VS качество докладов и то, зачем вообще сейчас нужен офлайн (не просто так же FrontendConf закрыли в этом году). А сейчас расскажу про интересные наблюдения именно с текущей HolyJS. TL;DR: фронтенд переезжает в мир агентной разработки (как и для разработчиков, так и в движении в сторону agent-ready интерфейсов). 1️⃣ В программе было очень много про AI. Часть докладов была полностью про ИИ-агентов и базовую обвязку вокруг них (промпты, MCP, skills и т.п.), но пока довольно мало глубоких кейсов (типа статистики по ускорению time to market, реального опыта Spec-Driven Development подхода на больших кодовых базах или каких-нибудь низкоуровневых штук про устройство агентов). Из интересного - послушал про стандарт WebMCP (Web Model Context Protocol) и протокол A2UI от Google. Идея первого в том, что на сайте можно описать специальное API для основных операций, которые агент сможет вызывать как тулзы. Не прокликивать сценарий по кнопкам и ссылкам, как это происходит сейчас, а напрямую вызывать нужные действия через JS API (например, собрать корзину и сделать заказ в интернет-магазине). А A2UI развивает идею backend-driven UI: агент генерирует JSON-описание интерфейса, а клиент рендерит его через заранее подготовленный набор компонентов. В общем, веб-приложения скоро придется готовить не только для людей и поисковиков, но и для агентов. 2️⃣ Во многих компаниях сотрудникам оплачивают подписки клод кода. И это очень круто. Но есть и обратная сторона: одновременно вводят различные метрики по использованию токенов. Типа больше токенов сжег - лучше поработал, сынок. Выглядит подозрительно похоже на то, как лет 10–15 назад мерили количество строк кода, только теперь вместо LOC у нас токены. Посмотрим, что с этим будет дальше. Другая часть компаний поднимает китайские open-source модели внутри корпоративного контура (DeepSeek, GLM, Qwen и т.д.). На мой взгляд, в ближайшие пару лет дистанция между такими компаниями сильно изменится (по культуре разработки и процессам) и первые уйдут далеко вперед. 3️⃣ Пообщался много с кем, и у всех в компаниях либо сокращения, либо урезание бюджетов. Кто попал под сокращение, может достаточно долго искать работу, так как вакансий мало, кандидатов много (рынок работодателя). Нам, как специалистам, остается развиваться профессионально и отдельно прокачивать скилл прохождения собеседований (алгоритмы, архитектуру, знание платформы, поведенческие интервью). В общем, быть готовым. Ну и, конечно же, встретился с большим количеством старых знакомых и коллег. Пообщался с ребятами из @moscowjs, из сообщества @ithozyaeva, с @artalog (обсудили с Артемом Reatom, в частности роутер, скиллы и продвижение). Поймал сфоткаться @ulbi_tv (до сих пор считаю, что контент Тимура по фронтенду один из самых качественных в ру-сегменте). Посмотрел на себя в слайдах на докладе про убийцу FSD, но об этом и архитектурные методологии в целом напишу отдельно. P.S. Один из участников выиграл книгу «Настоящий SRE» от O’Reilly и подарил мне ее для розыгрыша в канале. Так что скоро разыграю и отправлю случайному счастливчику (Семен, спасибо).

  • 18 мая1 401865

    Друзья, всем привет! Давно ничего не писал, в очередной раз попробую оживить канал. Обещать стабильный график не буду: один-два раза в неделю небольшие разборы технологий, диванные мнения, вопросы для обсуждения в чате и истории про героически решенные проблемы (или до сих пор не решенные), с которыми я сталкиваюсь на своих проектах. Для тех, кто давно не заходил или уже забыл, кто я и чем занимаюсь: я всё так же работаю техлидом в ~Самокате~ ecom.tech, всё так же пишу сервисы на Next.js, иногда выступаю на конференциях и митапах. В прошлом году начал записывать видеоуроки по Feature-Sliced Design на YouTube (и эту историю тоже планирую возобновлять). Писать, как и раньше, буду в основном про фронтенд и всё техническое, с чем сталкиваюсь в рабочих задачах: Next.js, инфраструктуру, Node.js, SSR, масштабирование, проектирование, архитектуру, FSD, React, стейт-менеджеры, Web API, TypeScript, Local First, a11y и немного gamedev. В общем, всё то, во что я сам периодически ныряю и потом пытаюсь разложить по полочкам. Отдельно хочу делиться опытом работы с AI, но не совсем в традиционном формате. У нас в компании нельзя использовать Claude Code/Codex и внешние модельки, поэтому суровая рабочая реальность такова: внутренние китайские open source-модели, развернутые внутри периметра. Буду рассказывать, насколько с таким сетапом вообще можно работать (в чём он хорош, а где ещё сильно отстает от top-tier-моделей). Для пет-проектов я пробую Codex и подписку OpenCode GO (параллельно тестирую другие китайские модели - Kimi, GLM, Qwen, которые потенциально в будущем появятся и в рабочем окружении). Честно, я тут сильно отстаю от ребят, которые уже совсем не пишут код руками и ваншотят задачи опусами/соннетами. Но, возможно, именно поэтому будет интересно смотреть на опыт использования AI в таком контексте. В общем, возвращаюсь. Посмотрим, насколько меня хватит в этот раз :)

  • Interface merging в TypeScript В TypeScript есть возможность автоматически мержить интерфейсы с одинаковыми именами в один (в доке это называется более общим понятием «declaration merging»). Эта возможность, кстати, одна из причин, почему я долгое время в своих проектах предпочитал использование тайп алиасов (type) в качестве основы для описания типов, а не интерфейсов. Чтобы случайно неявно не объединить два интерфейса вместе. Но есть несколько кейсов, когда расширение интерфейсов является довольно полезной фичей (с типами, объявленными через type это сделать не получится). Первый кейс – расширение интерфейса внешних библиотек или глобальных переменных без прямого редактирования оригинального кода. Например, подключаем к jest кастомный матчер со своим API (мы уже мигрировали на vitest, но пример с матчерами для jest-тестов первым пришел на ум): declare global { namespace jest { interface Matchers { toBeCustomTrue(): CustomMatcherResult } } } // в любом тесте expect(value).toBeCustomTrue() Или добавляем какое-то новое поле в объект window (так же расширяем интерфейс Window) declare global { interface Window { chatSdk?: AwesomeChatSdk } } Изначально интерфейсы Window и Matchers объявлены в отдельных пакетах и хранятся в node modules проекта, а данным кодом (declare XXX) мы их расширяем. Причем один интерфейс можно расширять сколько угодно раз, компилятор TypeScript-а все это «проглотит» и сформирует общий интерфейс. Второе кейс (частный случай первого) – формирование интерфейса lazy-модулей внутри самого приложения. Например, у нас есть DI-контейнер, который изначально пустой и мы инжектим сервисы не глобально в каком-то общем одном файле, а внутри самого сервиса, чтобы сам DI-контейнер не знал о том, какие сервисы внутри мы регистрируем (разделение ответственности и все такое). Для этого внутри контейнера объявляем пустой интерфейс: // ~/shared/di/types.ts export interface DIContainer {} И расширяем его при инжекте модуля: // ~/.../themeService.ts container.register('THEME_SERVICE_TOKEN', themeService) declare module '~/shared/di/types' { export interface DIContainer { THEME_SERVICE_TOKEN?: typeof themeService } } TypeScript это подхватывает и в местах использования DI-контейнера подсказывает зарегистрированные типы. Такой же подход сейчас используется для lazy-слайсов в rtk. Пример реализации простейшего DI-контейнера можете посмотреть тут (хук useDi + сервисы на preact-signals).

  • stringbool в zod Пару дней назад закрыли старый ишьюс от 2022 года в библиотеке zod, в котором предложили добавить более строгую проверку (через дополнительный аргумент) на приведение строки к булаен типу (z.coerce.boolean()), чтобы при передаче "false" в виде строкового значения возвращался false. Эта история расширилась тем, что помимо "false", так же хотелось бы обрабатывать "0", "off" и подобные falsy-значения. Это можно было решить через кастомную схему и метод transform (рассказывал в видео про валидацию переменных окружения), но в четвертой версии zod появился тип stringbool, который решает эту проблему. Что он делает? Преобразует "булевы" значения в строковом виде (список можно переопределять) в простые булевы значения: const strbool = z.stringbool(); strbool.parse("true") // true strbool.parse("1") //true strbool.parse("on") // true strbool.parse("false") // false strbool.parse("0") // false strbool.parse("off") // false Прикольно видеть, как инструменты развиваются (даже на примере этой фичи, которая пришла от пользователей), да и как за небольшое время zod стал чуть-ли не классическим инструментом в мире фронтенда. Знаю и про arktype, и про valibot с joi, но по экосистеме и популярности zod далеко впереди всех (отчет в npm trends). Кстати эта фича вошла в 4-ую версию библиотеки, которая вышла совсем недавно. Из самого интересно: - работа с новой версией через сабпрефикс (zod/v4) вместе с zod@3.25.0. Это сделано, чтобы не сломать экосистему, так как при публикации сразу 4-ой мажорной версии в npm, всем зависимым библиотекам пришлось бы оперативно поддержать новое API (вот тут в ишью можно почитать подробнее https://github.com/colinhacks/zod/issues/4371) - релиз Zod Mini. Одна из больших проблем zod заключалась в том, что API библиотеки нормально не tree-shake-илась, из-за чего, например, мы не стали подключать ее на клиенте, или из-за чего появился valibot (аналог zod с хорошим tree-shaking-ом). В Zod Mini немного изменили API (перевели схемы на функциональный стиль через композицию функций) и поддержали tree-shaking. Поэтому если вы не использовали zod по той же причиной, теперь можно пробовать :)

  • Мнение – SolidJS За последний год удалось несколько раз пописать код в рамках пет-проектов на SolidJS (+ разбирали проект с менти), еще одном вечно перспективном фреймворке. Поделюсь своим мнением. У солида хорошая документация и онбординг-туториал, которые знакомят с основами фреймворка. А примерно год назад вышел мета-фреймворк SolidStart, который берёт на себя сборку (на базе vinxi), роутер и работу с SSR (серверные экшены, дедупликация запросов, прелоадеры и т.д.). Про солид часто говорят, что это реакт без костылей. Они и правда очень похожи, и если вы пишете на реакте, то легко поймёте код, написанный на солиде: тот же компонентный подход, JSX, похожие lifecycle-методы компонентов, контекст и инфраструктура сигналов, схожая с useState для работы с состоянием внутри компонентов. Но конечно есть и большие отличия. Во-первых, у солида высокая производительность благодаря fine-grained reactivity (мелкозернистой реактивности). Компоненты рендерятся всего один раз, а все обновления происходят на уровне DOM-нод. Это дает сильный буст по производительности в отличие от реакта, где компоненты перерендериваются при изменении состояния, даже если нет изменений. Во-вторых, вся система состояний построена на сигналах. Они не привязаны к UI (как тот же useState в реакте), и на их основе можно писать логику за пределами компонентов. В-третьих, компоненты вместе с JSX компилируются в обычные функции и реальные DOM-ноды, а не в обертки в виде React.createElement (из-за чего например нет синтетических событий и все dom-события максимально близки к нативным). Кстати, фича use из реакта 19 для получения данных из асинхронных источников, в SolidJS реализована уже давно в виде createResource и является целевым способом для работы с запросами. В целом все это выглядит очень круто. Но вопрос – солид появился уже давно и до сих во всех опросах state of js у него ну очень маленькая доля использования. Почему? Давайте разберем проблемы: 1️⃣ Очень маленькая экосистема поддерживаемых библиотек. В целом порты с реакта есть много для чего, но большая часть из них либо не официальная, либо не поддерживается. Можно все написать самому, но на это нужно много времени и ресурсов, которых обычно нет. Отсюда вытекает другая проблема, LLM-ки не очень хорошо помогают с написанием кода, часто используют API от реакта, особенно все что касается реактивности. 2️⃣ Сложности работы с сигналами. Вероятно просто нужно набить руку, но за то время, что я успел поработать, очень просто допустить ошибку (например, неправильно деструктурировать пропсы или передать в дочерние компоненты не сам сигнал, а его значение) и потерять всю реактивность. А из-за того что теперь не подебажить консоль логами внутри компонентов (они же вызываются всего один раз), то найти источник проблемы не так просто. Плюс когда начинаешь строить состояние вокруг сигналов и появляются сложные зависимые друг от друга цепочки данных, начинаешь сталкиваться с проблемами, с которыми раньше вообще не приходилось сталкиваться. 3️⃣ Производительность. Странно это относить в минусы, но на одном проекте на tanstack-query (у которого есть адаптеры для солида, что не может не радовать) в какой-то момент весь список элементов начал пересоздаваться при выполнении запроса внутри компонента одного из элементов. И это отрисовывалось с сильными лагами. Внутри tanstack-query все мемоизировалось, но где-то все равно ссылка на объект менялась. Где именно, так и не нашли, так как по всем данным лишних событий не было, но DOM-нода контейнера пересоздавалась. Вероятно эту проблему можно перефразировать так – проблемы с производительностью с текущими инструментами дебага решать намного сложнее, чем в реакте. Что имеем по итогу? Улучшенная легковесная версия реакта с маленькой экосистемой. Изучить для расширения кругозора, если вы пишете на реакте или хотите познакомиться с сигналами точно рекомендую. Если у вас сильная команда разработчиков, нужно писать сложный и интерактивный интерфейс и есть ресурсы, то SolidJS выглядит очень заманчивым вариантом. Во всех остальных случаях реакт выигрывает.

  • без подписи

  • Лечу на CodeFest Друзья, на этих выходных буду на самой большой конференции в Сибири – CodeFest. Буду рассказать (снова) про Feature-Sliced Design, а точнее как можно в проект с модульной архитектурой внедрить FSD почти без изменений в самой архитектуре. Из интересного, собрал демо-проект (исходники доступны на гитхабе), где для каждой версии проекта построил граф зависимостей модулей (слайсов) через dependecy-cruiser. На первом изображении схема проекта без FSD, код которого почти полностью написал cursor. На втором – тот же проект, но адаптированный согласно методологии. Визуально по графу можно увидеть преимущества FSD, которые мы получаем из коробки – направление зависимостей по слоям, отсутствие кросс-импортов (поддержка низкой связанности), высокое зацепление модулей, публичное API, а так же линтер, который за всем этим будет следить. Конечно, все это можно получить и без FSD, но в качестве стартовой точки на мой взгляд – это отличное решение. Если тоже будете на конфе, подходите поздороваться и пообщаться :)

  • Новая статья в блог - про кросс-импорты модулей. Решил разобрать свои черновики для статей, которые накопились за последние пару лет, но до публикации так и не дошли. Первый на очереди пост про кросс-импорты модулей: что это такое, как их решать и нужно ли их вообще запрещать. Если рассказать про кросс-импорты в четырех тезисах: 1️⃣ Этот термин обозначает использование одного модуля другими в рамках одного архитектурного слоя; 2️⃣ Чем больше кросс-импортов, тем выше связанность (англ. coupling), а нужно стремиться к низкой связанности (поэтому, например, в FSD кросс-импорты вообще запрещены); 3️⃣ Решаются кросс-импорты двумя способами: инверсией зависимостей и выносом общего кода в отдельный модуль; 4️⃣ Полный запрет кросс-импортов (нулевая связанность между модулями) не бесплатен и требует ввода дополнительных абстракций, что со временем может сильно усложнить когнитивную сложность кодовой базы. Приятного чтения :)

  • Всем привет! Запускаю серию уроков по Feature-Sliced Design! В рамках серии будем разбираться с базовыми концептами, рисуя схемы взаимодействия модулей и закрепляя практикой с лайвкодингом. Давно хотел поснимать видео контент по FSD, чтобы поделиться своим опытом, рассказать как решать самые популярные проблемы, поделать код ревью опенсорс проектов и просто собрать (и оживить) сообщество вокруг методологии. Уроки будут не совсем для начинающих (как минимум нужно знать термины из методологии, прочитать документацию и поразбирать примеры). По формату это будут 20-30 минутные ролики, больше похожие на уроки как из кого-нибудь онлайн курса. Сейчас уже подготовлено 6 уроков (про лайауты страниц, тему сайта, фич флаги, менеджер модалок и инфраструктурные сущности), в планах записать еще с десяток (про работу с API, пользовательскую сессию, кросс импорты, работу с redux, фрактальные под-слайсы и т.д.). Планирую выпускать по одному видео раз в неделю. Первое видео из серии будет доступно в это воскресенье в 12:00 по МСК на моем youtube-канале (там кстати уже есть немного обучающего контента). Сюда анонс каждого видео выкладывать не буду, чтобы не спамить лишний раз, поэтому кому тема интересна, подписывайтесь на youtube-канал и следите за обновлениями (сюда выложу готовый плейлист через пару месяцев).

  • Уязвимости в NextJS Я думаю уже почти все слышали про уязвимость в NextJS, которая позволяет при запросах обходить миддлевары, и например, пропускать обработку авторизационных токенов и прочие серверные проверки. Уязвимость очень проста в эксплуатации, достаточно прокинуть http-заголовок x-middleware-subrequest, которая пропускает указанные миддлевары. Подробнее почитать можно в блоге Рашида Алама. Наш проект это не затронуло, так как мы не смогли в свое время завести в нашей инфре стабильную работу миддлевар. Но пару месяцев назад мы столкнулись с другой уязвимостью на основе «cache poisoning», которая не получила столь бурную реакцию в интернете, но в нашем случае тоже могла бы привести к довольно критичным последствиям. Cache poisoning (дословно, отравление кэша) - это атака, при которой обычным пользователям отображается вредоносный ответ на основе манипуляций с веб-сервером и кэшем. Кэш позволяет отдавать идентичный ответ пользователю, который сделал аналогичный запрос. Как понять, что запрос аналогичный? У запросов должен быть одинаковый кэш-ключ, который обычно формируется на основе различных компонентов: тип запроса, хост, pathname, некоторые http-заголовки (конечно этих параметров может быть намного больше). Если ключ совпадает, то отдается сохраненный результат из кэша, если нет - то запрос начинает обрабатываться сервером. Но есть компоненты, которые никак не влияют на ключ кэша (например какие-нибудь http-заголовки). И если какой-нибудь из этих компонентов может повлиять на ответ, то можно подложить отравленный результат в кэш, который будет возвращаться уже обычным пользователям. Вот и мы нарвались на такую уязвимость в нексте (CVE-2024-46982). Если кратко, у NextJS в рамках page router-а есть режим SSR через функцию getServerSideProps, которая подготавливает данные в формате JSON для страницы. Для этого некст при заходе на страницу отправляет запрос по пути /_next/data/.... Но есть внутренний query-параметр ?__nextDataReq=1, при добавлении которого к странице возвращается только JSON-данные для нее. И этот query-параметр не является частью ключа для кэша. Условно https://a.com и https://a.com/?__nextDataReq=1 будут иметь один ключ. Если результат второго запроса сложить в кэш, то у всех пользователей вместо главной страницы будет открываться JSON с данными. Сам по себе getServerSideProps является динамическим и в кэш ничего не складывает. Но еще есть внутренний http-заголовок x-now-route-matches, "включающий" SSG (server side generation) режим некста, который складывает результаты в кэш. Понимаете к чему я веду? Уязвимость заключается именно в этом и с помощью комбинации ?__nextDataReq=1 + x-now-route-matches и одного запроса можно сломать любую динамическую страницу приложения, путем складывания в кэш JSON-данных для страницы и отдачи их вместо html-контента. Более того, в этот JSON часто попадают значения http-заголовков, поэтому в него можно положить произвольный текст. А с учетом того, что запросы продолжают отдавать content-type равный text/html, то получаем еще и XSS абсолютно для всех пользователей, которые просто зайдут на страницу. Подробнее про это можете почитать в блоге все того же профессора Рашида https://zhero-web-sec.github.io/research-and-things/nextjs-cache-and-chains-the-stale-elixir Уязвимость уже была пофикшена в новых версиях NextJS, поэтому для фикса нам нужно было апнуть минорную версию, но это уже другая история.

  • На HolyJS был еще один доклад про FSD от Евгения Паромова, в котором Женя разобрал 3 недостатка на основе рабочего опыта работы с методологией на разных проектах, и как их можно решать путем переосмысления слоев. Было бы конечно круто нам прямо на конференции провести дебаты, но я после своего доклада был настолько выжат, что сил ни на что практически не осталось. Но в будущем возможно что-нибудь подобное и организуем. Доклад был довольно крутым и по контенту, и по подаче. Когда его выгрузят на ютуб, всем, кто использует FSD, рекомендую посмотреть (пока доступна только преза). Но указанные проблемы мне все же хочется разобрать. 1️⃣ Огромное количество сущностей и фичей в проекте. Это можно встретить на многих проектах, построенных на FSD, так как большое количество слоев «заставляет» декомпозировать все на отдельные сущности и фичи, и даже группировка по связанной функциональности не помогает. Важное правило которое я со временем понял, что не все сущности/фичи заслуживают место в глобальном неймспейсе, в нем должны находится только самые важные для проекта слайсы. Если фича не переиспользуется, то очень вероятно, что она может быть заинлайнена внутрь сущности (например, авторизация или логаут в сущность пользователя, или добавление товара в корзину в сущность корзины). Мы у себя все базовые сценарии храним рядом с сущностью, а новые продуктовые фичи добавляем в слой features (с фич флагом, что бы в будущем можно было без особых затрат фичу выпилить). 2️⃣ Widgets + Features + Entities = High Coupling. Здесь проблема схожа с предыдущим пунктом, когда единый модуль (слайс) пытаемся разбить по различным слоям и получаем высокую связанность (или если быть точнее - destructive decoupling). Решается она через local-first подход, когда все держим локально внутри слайса до тех пор, пока это не понадобится где-то еще. Кортима FSD довела эту идею до абсолюта, предложив изначально все хранить на уровне слайсов страниц и теперь подход page-first является приоритетным при разработке согласно методологии. 3️⃣ Entities не позволяет описать бизнес модель. Тут поинт в том, что на слое сущностей у нас запрещены кросс импорты и приходится работу со связанными сущностями выносить на верхлежащие слои. Ранее это и правда было проблемой, но сейчас это решается через фичу @x, которая разрешает кросс импорты. Женя подметил, что в сущностях есть UI, кросс импорты которых часто приводят к циклическим зависимостям. Но FSD в этом плане гибкий, и на уровне сущностей мы можем вообще не использовать UI, спустив его на другие слои и используя как слой domain из чистой архитектуры. И что важно, разрешая кросс импорты методология не отказывается от инверсии зависимостей через DI. 4️⃣ Изменчивый shared. Тут проблема в том, что shared состоит из конкретных реализаций (api, UIKit) и все другие слои зависят от него, что в чистой архитектуре ui/инфра выносятся на самый верхних слой и не влияют на низлежащие слои путем работы через абстракции. Но тут есть два момента. Первый, мы пилим фронтенд и полностью абстрагировать тот же UI от модулей, где есть бизнес-логика обычно не имеет никакого смысла. Второй, к сожалению это присуще всем приложениям на любой архитектуре, если изначально плохо реализовать общие UI-компоненты, data-слой и другие shared-модули, то это приведет к сложному рефакторингу в будущем всего проекта. Я эту проблему вижу по другому. Слой shared в FSD - это по сути единый модуль (слайс), в котором нет никаких правил. И если приложение обрастает инфраструктурой, то зависимости внутри между различными модулями (условно менеджер модальных окон и ремоут конфиг) могут быть неявными и запутанными. Мы для решения выделили отдельный слой для инфраструктурных сервисов (тоже с правилами кросс-импортов), а тот же UIKit изначально был вынесен в отдельный пакет монорепы (что позволило его изолировать от бизнес-специфичных кейсов).

  • Друзья, всем привет! На прошлой неделе выступил на конференции HolyJS, рассказывал опять про FSD. В прошлый раз я получил много комментариев, что FSD плох, но за эти пол года методология претерпела изменения, а за день до выступления вышла минорная версия 2.1. Идея доклада была рассказать о новых вещах в FSD через проблемы, но к сожалению все опять подумали, что в FSD ничего не меняется, одни проблемы 😁 Я считаю что у каждого инструмента есть недостатки, и рассказывать о том, как работать с инструментом через решение проблем намного интереснее. О чем рассказывал? 1️⃣ Разбирал запрет кросс-импортов и как с ними работать через инверсию зависимостей и фичу @x, которая так и называется, публичное API для кросс-импортов (появилась в 2.1). 2️⃣ Про размазывание кода, когда мы сразу пытаемся все раскидать по всем слоям. Тут решение достаточно тривиальное - использовать local-first стратегию и не заниматься преждевременной декомпозицией. Даже если явно можно выделить сущность или фичу, не стоит ее сразу выносить в глобальный неймспейс, если она используется в одном месте. Вполне нормально, если фичи будут жить прямо в слайсе связанной сущности. 3️⃣ Про субъективное понимание слоев и почему все используют FSD по своему. Рассказал про page-slice подход (он появился в 2.1 и называется page-first), который меняет ментальное восприятие, как мы используем FSD. В этом подходе мы начинаем разработку от страниц и виджетов (а слои сущностей и фичей вообще первое время не используем). В связи с этим у методологии сейчас появилось два разных подхода, которые можно использовать (проектирование от сущностей VS от страниц). Какой способ выбрать, может зависеть от типа клиента (тонкий или толстый) и понимания полноты домена предметной области. 4️⃣ Про инфраструктурные сущности (мы у себя ввели кастомный слой shared/services) и как можно расширять методологию (новыми слоями и вертикально через домены). А так же рассказал про главную на мой взгляд проблему в методологии, из-за которой многие начинают использовать FSD неправильно - документация и отсутствие хороших примеров. Но не все так плохо, как может показаться. Документация хоть и медленными шагами, но переписывается и пополняется новыми туториалами. А еще кортима активно работает над архитектурным линтером steiger (недавно был релиз версии 0.5.0), который помогает избежать целого ряда проблем на старте (той же преждевременной декомпозиции). Запись я думаю выложат уже в новом году, обязательно скину ее в канал, а пока можете 👉 посмотреть слайды и 👉 поизучать дополнительный материал.

  • Что нового во фронтенде Признаюсь. Периодами я совсем перестаю следить за тем, что происходит во фронтенде, а сотни новых библиотек каждую наносекунду никто не отменял. Сейчас восполняю пробелы и попробую собрать самые значимые на мой взгляд изменения за последнее время. 1️⃣ Серверные компоненты/экшены В настоящее время идет довольно сильный мейнстрим на перенос части логики приложений на сервер (лучшие метрики типа TTFB/TTI за счет того, что часть JS-кода можно вообще не загружать на клиенте или загружать лениво). В React - это серверные компоненты, которые выполняются только на сервере и не попадают в клиентский бандл. Сюда же отношу серверные экшены, которые реализуют RPC (remote procedure call) и позволяют исполнить код на сервере прямыми вызовами из клиентских компонентов. Раньше думал это чисто история в рамках NextJS, но как оказалось они уже много где реализованы. Я до сих пор убежден, что 90% проектам все это не нужно и достаточно обычных SPA, но core-команда того же React сильно продвигает эти идеи, делая большой упор на серверные оптимизации, что подхватывают и другие. 2️⃣ Мета-фреймворки У каждого фреймворка (React, Vue, Svelte, SolidJS, Angular) есть свой мета-фреймворк (NextJS, NuxtJS, SvelteKit, SolidStart, Analog), который отвечает за сборку, роутинг, загрузку данных, кеширование и различные режимы работы (SSR/SSG и т.д.). Кажется время, когда мы собираем все по кусочкам подходит к концу, и скоро старт любого нового проекта будет начинаться с использованием мета-фреймворка. Хотя опять же считаю, что набор из Vite в качестве сборщика, роутера (ReactRouter), пакеты для работы с состоянием (Zustand+ReactQuery) будет достаточно для большинства проектов. 3️⃣ Серверные рантаймы С учетом того, что сервер сильно интегрируется во фронденд приложения (серверные компоненты/экшены, смотри 1️⃣👆), то возникает вопрос: на чем и где его запускать? Раньше у нас был единственный выбор - NodeJS. Но сейчас ежегодно появляются различные решения, типа Deno (на самом деле появился уже давно), Bun или Egde Runtime для NextJS от Vercel. И скорее всего будут побеждать библиотеки, которые умеют работать на различных рантаймах и деплоиться на различные облачные сервисы. Например, SolidStart (метафреймворк для SolidJS) и NuxtJS (для VueJS) используют для серверной части Nitro, который как раз таки супер универсальный и может запускаться на любом ~холодильнике~ рантайме. 4️⃣ Island (островная) Architecture - архитектура, которую уже давно много кто реализует у себя, получила особую популярность вместе с фреймворком Astro. Идея простая: рендер всего приложения на сервере, кроме динамических частей (так называемых виджетов). Они возвращаются с сервера в виде пустых слотов (их еще называют плейсхолдерами) и уже после гидрируются на клиенте как независимые модули (типа отдельных приложений), получая для себя HTML так же с сервера. Подходы partial hydration, progressive hydration, streaming rendering как раз таки лежат в основе «островов». NextJS пошел дальше и представил фичу Partial Prerendering (демка: https://www.partialprerendering.com), которая подготавливает статичную часть на момент сборки. Про это подробно писал SuperOleg. 5️⃣ Сигналы появились кажется уже везде (кроме React-а). Даже есть предложение на добавление сигналов в стандарт ECMAScript. Сигнал - это контейнер для значения (от примитивов до сложных структур данных), который может уведомлять потребителей об изменении этого значения. Другими словами сигнал обеспечивает отслеживание зависимостей при чтении и срабатывание эффектов при мутации (реактивный подход). Крутость в том, что на основе сингалов можно описать состояние и связать его напрямую с DOM-элементом без подписки на ререндер компонента. На таком подходе например построен компилятор SolidJS, который не полагается на виртуальный DOM, а делает биндинги напрямую к нативным элементам, что сильно улучшает перфоманс (условно изменение глобального провайдера контекста не будет вызывать перерендер всего поддерева). Кажется все, но если что-то упустил, пишите в комментариях, сделаем вторую часть)

  • Неделю назад прошла конференция «Я 💛 Фронтенд 2024», на которой я в качестве спикера рассказывал про архитектурную методологию Feature-Sliced Design. На отдельные ролики еще не нарезали, но есть запись трансляции всей конференции с разбитыми таймингами по докладам. Как выложат отдельное видео, скину его сюда. Уже давно хочу поделиться некоторыми мыслями насчет конференций (за последний год был на нескольких митапах, FrontendConf и CodeFest), что движение идет куда-то не в ту сторону. Часть с докладами уходит на второй план и уступает стендам партнерских компаний и охотой за мерчем. В ближайшее время соберу все вместе и закину свои рассуждения на этот счет. Что касается моего выступления. Рассказал что из себя представляет методология FSD (= архитектурные паттерны + правила по неймингу структуры проекта), основные сложности при использовании (= нужно знать архитектурные принципы, многие вещи методология не регламентирует, интегрировать в существующий проект практически нереально). Далее на примере разобрали основные слои и перешли к проблемам: кросс-импорты, зависимости на разных слоях (которые решаются инверсией), нехватка слоев. Несколько вещей, которые хочется отметить отдельно: 1️⃣ Было много хороших вопросов в чате, на какую-то часть ответил со сцены, на другие постарался ответить в чате конференции. Про микрофронты, про редакс со своим моностором, про внедрение в существующие проекты, про внедрение в компанию в целом, про то, можно ли замерить эффективность использования FSD. Может как-нибудь соберу все вместе и сделаю контрибьют в документацию FSD в раздел вопросов/ответов. 2️⃣ В чате написали, что как будто доклад про то, почему FSD не стоит использовать, сильно много проблем, которые приходится решать. На самом деле посыл был не такой. Проблемы есть у каждой методологии/фреймворка, FSD не исключение. Я мало говорил о достоинствах (и возможно зря), так как был сильно ограничен таймингами. Хотелось поделиться именно сложностями на основе реального опыта (считаю это самой интересной частью) и путями их решения. Через расширение методологии (местами через экспериментальные фичи) можно решить практически все, поэтому как минимум к изучению методологию точно рекомендую 👍 3️⃣ Про новый линтер. Пару недель назад core-команда анонсировала новый архитектурный линтер steiger, который выложили на гитхаб через несколько дней после выступления. Проект еще в статусе активной разработки, но у него очень большой потенциал. Его можно использовать и за пределами FSD, или использовать только те правила, которые релевантны для вас. Каждое правило - это обычная функция, в которой с помощью готовых хелперов можно делать самые разные проверки на директории, файлы или импорты внутри (например, правило проверки отсутствия публичного API у слоев https://github.com/feature-sliced/steiger/blob/master/packages/steiger-plugin-fsd/src/no-layer-public-api/index.ts#L5-L19). ESLint сильно ограничен по словам ребят, поэтому решили делать с нуля. Тут важно отметить, что FSD в первую очередь про архитектуру, на основе которой уже получается определенная структура проекта. Линтер - это про структуру. Но с соблюдением структуры (через линтер) можно следить за тем, что бы не нарушать архитектуру. С одной стороны замкнутый круг получается, с другой - хороший линтер будет помогать и с архитектурой, и со структурой проекта. P.S. Все ребята, которые были офлайн и подходили задавать вопросы, подискутировать по FSD и в целом про фронтенд, спасибо вам! Был очень рад со всеми пообщаться)

  • без подписи

  • Завтра буду выступать на конференции «Я люблю фронтенд» от Яндекса и рассказывать про архитектурную методологию Feature-Sliced Design. Сделаю общий экскурс, разберу основные понятия, подробно остановлюсь на нескольких слоях (сущности, фичи и виджеты). Разберу проблемы, с которыми столкнулся на рабочем проекте: взаимодействие модулей на разных уровнях, кросс-импорты и нехватка слоев. Для каждой проблемы конечно же расскажу о возможных вариантах решения. Если вы тесно работаете с методологией, то я вряд ли открою для вас что-то новое. Но если вы только планируете использовать FSD или просто тема интересна для вас, то присоединяйтесь к трансляции на ютубе. Сама конференция будет с 11:00 до 18:00, я начну вещать с 13:15 по МСК (запись тоже будет доступна). P.S. На фото репетиция выхода на сцену (все серьезно) и финальный домашний прогон. Пока уложиться в 30 минут, отведенные организаторами на выступление, не особо удается, но буду стараться попасть точно в тайминги)

  • В марте выступал на MoscowJS и рассказывал про NextJS, о чем стоит знать, если вы хотите использовать его в своем проекте. Доклад всего на 20 минут, но краткую выжимку сделаю ниже. Что важно знать про NextJS? У него сейчас доступны две архитектуры для разработки: Page Router (архитектура, реализующая стандартный SSR подход, которая считается устаревшей, но формально поддерживаемая разработчиками, на самом деле нет) и пришедшая на замену - App Router (доступна с 13 версии, вносит кучу новых фич, типа Streaming render, RSC, selective hydration, partial prerendering, все то, что было недоступно ранее). Эти архитектуры разделяют NextJS на два разных фреймворка (даже вся документация разбита на два независимых раздела). Далее рассказывал про темные стороны (проблемы): 1️⃣ Серверная составляющая. Довольно важная тема, которую редко обсуждают - конфигурируемость и гибкость сервера, чем NextJS не может похвастаться. Нельзя без манки-патчинга собирать логи, без кастомного сервера собирать http-метрики (и даже с ним внутренние метрики рендеринга остаются недоступными), нет никаких инструментов для борьбы с отказоустойчивостью и работы с кэшированием. Получаем черную коробку с очень скудным публичным API для работы с сервером. Справедливости ради, в App Router есть возможность работать с кэшем, но без него (и с RSC) приложение выжирало бы абсолютно все ресурсы серверов даже при небольшой нагрузке. У меня есть знакомые коллеги, которые распилили NextJS по частям и сами управляют жизненным циклом запроса, активно используют тот же программный кэш в процессе рендеринга реакта. Однако у ребят есть своя техническая команда, у которой есть на это время и ресурсы. НО, приложение можно запустить на облачных серверах Vercel, и многие пункты выше станут неактуальными (кэширование/логирование/сбор метрик). И тут мы переходим ко второй проблеме. 2️⃣ Вендорлок. У сообщества создалось впечатление, что разработчики NextJS разрабатывают и проектируют фичи в первую очередь под запуск на серверах Vercel (логично, компания этим зарабатывает деньги), и уже после для запуска на своей инфраструктуре. А деплой на своей инфре из-за «бедного» сервера совсем не тривиальный. И все бы ничего, но одновременно React сильно привязался к NextJS. Многие последние фичи реакта поддержаны именно в нексте, что создает неявную связь, хочешь использовать реакт, будь готов деплоиться на Vercel. 3️⃣ Будущее. Тут я рассказывал про RSC (серверные компоненты реакта), о которых уже писал ранее. Чтобы их использовать (доступны только в App Router), нужно сильно переписать проект, если он изначально был на Page Router, из-за чего многие отказываются мигрировать (мы у себя тоже пока решили жить на старой архитектуре). И здесь проблема в том, что Page Router тихо перестают поддерживать и развивать, а многие ишьюсы просто закрывают с комментами "решено в App Router - используйте его". А если проект начинать на App Router, то имеем другую пачку открытых ишьюсов уже в App Router, в части которых ребята из кортимы рекомендуют использовать Page Router (например баг с useParams в режиме SSG). Получаем замкнутый круг. Какое дальнейшее развитие я вижу? Разработчики NextJS не смогут усидеть на двух стульях и поддерживать две архитектуры, и от App Router отказываться точно не будут. Поэтому первое, что сейчас важно сделать команде некста, это активно закрывать баги в новой архитектуре и в какой-то момент официально задепрекейтить старую (это точно негативно воспримет часть сообщества, но других вариантов я не вижу). И второе, работать над расширяемостью и кастомизацией серверной части, чтобы уменьшить поток критики по поводу вендорлок-стратегии. Бонусом собрал немного полезного материала про NextJS, который использовал в ходе подготовки к докладу.

  • Друзья, всем привет! Почти год ничего не писал, и за это время накопилось большое количество тем, которые хочется с вами обсудить. Пора оживлять канал, поэтому в ближайшее время ждите посты про NextJS (RSC и React конечно же), про Feature-Sliced Design, про фронтенд платформу, про обзоры технических книг, которые я прочитал за последнее время и про многое другое, что каким-то образом связано с разработкой. Для новеньких буквально пару слов о себе (а таких за год появилось довольно много, за что отдельное спасибо Виталию @web_platform, который собрал папку с фронтенд группами и добавил этот канал, вас стало чуть ли не в два раза больше). Меня зовут Александр, живу в Питере, работаю над клиентским веб приложением Самоката в роли техлида. До этого работал в Яндексе и аутсорсе. Иногда выступаю с докладами, периодически занимаюсь менторингом (много лет назад в Яндексе даже офлайн курс по фронтенду организовывал). Здесь публикую контент для уровня мидл и выше, совсем о базе почти не пишу. В любом случае всем рад, добро пожаловать!) Кстати, на этих выходных (уже завтра) буду на конференции CodeFest в Новосибирске, после сделаю выжимку самых интересных докладов, которые получится послушать. Если вдруг тоже будете там, подходите пообщаться, большую часть времени буду у стенда Samokat_tech.

  • Всем привет! На выходных дописал пост в блог про архитектуру проектов на основе вертикальных слайсов: https://amorgunov.com/posts/2023-05-28-vertical-sliced-architecture-in-frontend/ Получилось немного сумбурно, но по итогу удалось сформировать универсальную структуру фронтенд проекта. Сейчас нет смысла собирать структуру с нуля (можно использовать тот же Feature Sliced), однако понимать архитектурные подходы, которые используются внутри, нужно. Особенно, если вы запускаете новые проекты или занимаетесь рефакторингом текущих (или планируете это делать в будущем). Вертикальные слайсы - один из таких подходов.

  • Валидация переменных окружения Валидацией форм или параметров запроса никого не удивить, а вот на переменные окружения часто забивают и используют их напрямую. В чем проблема такого подход? Все переменные окружения в нашем коде будут типа string или undefined, даже если переменная описывает какой-нибудь булеан флаг или число. Поэтому часто в коде можно встретить конструкции типа process.env.IS_ENABLED === 'true' (здесь true - это строка) или parseInt(process.env.DELAY_MS, 10). И в каждом месте использования нужно не забыть сделать такую проверку. А еще, частая ситуация, что кто-то добавляет required переменную и у всех остальных проект начинает падать в рантайме в момент использования этой переменной. Ну или проект не падает, но спустя пол года файл .env сильно устаревает. Как решить эти проблемы? 1. Все переменные можно считывать в одном месте (например, в каком-нибудь src/env.ts файле), трансформировать/валидировать данные под нужный формат, а во всех остальных местах использовать уже подготовленные переменные. Как бонус можно запретить использование process.env.* за пределами этого файла с помощью eslint. 2. Я недавно писал про библиотеку zod, которая позволяет описывать схемы валидации. Она также позволяет трансформировать данные, а в одном из последних релизов появилась возможность приведения (coerce) примитивных типов. Например, в процессе валидации преобразовать входные данные к нужному примитивному типу (в нашем случае для переменных окружений, которые представляют собой числовое значение, преобразовать строку в число). Эта библиотека отлично решают задачу подготовки переменных. Что нам дают эти два пункта? Приводим переменные окружения к нужному типу, валидируем их и не даем запустить проект при отсутствии обязательных. Пример валидации env переменных с помощью zod можно посмотреть тут.