amorgunov
1.96K subscribers
16 photos
1 video
101 links
Пишу о JavaScript и его экосистеме (TypeScript, React, NextJS), о процессе разработки и архитектуре.

Блог: https://amorgunov.com
По всем вопросам (рекламу не продаю): @saaaaaaaaasha
Download Telegram
В прошлом месяце я начал работать над пет проектом на стеке React/Redux-Toolkit/Vite, используя методологию Feature Sliced (FSD). Вы можете посмотреть на него здесь: https://github.com/noveogroup-amorgunov/nukeapp

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

Сейчас я экспериментирую именно с FSD. Это архитектурная методология для фронтенда, которая говорит, как правильно должны быть расположены директории в проекте, а модули взаимодействовать друг с другом. Внутри себя FSD объединяет различные лучшие практики, такие как вертикальные слайсы, публичный API, композиция модулей, чистая архитектура, low coupling high cohesion. На мой взгляд это лучшее, что сейчас есть на рынке. Но по ряду причин методология часто используется не верно, а порой приносит больше вреда, чем пользы (мало примеров, много ситуаций, которые FSD никак не регламентирует, да и в общем архитектура - всегда сложно).

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

В общем, если вам будет интересно посмотреть на Feature Sliced в бою, то заглядывайте в репозиторий. Кстати, ссылку на проект добавили в примеры на официальный сайт методологии (https://feature-sliced.design/examples), поэтому проект уже принес небольшую пользу сообществу, пополнив шоукейс методологии.
Всем привет! С того момента, как я опубликовал предыдущий пост, количество звездочек у репозитория на гитхабе выросло на 140%, почти догнав все официальные примеры по Feature Sliced методологии. Спасибо за поддержку ❤️

Ниже хочу поделиться некоторыми техническими моментами, которые открыл для себя, делая этот пет проект:

- Насколько же приятно работать с бандлером Vite. У меня есть ассоциации с Vue (на котором я очень давно не писал, но это был очень приятный опыт с точки зрения разработки). Так вот так же и Vite, очень быстрый запуск, горячая перезагрузка, CLI стартер под нужный стек. Я ничего не могу сказать про большие проекты, на сколько в них Vite стабилен, но для небольших - работает отлично. Интересно в будущем попробовать SSR плагин в связке с React-ом.

- С помощью плагина eslint-plugin-boundaries можно (делать настоящую магию) задавать кастомные правила линтера для импортов внутри проекта. Для простых кейсов есть правило no-restricted-imports, для сложных - рекомендую именно этот плагин (на основе этого плагина сделано правило для импортов по FSD, которое я немного расширил для кросс-импортов в рамках слоя).

- Попробовал библиотеку zod для валидации runtime-структур. Описывайте схему объекта, с помощью которой можно и TypeScript тип вывести, и провалидировать данные. Можно проверять все то, что на момент компиляции типа unknown (переменные окружения, формы, параметры URL, ответы API и т.д.). Тоже рекомендую.

- Узнал, как работать с темным режимом. У меня на практике ни на одном проекте не было тем. Был приятно удивлен поддержкой браузеров (например, атрибут color-scheme), возможностями css-переменных для довольно простого переопределения цветовой палитры. А так же всяким UX штукам, что толщину шрифта на темных темах нужно делать меньше или накладывать фильтры на картинки, чтобы они были не такие яркие.

- И пару вещей про гитхаб: (1) бейджи для README можно генерировать в https://shields.io/ (куча интеграций и кастомные темы), (2) для светлой/темной тем можно отображать различные картинки в markdown файлах, добавив в ссылку на изображение специальный хеш: #gh-dark-mode-only или #gh-light-mode-only (подробнее в блоге самого гитхаба: https://github.blog/changelog/2021-11-24-specify-theme-context-for-images-in-markdown/).
ts-reset

Вы знали, что в TypeScript-е методы JSON.parse и response.json() внутри fetch по умолчанию неявно возвращают тип any? Мы и правда не можем знать, какая структура данных находится в строке или придет с бекенда в рантайме. Но вместо того, что вернуть unknown и явно сказать, что тип неизвестен, TypeScript для поддержки обратной совместимости возвращает any.

И таких небольших моментов довольно много: filter(Boolean) не сузит тип, Array.isArray или Array.from так же вернут any[], многие методы массива (includes, indexOf) будут некорректно работать с const assertions.

Мэтт Покок (на ютубе ведет канал про TypeScript) собрал такие кейсы в виде небольшой библиотеки ts-reset, которую можно подключить на свой проект в виде деклараций типов (d.ts файл). Все, что делает библиотека, переопределяет встроенные в TS интерфейсы (declaration merging). В issues можно посмотреть еще на ряд случаев, когда TypeScript плохо или странно выводит типы.

Подключив ts-reset к проекту, вы вряд ли получите какой-то вау эффект, но определенно сделаете типы на встроенные интерфейсы в языке лучше.
Валидация переменных окружения

Валидацией форм или параметров запроса никого не удивить, а вот на переменные окружения часто забивают и используют их напрямую. В чем проблема такого подход? Все переменные окружения в нашем коде будут типа 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 можно посмотреть тут.
Всем привет! На выходных дописал пост в блог про архитектуру проектов на основе вертикальных слайсов: https://amorgunov.com/posts/2023-05-28-vertical-sliced-architecture-in-frontend/

Получилось немного сумбурно, но по итогу удалось сформировать универсальную структуру фронтенд проекта. Сейчас нет смысла собирать структуру с нуля (можно использовать тот же Feature Sliced), однако понимать архитектурные подходы, которые используются внутри, нужно. Особенно, если вы запускаете новые проекты или занимаетесь рефакторингом текущих (или планируете это делать в будущем). Вертикальные слайсы - один из таких подходов.
Друзья, всем привет! Почти год ничего не писал, и за это время накопилось большое количество тем, которые хочется с вами обсудить. Пора оживлять канал, поэтому в ближайшее время ждите посты про NextJS (RSC и React конечно же), про Feature-Sliced Design, про фронтенд платформу, про обзоры технических книг, которые я прочитал за последнее время и про многое другое, что каким-то образом связано с разработкой.

Для новеньких буквально пару слов о себе (а таких за год появилось довольно много, за что отдельное спасибо Виталию @web_platform, который собрал папку с фронтенд группами и добавил этот канал, вас стало чуть ли не в два раза больше). Меня зовут Александр, живу в Питере, работаю над клиентским веб приложением Самоката в роли техлида. До этого работал в Яндексе и аутсорсе. Иногда выступаю с докладами, периодически занимаюсь менторингом (много лет назад в Яндексе даже офлайн курс по фронтенду организовывал). Здесь публикую контент для уровня мидл и выше, совсем о базе почти не пишу. В любом случае всем рад, добро пожаловать!)

Кстати, на этих выходных (уже завтра) буду на конференции CodeFest в Новосибирске, после сделаю выжимку самых интересных докладов, которые получится послушать. Если вдруг тоже будете там, подходите пообщаться, большую часть времени буду у стенда Samokat_tech.
В марте выступал на 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, который использовал в ходе подготовки к докладу.
Завтра буду выступать на конференции «Я люблю фронтенд» от Яндекса и рассказывать про архитектурную методологию Feature-Sliced Design. Сделаю общий экскурс, разберу основные понятия, подробно остановлюсь на нескольких слоях (сущности, фичи и виджеты). Разберу проблемы, с которыми столкнулся на рабочем проекте: взаимодействие модулей на разных уровнях, кросс-импорты и нехватка слоев. Для каждой проблемы конечно же расскажу о возможных вариантах решения.

Если вы тесно работаете с методологией, то я вряд ли открою для вас что-то новое. Но если вы только планируете использовать FSD или просто тема интересна для вас, то присоединяйтесь к трансляции на ютубе. Сама конференция будет с 11:00 до 18:00, я начну вещать с 13:15 по МСК (запись тоже будет доступна).

P.S. На фото репетиция выхода на сцену (все серьезно) и финальный домашний прогон. Пока уложиться в 30 минут, отведенные организаторами на выступление, не особо удается, но буду стараться попасть точно в тайминги)
Неделю назад прошла конференция «Я 💛 Фронтенд 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 и в целом про фронтенд, спасибо вам! Был очень рад со всеми пообщаться)
Что нового во фронтенде

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

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, а делает биндинги напрямую к нативным элементам, что сильно улучшает перфоманс (условно изменение глобального провайдера контекста не будет вызывать перерендер всего поддерева).

Кажется все, но если что-то упустил, пишите в комментариях, сделаем вторую часть)