Dev News от Максима Соснова
2.79K subscribers
15 photos
1.28K links
Привет! Меня зовут Максим Соснов и по утрам я читаю всякие разные дайджесты про фронтенд, разработку и управление разработкой. Самые интересные, по моему мнению, ссылки из этих дайджестов я кидаю в этот канал с небольшим описанием.

Контакт: @msosnov
Download Telegram
Building Type-Safe Compound Components

Статья про правильную организацию связанных компонентов (Compound Components) с корректной проверкой типов. Каноничные примеры таких компонентов: Select+Option, Modal+ModalHeader+ModalFooter+ModalContent

Сначала автор разбирает Select+Option
import { Select, Option } from '@/components/select';

function ThemeSwitcher({ value, onChange }) {
return (
<Select value={value} onChange={onChange}>
<Option value="system">🤖</Option>
<Option value="light">☀️</Option>
<Option value="dark">🌑</Option>
</Select>
);
}



Какие тут проблемы видит автор:
1. Появляется возможность всунуть в children не Option, чего мы не хотим. Типизировать компонент так, чтобы можно было всунуть только конкретный Option - неправильно с точки зрения идеологии компонентов React
2. Select не нужен для статичных данных, особенно если там меньше 5 элементов. Select нужен для динамичных данных
3. Писать Option в разметке, когда мы там ничего больше и не хотим видеть - странное лишнее занятие

Поэтому лучшее решение - сделать проп options, а сам Select - generic-компонентом, у которого options, onChange и другие пропсы связаны одним типом

import { Select } from '@/components/select';
import { useSuspenseQuery } from 'some-query-library';

function UserSelect({ value, onChange }) {
const userQuery = useSuspenseQuery(userOptions);

return (
<Select
value={value}
onChange={onChange}
options={userQuery.data}
/>
);
}

// Типизация Select компонента
type SelectValue = string | number;

type SelectOption<T extends SelectValue> = {
value: T;
label: string;
};

type SelectProps<T extends SelectValue> = {
value: T;
onChange: (value: T) => void;
options: ReadonlyArray<SelectOption<T>>;
};



Те же критерии верны и для модалки - нет смысла давать возможность указывать children, вместо этого лучше воспользоваться слотами

function ModalDialog({ header, body, footer }) {
return (
<DialogRoot>
<DialogBackdrop />
<DialogContent>
<DialogHeader>{header}</DialogHeader>
<DialogBody>{body}</DialogBody>
<DialogFooter>{footer}</DialogFooter>
</DialogContent>
</DialogRoot>
);
}

// Usage:
<ModalDialog header="Hello" body="World" footer="!" />




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

import { RadioGroup, RadioGroupItem } from '@/components/radio';
import { Flex } from '@/components/layout';

function ThemeSwitcher({ value, onChange }) {
return (
<RadioGroup value={value} onChange={onChange}>
<Flex direction={['row', 'column']} gap="sm">
<RadioGroupItem value="system">🤖</RadioGroupItem>
<RadioGroupItem value="light">☀️</RadioGroupItem>
<RadioGroupItem value="dark">🌑</RadioGroupItem>
</Flex>
</RadioGroup>
);
}



Чтобы достичь типизации, можно сделать RadioGroupItem дженериком
<RadioGroupItem<ThemeValue> value="system">🤖</RadioGroupItem>


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

type ThemeValue = 'system' | 'light' | 'dark';

type ThemeSwitcherProps = {
value: ThemeValue;
onChange: (value: ThemeValue) => void;
};

const Theme = createRadioGroup<ThemeValue>();

function ThemeSwitcher({ value, onChange }: ThemeSwitcherProps) {
return (
<Theme.RadioGroup value={value} onChange={onChange}>
<Flex direction={['row', 'column']} gap="sm">
<Theme.RadioGroupItem value="system">🤖</Theme.RadioGroupItem>
<Theme.RadioGroupItem value="light">☀️</Theme.RadioGroupItem>
<Theme.RadioGroupItem value="dark">🌑</Theme.RadioGroupItem>
</Flex>
</Theme.RadioGroup>
);
}



https://tkdodo.eu/blog/building-type-safe-compound-components

#development #react #typescript #javascript #compoundComponents
👍8👎31
Stop turning everything into arrays (and do less work instead)

Простая статья с основным посылом прямо в заголовке - хватит превращать все в массивы, можно делать проще и эффективнее. Если коротко - базовые цепочки типа arr.map().filter().slice().map() для большой нагрузки (или больших массивов) эффективнее заменить на итераторы items.values().filter().map().take().toArray().

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

Если у вас массив из 5000 элементов и вы сделаете arr.map(fn).map(fn2).filter(fn3).slice(0,10) то, несмотря на то, что вам нужно всего 10 элементов, движок обработает перед slice все 5000 элементов дважды в map. Т.е. в массивной цепочке операции выполняются друг за другом для всего потока данных.

Аналогичная цепочка в итераторах выглядит так items.values().map(fn1).map(fn2).filter(fn3).take(10).toArray(). Эта цепочка выполняет каждую операцию друг за другом для каждого элемента в отдельности и цепочка завершится ровно тогда, когда take(10) наберет 10 элементов после filter.

Еще в статье приводится юзкейс с загрузкой пагинированных данных через итераторы
async function* fetchPages() {
let page = 1;
while (true) {
const res = await fetch(`/api/items?page=${page++}`);
if (!res.ok) return;
yield* await res.json();
}
}

const firstTen = await fetchPages()
.filter(isValid)
.take(10)
.toArray();


Выглядит достаточно лаконично

Когда не следует использовать итераторы:
1. Нужен доступ по индексу (arr[10])
2. Алгоритм изменяет массив
3. Мало данных - в этом случае проще будет обойтись методами массива

Достаточно простая заметка, но напоминает о возможностях итераторов в современном JS

https://allthingssmitty.com/2026/01/12/stop-turning-everything-into-arrays-and-do-less-work-instead/

#development #javascript #array #iterators
👍26🔥3👎1
War story: the hardest bug I ever debugged

Наверняка вы сталкивались с багами, которые настолько неуловимые, что аж бесит. Только кажется, что нашёл причину, как оказывается, что это тупик. Статья как раз про такую историю с очень интересным багом.

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

Были обращения пользователей, но не прям массовые. Из обращений пользователей вида "баг происходит когда я делаю Х" ни одно не повторилось. Вообще не было уверенности, что этот баг вообще происходит. Но если он происходит — это точно блокер. Единственное, что известно — на баг жалуются пользователи Google Chrome.

Автор запустил дев-версию Google Docs и начал по-разному играться с UI. Ну, насколько это возможно для Google Docs — копировал, вставлял, игрался с форматированием, переносами и т. д. и т. п. В один момент он решил сделать скрипт, который создаёт 50 страниц текста и меняет жирность всего текста 100 раз. И оказалось, что баг повторяется между 10 и 40 итерациями! Постоянная смена жирности текста в итоге крашила что-то в приложении и появлялся огромный отступ.

Был найден способ воспроизвести баг, но всё ещё непонятно, где он конкретно появляется, т. к. движок рендера Google Docs был достаточно особенным — в те годы весь текст под меню позиционировался через абсолюты. Чтобы это работало быстро, большинство расчётов кешировалось. По стек-трейсу удалось понять, что кто-то записывает в кеш неправильное значение, которое затем крешит приложение.

Автор был более опытен в бекенде, поэтому позвал более опытного в UI коллегу разбираться с багом.

Они начали дебажить код, который рассчитывает значение, которое складывается в кеш — обложили всё консольными логами и работали в дебагере, но ничего такого поймать не смогли.

В такие моменты начинаешь проверять самые безумные теории. Было решено залогировать вывод Math.abs() — вероятность ничтожно мала, но мало ли. Но оказалось, что Math.abs() для отрицательного числа может вернуть то же самое отрицательное число. ВАУ!

Благо ребята работали в Google и просто зашли к чувакам, которые разрабатывают браузер. Там им ответили чисто по корпоративному: «Да, это баг, но технически он не наш — это вам в команду V8 движка». Оказалось, что баг уже пофикшен и выйдет в следующем релизе. Ребята оптимизировали работу Math.abs() в движке и допустили небольшую ошибку, приводящую к тому, что Math.abs() возвращал не то значение.

Достаточно интересная история с неожиданным источником проблемы. Всё-таки мы всегда доверяем встроенным в движок методам (особенно если это Math.abs(), где и ошибиться-то негде). Но вот бывает и такое.

https://www.clientserver.dev/p/war-story-the-hardest-bug-i-ever

#development #javascript #bug #googleDocs
😱31🔥9😁21🤩1
7 practical animations tips

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

Разобраны следующие примеры:
- Изменение scale на кнопке при взаимодействии
- Анимацию появления чего-либо (например тултипов) следует делать не со scale(0), а с хотя бы scale(0.9)
- Если у вас в интерфейсе есть задержка перед появлением тултипов, то для рядом расположенных элементов задержка имеет смысл только при первом появлении тултипа, а дальше тултип можно показывать сразу
- Выбирайте правильную кривую анимации. Для своих кейсов можно сделать кастомную
- Анимация появления должна быть направлена от точки, где пользователь взаимодействовал с интерфейсом (этот пример в статье заметен только с включением замедления анимации)
- Анимации должны быть быстрыми. Быстрые анимации дают ощущение быстрого продукта
- Если нужно сделать анимацию изменения состояния, а состояния разные - используйте blur.

Лучше зайти в статью и глянуть разницу анимаций своими глазами

https://emilkowal.ski/ui/7-practical-animation-tips

#development #javascript #css #animations
👍10
Yarn 6 Preview

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

Работы по переписыванию уже ведутся какое-то время и текущая версия реализации уже работает в разы быстрее.

Кроме переписывания на Rust (и, следовательно, более быстрой работы) обещают:
- Ленивую установку пакетов - это когда вы делаете Yarn run, а Yarn сам поймет: надо ли установить какие-то пакеты или нет
- Yarn Switch - свой менеджер менеджеров пакетов. Вы в package.json указываете, какой пакетный менеджер используется в проекте, а Yarn Switch занимается установкой и запуском этого менеджера пакетов

https://yarn6.netlify.app/blog/2026-01-28-yarn-6-preview/

#development #javascript #yarn #rust
👍6💩41
Here's a 1 GB memory reduction for very long Claude Code sessions

Начал снова читать Твиттер, а там оказывается интересное показывают! Например, однострочный фикс утечки памяти в Claude Code, которая спасает гигабайты оперативки.


Код с утечкой памяти: () => controller.abort()
Фикс: controller.abort.bind(controller)

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

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

https://x.com/jarredsumner/status/2017825694731145388

#development #javascript #performance #claudeCode #memoryLeak
8
Немного про свой ИИ-кодинг

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

Недавно, читая посты в твиттере и чье-то интервью или заметку (по-моему Мартина Фаулера), я нашел интересную мысль: многие бест-практисы разработки, которые продвигали Мартин Фаулер, Кент Бек, Боб Мартин и другие, хорошо ложатся на ИИ-кодинг.

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

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

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

Я в своем пет-проекте решил попробовать часть этих практик для кодинга. Сразу оговорюсь, что я не так много развлекаюсь с ИИ-кодингом т.к. на работе я в основном занимаюсь всяким менеджментом, а пет-проектом занимаюсь в фоне.

№1 TDD
TDD, как паттерн разработки, прекрасно работает для ИИ-кодинга:
- Пиши тест, убедись что он падает
- Напиши реализацию, убедись что тест проходит
- Рефактори

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

Что для этого нужно:
- Правило или скилл, которые скажут агенту а) как надо разрабатывать по TDD; б) запускать тесты и не завершать задачу, пока все тесты не будут зелеными
- Какие-то примеры, какие тесты считать хорошими и как вообще надо тестировать

Но, конечно, в современном фронтенде не без проблем. Однажды ИИ написал бесконечный ререндер компонента через useEffect, что привело к бесконечному запуску тестов. Когда я указал ему, что тесты бегают в бесконечном цикле и это надо исправить. Я его оставил, но через какое-то время обнаружил, что макбук начал лагать. Оказывается, ИИ решил проверять исправления следующим образом: запускать тесты в фоне и, если через 15 секунд процесс не завершился - значит не починил. Так он и запустил в фоне 6+ vitest с бесконечным циклом внутри react.

№2 BDD
BDD оказался очень удобным фреймворком для ИИ-кодинга в пет-проекте. Как это работает: я описываю юзер стори, которые надо реализовать. Затем мы с ИИ описываем их в feature-файле через gherkin (формат сценария Given-When-Then)

Таким образом я получаю файл со сценариями, которые описывают как работает приложение. Этот же файл служит источником для компонентных тестов cypress и некоторых юнит-тестов

№3 Строгое разделение ответственности
Тут много всяких паттернов: SOLID, CQS, View-Controller и так далее.

Суть в том, чтобы строго следовать бест-практисам по делению ответственности. В текущем сетапе у меня настроены такие правила хорошего дизайна:
- В *.types.ts описывается интерфейс сущности с JSDoc. Интерфейса должно быть достаточно для использования сущности потребителем, т.е. не надо сканировать всю реализацию для ее использования
- React-компоненты жестко делятся на View (не достают ничего из DI, не используют стор) и на Container. Все состояния View компонентов загоняются в storybook. Также view-компоненты удобно тестировать в cypress
- Логика приложения находится в store. API на изменение стора - это экшны. У фичи может быть несколько сторов для разных кейсов. Например в текущей фиче, которую щас делаю, 3 стора: UI конфигурации, доменная модель настроек, применение фичи на канбан-доске.

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

Поделитесь в комментах фидбеком или тем, какие вы практики нашли полезными в ИИ-кодинге - буду учиться :)





#development #javascript #bdd #cypress #note
🔥18👍41👎1
npmx.dev: a fast, modern browser for the npm registry.

npmx.dev - альтернатива сайту npmjs.com. Как и npmjs, позволяет искать и исследовать пакеты в npm, но также имеет несколько дополнительных фичей: поддержка темной темы, возможность провалиться в исходники пакета, показ используемой модульной системы, показ размера пакета и тд

Выглядит интересно

https://github.com/npmx-dev/npmx.dev

#development #javascript #npm
👍8😁4👎2😱1
Announcing TypeScript 6.0 Beta

Анонсировали бету TypeScript 6.0. Версия, которая должна стать переходной между текущей реализацией TS на JS и будущей реализацией на Go в 7.0. Версия 6.0 будет нести в себе изменения, упрощающие миграцию на будущую 7.0 версию, но также и некоторые полезные изменения.

Первое интересное исправление. Если у вас есть Generic-функция, которая вычисляет аргумент generic'а по переданному аргументу функции внутри, то если функция является стрелочной, то все будет работать корректно, а если это метод объекта - то будет ошибка. Шок контент. Пример ниже
declare function callIt<T>(obj: {
produce: (x: number) => T,
consume: (y: T) => void,
}): void;

// Works, no issues.
callIt({
produce: (x: number) => x * 2,
consume: y => y.toFixed(),
});

callIt({
consume(y) { return y.toFixed(); },
// error: 'y' is of type 'unknown'.

produce(x: number) { return x * 2; },
});


Это связано с тем, как TS обрабатывает this у функций. Хотя ни там, ни там он не используется, во втором случае у функции есть свой this и это как-то влияет на тайп-чекер. Теперь это исправили

Завезли флаг --stableTypeOrdering для упрощения миграции до 7 версии. Этот флаг, как следует из названия, стабилизирует порядок типов в скомпилированных декларациях. Оказывается, есть кейс, когда заведение переменной, которая никак не связана с функцией, меняет порядок типов в декларации функции. Пример

Вот так компилируется, что функция возвращает 100 или 500
// Input: some-file.ts
export function foo(condition: boolean) {
return condition ? 100 : 500;
}

// Output: some-file.d.ts - 100 | 500
export declare function foo(condition: boolean): 100 | 500;



Но если добавить перед этим переменную, то возвращаемое функцией значение начнет компилироваться как 500 или 100
// Input: some-file.ts
const x = 500;
export function foo(condition: boolean) {
return condition ? 100 : 500;
}

// Output: some-file.d.ts - 500 | 100
export declare function foo(condition: boolean): 500 | 100;



Внимание, флаг может замедлить тайп чекинг на 25%.

Также в TypeScript 6 вошли:
- target es2025
- Поддержка Temporal
- Поддержка новых методов в Map: getOrInsert, getOrInsertComputed
- Поддержка RegExp.escape
- lib dom.iterable внесли просто в dom. Теперь достаточно подключить только dom чтобы итерироваться по document.querySelectorAll

Ломающие изменения
На основе анализа конфигов пользователей, часть опций стали дефолтными, часть требует явного указания.
- Необходимо указывать types. По дефолту теперь будет []. Для включения старого поведения можно установить ["*"]
- rootDir по дефолту теперь ..
- strict: true по дефолту
- module: "esnext" по дефолту
- target: "es2025" по дефолту. В целом политика такая, что дефолтным будет считаться текущий стабильный ES.
- noUncheckedSideEffectImports: true по дефолту
- libReplacement: false по дефолту
- target: es5 задепрекейтили. Теперь минимальный таргет - es2015 (более известный как es6)
- --moduleResolution node задепрекейтили. Те кто использовали эту опцию, должны переехать или на nodenext или на bundler
- Удалена поддержка --module amd, umd, systemjs
- baseUrl больше не поддерживается, используйте paths
- --outFile задепрекейтили, используйте бандлеры вместо TS для вывода результата в 1 файл
- Задепрекейтили старый синтаксис для модулей. Это, конечно, может выстрелить т.к. могут быть third-party type declarations, где такой синтаксис используется

// 
module Foo {
export const bar = 10;
}

//
namespace Foo {
export const bar = 10;
}

declare module "some-module" {
export function doSomething(): void;
}


- Также задепрекейтили ассерты импортов в пользу with

//  
import blob from "./blahb.json" asserts { type: "json" }

//
import blob from "./blahb.json" with { type: "json" }


В общем, готовимся к переезду на 7.0. Как минимум морально. Как максимум - заменяем все устаревшее на современное.


https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-beta/

#development #typescript #javascript #breaking
👍18💩21
wc3ui - библиотека компонентов в стиле wc3

Битва UI-китов закончена. Наконец-то нашелся ультимативный ui-kit, предоставляющий компоненты как в warcraft III. Гонка ui-kit'ов потеряла смысл с таким игроком.

Выглядит круто. Даже захотелось внедрить в какой-то пет-проект и снова перепройти кампании wc3. Надеюсь есть в канале люди, у которых сводит олд-скулы от такого.

https://wc3ui.banteg.xyz

#development #javascript #uikit #wc3
🔥29
@github-ui/storybook-addon-performance-panel

Аддон для storybook, который собирает и отображает в интерфейсе storybook метрики производительности компонентов. Новый Must-Have среди сторибучных аддонов для всех проектов. Должен помогать находить критичные проблемы рендера на ранних этапах и в изолированной среде.

https://github.com/github/storybook-addon-performance-panel

#development #javascript #storybook #performance
🔥18
fetch-network-simulator

fetch-network-simulator - библиотека, которая позволяет симулировать проблемы с сетью при работе с fetch.

Что можно делать:
- Повышать задержку
- Терять пакеты
- Автоматические ретраи
- Ответы приходят в порядке, отличном от порядка запросов (полезно для вскрытия кейсов, когда приложение ожидает, что при отправке двух запросов А и Б, ответы точно придут в том же порядке)
- Ограничение на параллельность

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

Настройка библиотеки выглядит так
import { enableNetworkSimulator } from "fetch-network-simulator";

if (process.env.NODE_ENV === "development") {
enableNetworkSimulator({
debug: true, // optional: structured request lifecycle logs

latency: { enabled: true, delayMs: 1500 },
packetLoss: { enabled: true, lossRate: 0.3 },
retry: { enabled: true, maxAttempts: 3, retryDelayMs: 200 },
staleResponse: { enabled: true, staleProbability: 0.5 },
burstControl: { enabled: true, maxConcurrent: 1 },
networkSpeed: { enabled: true, kbps: 500 }
});
}


https://github.com/thisiskps/fetch-network-simulator

#development #javascript #fetch #performance
🔥21👍5
Electrobun v1

Год назад я в канале писал, что анонсировали разработку Electrobun - это Electron, но поверх bun. И вот теперь вышел Electrobun v1

Какие изменения:
1. Автор уже завез один свой проект на Electrobun. Получается уже можно сделать что-то рабочее
2. Поддержка macos, windows, ubuntu
3. Своя реализация OOPIF (Out Of Process IFrame) для вставки webview. Про создание своего OOPIF есть отдельная статья

https://blackboard.sh/blog/electrobun-v1/

#development #javascript #bun #electron #electrobun
😱2👎1😁1
Fastest Frontend Tooling for Humans & AI

Статья про замену инструментов вокруг разработки на более быстрые. Во-первых, это полезно разработчикам. А во-вторых, это ускоряет работу ИИ-агентов.

Что предлагает автор:
Первое - уже переехать на typescript, написанный на go (tsgo). Сам автор уже полгода сидит на tsgo и очень доволен т.к. tsgo в 10 раз быстрее. Также говорит, что tsgo находит ошибки, которые не находит обычный tsc

Миграция:
- npm install @typescript/native-preview
- Удалить легаси-флаги конфига TS
- Заменить все вызовы tsc на tsgo
- Добавить "typescript.experimental.useTsgo": true в конфиг vscode

Второе - переехать на oxfmt вместо prettier. Oxfmt это более быстрая реализация prettier с кучей встроенных плагинов. Также oxfmt фолбечится до prettier для обработки файлов не на JS/TS.

Миграция через ИИ-агента:
> Migrate this project from Prettier to Oxfmt. Read https://oxc.rs/docs/guide/usage/formatter/migrate-from-prettier.md. Update all scripts, tools, and hooks to use Oxfmt. Remove all Prettier configuration files and reformat the code using Oxfmt.

Также полезным будет установить расширение для vscode
code --install-extension oxc.oxc-vscode.

Третье - переехать на oxlint вместо eslint. Oxlint умеет запускать eslint плагины внутри себя, что позволяет переехать на более быстрый линтер, не потеряв при этом богатую экосистему плагинов

Миграция также через ИИ-агента
> Migrate this project from ESLint to Oxlint. Read https://oxc.rs/docs/guide/usage/linter/migrate-from-eslint.md. Update all scripts, tools, and hooks to use Oxlint. Remove all ESLint configuration files. Lint the code and fix any lint errors.

Также автор собрал свой конфиг для линтера, который вы можете поставить. Сам конфиг не интересен, но интересны принципы, которые лежат в основе конфига:
1. Только ошибки, никаких предупреждений.
2. Строгий, консистентный код-стайл.
3. Предотвращение багов (запрет любых конструкций, которые могут к ним привести)
4. Только быстрые правила. Медленные отключаются
5. Субъективные правила отключены (например, влияющие на стиль написания кода)

Эти правила уменьшают двусмысленность, что полезно при ИИ-кодинге.

Также из быстрого инструментария:
1. npm-run-all2 - команда для запуска других команд в параллель
2. ts-node + swc

Звучит интересно. Кто-то уже пробовал tsgo и oxlint?

https://cpojer.net/posts/fastest-frontend-tooling

#development #javascript #eslint #typescript #oxlint #prettier
13👍7🔥2
Can't Maintain. Can you spot the better API?

"Не поддерживаемый" (Can't maintain) - сайт с короткими квизами, где надо выбрать более поддерживаемый вариант реализации JS или React-кода. При ответе краткий экскурс, почему один из вариантов более поддерживаемый, чем другой. Очень залипательный сайт.

https://cant-maintain.saschb2b.com

#development #javascript #quiz #react
🔥164
Why is WebAssembly a second-class language on the web?

WebAssembly развивается с 2017 года, но все еще не стал полноценной частью веб-экосистемы. В статье Ryan Hunt объясняет, какие проблемы есть у современного WASM и что надо изменить, чтобы wasm стал популярен у разработчиков

Есть 2 основные проблемы, делающие WASM "неудобным":
1. Код из WASM не может напрямую работать с Web API. Для этого нужна JS прослойка, которая будет мостиком между WASM и Web API
2. WASM код нельзя загрузить никак, кроме как из JS-кода (что является следствием первой проблемы)

Вторую проблему уже решают в пропозале esm-integration. Он позволяет загружать wasm-модули как обычные js-модули

import { run } from "/module.wasm";

run();



<script type="module" src="/module.wasm"></script>



Но необходимость прослойки для работы с Web API все еще делает неудобной работу с Web API. Можно писать эту прослойку самому, но это а) не самая тривиальная работа; б) требует одновременно компетенций как в wasm, так и в web. Можно использовать embind или wasm-bindgen, но там тоже есть свои нюансы

Компиляторы, собирающие нативный код в WASM, не занимаются сборкой JS-кода для интеграции wasm в web-api - они лишь собирают wasm, а дальше разработчик сам должен написать мостик между WASM и Web API, если это требуется для приложения.

Также есть проблема, что даже если написать такой мостик, то от этого пострадает перформанс. В классическом бенчмарке TodoMVC wasm+JS оказался почти в 2 раза медленнее, чем wasm-код с доступом к web api.

Как решение этой проблемы предлагается WebAssembly Component - пропозал, разрабатываемый с 2021 года

WebAssembly Components:
- Могут быть созданы из разных языков программирования
- Могут быть запущены в разных рантаймах
- Могут использовать друг друга
- Могут напрямую вызывать Web API

Это позволит снизить порог входа для интеграции WASM в приложения.

https://hacks.mozilla.org/2026/02/making-webassembly-a-first-class-language-on-the-web/

#development #javascript #web #wasm
👍141
How we Rewrote 130K Lines from React to Svelte in Two Weeks

Команда strawberry за 2 недели переписала 130к строчек кода с React на Svelte с применением ИИ-агентов. Интересный кейс, про который можно и почитать. Хотя, конечно, тот факт, что ИИ-агенты хорошо справляются с переписыванием - давно не новость

Почему вообще понадобилось переезжать с React:
1. Strawberry использует несколько отдельный рендереров для разных компонентов своего браузера. Каждый компонент - отдельное React-приложение. Обновление этих приложений из главного процесса приводит к плохому UX.
2. Strawberry - это ИИ-стартап, знаете ли. ИИ-агенты, которые работают как ядро приложения, постоянно вызывают ререндер React-приложений.

В общем, React быстрый, но недостаточно быстрый для чуваков из Strawberry (хотя конечно больше похоже на то, что они просто решили не париться с оптимизацией ререндеров)

Отдельно забавляет подход, с которым начался процесс переписывания. Какое-то обсуждение через ADR, RFC или хотя бы тред - это все лишнее. Чел просто на один из дейликов пришел и сказал "смотрите, переписал уже 60% кодовой базы на Svelte". Живем в эру, где быстрее показать Proof of Concept, чем начать обсуждение какой-то инициативы.

Как состоялось переписывание:
1. Были оформлены правила миграции, чтобы гарантировать, что ИИ-агенты пишут код "правильно". Короткие примеры правил: "Avoid $effect", "Prefer stores over component state", "Keep reactivity explicit."
2. За 1 раз переписывается одна фича
3. ИИ-агент читает правила, портирует код, тестирует код.
4. Старый код не удалялся, пока не была завершена миграция (чтобы при любых проблемах можно было поднять эталонные исходники и сравнить решение)
5. Часть библиотек отсутствует на svelte, поэтому пришлось их написать самим (через агентов конечно)

Какие результаты:
1. х2 ускорение
2. FCP уменьшился с 300мс до 124мс
3. Количество ререндеров в приложении уменьшилось в 10 раз
4. Удалили 56 неиспользуемых зависимостей


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



https://strawberrybrowser.com/blog/react-to-svelte

#development #javascript #react #svelte #migration #refactoring #ai
😁8💩6👍2
Reveal.js 6.0

Вышел новый релиз Reveal.js. Reveal.js — это инструментарий для создания веб-презентаций из кода. Основное важное изменение для релиза — нативная поддержка React. Теперь можно собирать интерактивные презентации на привычном стеке.

Я даже уже попробовал использовать. Может показаться смешным, но у нас в направлении есть своя презентация для фит-интервью. К нам приходят кандидаты, мы спрашиваем их про их опыт и всякое разное, а потом рассказываем про нас через презу. Но преза сделана на PowerPoint и не обновляется годами, потому что никто особо не хочет лезть править презентацию.

В общем, решил совместить приятное с полезным и отправил агента сначала мигрировать презентацию с pptx на reveal. Агент нашёл какой-то пакет для парсинга слайдов на Python и собрал базу для презентации. Результат простой миграции выглядит, конечно, достаточно убого.

Дальше пошли сплошные плюсы. Заставил агента через Chrome MCP провести ревью слайдов и исправить явные косяки. С этой задачей он справился: где-то выровнял, где-то увеличил, а где-то распределил контент по слайду.

Дальше пошёл самый кайф. Т. к. на слайдах были картинки, а тут у нас целый React, то почему бы не добавить интерактива? Описал, какой интерактив я ожидаю на слайдах, и вообще попросил все картинки из интернета заменить на что-то более живое.

Результат следующий: половина коллег сказали «вау, круто», другая половина оценила как «юзлес кринж». Считаю, главное, что мне нравится :)

Но как итог:
- Reveal.js норм ложится для создания интерактивных презентаций на React. Слайды с демками, кликабельные блоки, кастомные анимации любой сложности — всё можно сделать в Reveal.js.
- ИИ-агент с доступом к Chrome MCP вполне бодро может собрать презентацию сам по вашему контенту и с вашим видением.


https://github.com/hakimel/reveal.js/releases/tag/6.0.0

#development #javascript #react #revealjs #presentation #release #ai #agents
👍4🔥1
Next.js 16.2

Вышел релиз Next.js 16.2. Кратные ускорения (400% ускорение старта при разработке и 50% ускорение рендеринга), правки в Turbopack и адаптация фреймворка для AI. Ещё новая дефолтная страница для 500 ошибки.

Что прикольного завезли:

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

Удобное отображение диффа между серверной и браузерной разметкой, когда разъехалась гидрация. За это однозначный лайк. Также более удобное отображение ошибок в dev overlay.

Я бы не писал в канал, если бы не отдельные телодвижения в релизе для ИИ-агентов.

Что конкретно сделали:

Теперь при создании нового приложения через create-next-app создаётся Agents.md. В файле написано, что это Next.js репозиторий и необходимо смотреть доку в node_modules/next/dist/docs. Это сделано потому что, по исследованию, наличие доки по фреймворку рядом с кодом позволяет агентам достигать лучших результатов в бенчмарках.

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

При старте сервера Next.js пишет в lock-файл PID процесса. Если попытаться стартануть новый сервер, то Next.js не позволяет это сделать и говорит, что сервер уже запущен. Это также полезно для агентов, которые любят запустить приложение с десяток раз.

Сделали DevTools для агентов. Можно получать инфу из DevTools через CLI. В целом, как будто ИИ-агенты и так могут это делать через MCP или прямое или почти прямое использование CDP через CLI, но встроенная в фреймворк возможность так делать, конечно, удобнее для массового использования.

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


https://nextjs.org/blog/next-16-2

#development #javascript #react #nextjs #turbopack #release #ai #agents #devtools
13👍3
Start naming your useEffect functions, you will thank me later

Огромная статья, в которой очень простой месседж — именуй чёртовы колбэки в useEffect.

В чём суть. Есть две большие причины именовать колбэки в useEffect.

Первая: самодокументирующийся код. useEffect(() => {}) говорит о том, как мы запускаем код (при изменениях чего-то), но не говорит о том, что там запускается и почему.

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

Намного лучше, если разработчик сразу даст имя колбэку в useEffect, которое позволит быстро понять, что тут происходит. Если компонент простой и useEffect тоже простой, то проблема незаметна. Но если в компоненте есть 5–6 useEffect, то понять, зачем они нужны, может быть не так-то просто.

Автор даёт в статье пример компонента, который общается по WebSocket и синхронизирует состояние сущности.

Пример useEffect с именованным колбэком:

useEffect(function resetStockOnLocationChange() {
if (prevLocationId.current !== locationId) {
setStock([]);
prevLocationId.current = locationId;
}
}, [locationId]);


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

Вторая причина для именования колбэков чисто прагматичная. Лучше в инструментарии и в стек-трейсах видеть at resetStockOnLocationChange, чем at anonymous.

В общем, именуйте колбэки в useEffect. Это бесплатно и сразу делает вашу жизнь лучше. Можно ещё написать или найти ESLint-правило, которое не даст коммитить анонимные useEffect.


https://neciudan.dev/name-your-effects

#development #javascript #react #hooks #useeffect #best-practices #article
👍23💩32