Сова пишет…
3.13K subscribers
345 photos
37 videos
5 files
417 links
Frontend Senior Fullstack Backend Lead и прочие слова.
Изучаю самые современные технологии.
Обучаю разработчиков как стать сильнее — https://frontend.vision.

По коллаборациям и сотрудничеству пишите в сообщения канала!
Download Telegram
После рабочего дня, обязательно нужен отдых, нельзя переходить от одной работы к другой. Мозги не изнашиваются как детали двигателя, они перестают работать эффективно, нужен отдых и разрядка напряжения.

Избавьтесь от свободного времени. Это главный враг эффективности. Запланируйте свой день, впишите всё, что нужно, включая отдых и развлечения. Конечно, корректировать план никто не запрещает, но только в пользу своему здоровью и поставленым целям.

Я начинаю каждое утро с планирования дня, оценки планов на неделю, месяц и год. Заканчиваю день, легкой ретроспективой, что успел, а что нет и почему. Очень хочу начать читать книги регулярно, но пока не решил когда это делать лучше всего. На самом деле, я ещё не выработал в себе дисциплину полностью соблюдать план на день. Возможно, я слишком много планирую и мне нужно научиться выполнять 100% поставленных задач, пусть их будет 3. А уже после переходить к большему количеству.

Спасибо, что читаете!
Я записал тестовую дорожку.
Послушайте и напишите комментарий с пожеланиями, предложениями или замечаниями: https://podcast.sova.dev/
Apple опубликовала подкаст Сова говорит. Готовлю выпуск. Напишите в комментариях на каких платформах вам было бы удобно слушать.
Уже несколько дней подряд думаю о нейронках и ДНК. Не могу написать ни строчки кода или сценария...

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

Натренировать на этой базе нейронные сети распознавать каждый пункт анкеты. Сложно даже представить какие результаты будут. Ведь нейронка не определяет факты, она лишь найдет корреляции. Например, схожесть ДНК курильщиков. А может это не схожесть и действительно заложено в нашей наследственности?
Немного обсуждений в @effectorjs чате натолкнули меня на мысль изменить подход к описанию логики в effector+react.

Я стараюсь разделять представление и логику. Если взять в пример страницы, рядом с файлом компонента страницы лежал файл логики на эффекторе.
pages/counter/{index.tsx, model.ts}

В файле компонента я напрямую импортировал сторы и ивенты из модели и использовал в компонентах:

import * as React from ‘react’
import { useStore } from ‘effector-react’
import { $counter, incrementClicked, pageMounted } from ‘./model’

export const CounterPage = () => {
const counter = useStore($counter)
React.useEffect(() => { pageMounted() }, [])

// show counter and use events
}


И это работало весьма хорошо, тестировать просто (импортируем сторы в тест и setState(data)). Объявляем контрактом любые экспорты из model.ts и очень осторожно их изменяем.

View-слой (компонент) может независимо меняться от бизнес-логики, и логика может переписываться, если не меняется контракт(экспортируемые сущности).

Но если нужно маппить данные из стора по какому-то ключу или ещё какие операции со списками, то эта логика затаскивалась прям в компонент вместе с useStoreMap и useList. Это мне не очень нравится, ибо тестировать становится сложнее.

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

Использовать хуки, для получения данных и ивентов из модели.
Я предлагаю из модели экспортировать реакт-хуки, вместо сущностей и ивентов. Этот подход имеет смысл в первую очередь для моделей страниц. Разделяемые сущности чаще всего не нужно отдельно заворачивать в хуки, так как часто хочется создавать computed от общих сторов.

Как это выглядит и что это дает: модель экспортирует хуки, которые возвращают наборы данных и ивенты. В этих хуках могут быть любые необходимые вычисления.

import { useCounter, useEvents } from ‘./model’

export const CounterPage = () => {
const counter = useCounter()
const { pageMounted, incrementClicked } = useEvents()

React.useEffect(() => { pageMounted() }, [])

// show counter and use events
}

Можно пойти дальше, и унести React.useEffect() внутрь useEvents, чтобы модель сама устанавливала нужные реакции. Но тут спорно и нужно обдумать. Возможно это нужно выделить в отдельный хук.

А теперь зачем это.
1. Семантика. Теперь, контракт выглядит как независимая сущность. Можем заменять STM как нам захочется, внутри хука может быть React.useReducer, или же Redux.useSelector, или же Effector.useStore. Все равно.
2. Сокрытие сложности. Всякие useStoreMap могут быть скрыты внутри хука, разработчику компонента теперь не нужно знать детали реализации модели, чтобы разработать компонент. Ведь теперь, чтобы рабоать независимо достаточно спроектировать контракт на хуках, описать типы и вернуть dummy-данные.
3. Тестирование. Тесты упрощаются, до мока конкретных хуков, а не сторов. Ведь теперь разработчик компонента, может спокойно написать тесты только на вью, не трогая при этом сторы модели. Логика моделей при этом может быть разработана действительно независимо.

Я пока не могу понять, имеет ли смысл разделять контракт и модель? pages/counter/{contract.ts, model.ts, index.tsx}
- contract — импорты из model завернутые в react-хуки
- model — чистая логика, без примесей хуков
- index — компонент, использующий контракт, без примесей STM

Как думаете?
Завтра в 18:00 начинается митап.

Приезжайте на м. Петроградская, зал Леонардо находится в пространстве Точка Кипения, которая возле клуба A2.

За несколько часов до начала, я отправлю всем на почту подробный план, как пройти, а также напишу его здесь. Примерно в 19:00-19:30 приедет пицца, сможем перекусить перед продолжением.

На всякий случай напоминаю почему Effector вообще появился и какие у него плюсы:

— децентрализованность, декларативность, эффективность. требовался инструмент, позволяющий управлять данными в сложных приложениях без опасности раздуть монолитный центральный стор, с явным control flow, нормальной типизацией и емким апи
— сторы для приложения должны быть лёгкими, насколько это возможно — не должна пугать мысль о том, что нужно добавить ещё один стор для конкретных нужд
— сторы должны свободно совмещаться — идея в том, что данные, которые потребуются приложению, можно распределить статически, заранее показав как данные будут преобразоваться во время работы приложения
— принцип работы должен by design исключать необходимость в reselect, оповещая об изменениях только тех, кому они необходимы. это позволяет не задумываться о том, что у тебя будет триггериться всё приложение если ты захочешь вынести стейт модалки из реакта. по совместительству это означает что приложения избавлены от проблем с перфомансом, возникшим у react-redux при переходе на контекст
— возможность, место, и способ вынести любую требуемую бизнес-логику из view, максимально упрощая компоненты
— независимость от спорных концепций — никаких декораторов, никаких зависимостей от реакта/rxjs либо необходимости юзать классы или прокси — ничего из этого не требуется для управления состоянием приложения и поэтому апи библиотеки использует только функции и простые js объекты
— предсказуемость апи. небольшое число базовых принципов переиспользуются в различных кейсах, снижая нагрузку на юзера и повышая узнаваемость. зная как работает .watch в эвентах, можно догадаться, что делает функция .watch у стора
— приложение строится из комбинации базовых элементов и возможности строить новые.
нет никакого смысла стремиться выдать всё за стрим, за редьюсер или за обсервабл, в приложении требуются они все, и библиотека предлагает решение чтобы управлять структурой данных, а не скрывать её
Ребята, буду благодарен, если напишете отзывы в комментарии. Можно писать все замечания по организации митапа и трансляции. А также по всем докладам. Нам это поможет сделать следующие митапы лучше
Привет. По просьбе нашего техдира я начал описывать процесс разработки фронта. Документ будет полезен как внутри компании, так и снаружи.
На данный момент я набросал простенькую структуру. Мне интересно мнение опытных людей, взгляд со стороны.

1. Сбор требований к проекту (что входит в ближайший релиз и в следующий)
2. Макетирование (дизайнеры собирают карту взаимодействий)
3. Нарезка на библиотеку компонентов (на карте взаимодействий)
4. Определение зависимостей компонентов для всех экранов
5. Выделение паттернов дизайна (общая разметка, повторяющиеся блоки, тянущаяся верстка, адаптивная верстка)
6. Первая реализация библиотеки компонентов (карта состояний в storybook, поверхностное unit-тестирование)
7. Определение необходимых данных для каждого экрана
8. Определение требований к бекенду (методы API)
9. Первичная сборка 1-3 разных экранов по внешнему виду
10. Составление диаграммы процессов приложения (1-3 сценария по выбранным экранам)
11. Имплементация выбранных сценариев предметной области (на языке программирования)
12. Прототипирование экранов с привязкой к сценариям (моки API, graybox реализация без глубокой обработки ошибок)
13. Ретроспектива прототипа (анализ сценариев, анализ прототипов, анализ ошибок, анализ библиотеки компонентов и паттернов)
14. Составление карты сущностей и отношений
15. Составление карты ролей, политик и разрешений (RBAC / ABAC / ACL / ...)
16. Отображение карты взаимодействий на карту роутов (доступ, параметры, переходы)
17. Последовательная имплементация основных сущностей, роутов и прав доступа (тестирование)
18. Параллельная имплементация сценариев, сгруппированных по области ответственности (тестирование)
19. Ретроспектива релиза (список неудачных решений, список полностью решенных задач, список узких мест производительности и архитектуры, список удачных архитектурных решений, что можно улучшить перед реализацией следующего релиза)
Немного запоздалых новостей:

- На GitHub появилась коллекция JavaScript State Management Tools, в которой на первом месте Effector
- GitHub наконец-то одобрил добавление топика effector
- Я сделал шаблон Effector SSR, для быстрого старта разработки на TypeScript, React, Effector, Razzle, StyledComponents
- Часто вижу вопросы, как сделать debounce на Effector, и поэтому сделал библиотеку для этого 👀. Вообще, это больше в качестве примера написания библиотек для эффектора. В репозитории есть тесты на сам debounce, а также на корректность работы в fork.
- Я продолжаю писать генератор кода из Swagger/OpenAPI в ActixWeb
Оказалось, что я переношу несколько функций между проектами на Effector.

Не так давно я вынес debounce, throttle и reshape в отдельные пакеты. И вот, на работе родилась функция splitMap, а за ним затащил и spread.
Начав новый проект, я осознал, насколько мне стало лениво устанавливать и обновлять столько модулей.

Представляю сборник effector функций — Patronum.

github.com/sergeysova/patronum

Сейчас реализовано 5 функции помеченные 🧁: debounce, throttle, delay, condition, reshape. Всем желающим предлагаю закидывать в issues или PullRequests свои частоиспользуемые функции для effector.
Написал значит я тесты для patronum/delay. Хочу убедиться, что delay действительно корректный, мол на правильное время откладывает. Прогнал тесты на своем маке и запушил релиз.
И тут всё сломалось. https://github.com/sergeysova/patronum/runs/619547616?check_suite_focus=true

Тесты на ubuntu кричат:
expect(start.diff() > 100).toBe(true)
Время delay было меньше 100 миллисекунд.

Думал, я думал. Логгировал, логгировал, да не перевылоггировал.
Оказалось, что в некоторых случаях setTimeout срабатывает раньше указанного срока, хотя я всегда предполагал, что позже.

Как же тогда писать тесты красиво и удобно. Да так, чтобы время было корректным? Всего лишь нужно ввести так называемую проверку с порогом:
if (received < expected + threshold && received > expected - threshold)

И благо, jest позволяет добавить кастомные матчеры, чтобы такие проверки не дублировать в каждом тесте. Результат выглядит так:

expect(start.diff()).toBeCloseWithThreshold(100, 30)
— Жест, будь так добр, проверь, что полученное значение, находится в районе 100 миллисекунд, плюс/минус 30мс.

Реализацию можно поглядеть здесь:
https://github.com/sergeysova/patronum/commit/1aa43c48d81b54b8a85099d889e49604a0148ae7
Тем временем я расширил реализацию spread от @ZeroBias, написал тесты и добавил документацию.

https://github.com/sergeysova/patronum/tree/master/spread

spread(source, {
first: $targetFirst,
second: targetSecond,
third: targetThirdFx,
})


source({ first: "Hello", second: 123, third: true })

Если в source передан не объект, то падения в рантайме не будет.
Spread будет вызывать только те таргеты, для которых нашлись ключи. Например:

source({ first: "Hello" })
Здесь будет вызван только $targetFirst
Монтирую выпуск о моём опыте проработки продукта. Надеюсь сегодня будет готов и смогу выложить.
Первый выпуск подкаста «Сова говорит...» о проработке продукта.


https://podcast.sova.dev/episodes/1-0-product-development
Как же меня сейчас разбомбило!

Итак, почему CRA это отвратительная штука:
- невозможно просто так подключить babel плагины
- заставляет юзать свой eslint-config-react-app, и env ESLINT_EXTEND
- вокруг него есть десятки расширителей rewired, customize-cra, craco…. и всё это работает не очень хорошо и имеет проблемы с обновлениями CRA
- он насильно исправляет tsconfig при запуске (WTF?)
- подключить postcss плагин уже проблема
- env переменные обязаны начинаться с REACT_APP_

Я думаю вы сможете накидать ещё десятки комментариев о проблемах с CRA. Но у меня весь процесс “БЫСТРОГО” старта выглядит так:
1. yarn create react-app my-app
2. исправить все косяки CRA (3-4 коммита)
3. писать немного код
4. понять, что нехватает расширения
5. пытаться жить с craco/rewired+customize-cra
6. получить какой-то баг при обновлении
7. сделать eject и исправить всё что нужно руками
8. переписать все конфиги

Итого, просто на мучения с CRA в сумме уходит несколько рабочих дней и 2000±500 строк изменений. Это неправильно. Такую систему невозможно называть УДОБНОЙ или УПРОЩАЮЩЕЙ. Оно даже для старта ничего не упрощает.

Зачем было навязывать свои еслинт плагины?
В чем проблема сделать как Razzle? Он решает одну задачу — конфигурирует webpack для SSR, и делает её очень хорошо. При этом позволяя легко кастомизировать и расширять любую часть, что бабель, что вебпак, что энвы, что еслинт.
Но авторы CRA это дичайшие вахтеры, навязали десятки спорных решений, и называют это хорошим решением для новичка. Притом новичок не может использовать инструменты вроде eslint и postcss по документации этих инструментов, CRA ломает использование инструментов.

Так не должно быть. Я собираю голоса на создание нормального инструмента для бутстрапинга react приложений. Пишите в комментариях всё, что думаете про CRA и его проблемы. Какие плюсы лично вам нравятся в CRA и их нужно перенести в адекватную реализацию?
Forwarded from Монада Кедавра (🚀🔬 🚀🔬🚀🔬)
обзор производительности веб-фреймворков в реальных условиях.

исследование показателей react, vue и angular для множества реальных приложений по ряду метрик:
размер js файлов (angular позади всех)
время, потраченное на выполнение жс кода (в отстающих, как ни странно, react, то есть он способствует написанию компактного, но тяжёлого для выполнения кода)
разрыв в производительности между мобильными устройствами и десктопами

бонусом указана степень популярности фреймворков — от пятисот тысяч сайтов на react до скромных двадцати тысяч у angular

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

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

потому что производительность — это спектр значений

https://timkadlec.com/remembers/2020-04-21-the-cost-of-javascript-frameworks/
Несколько интересных статей про Rust:

Что делает Rust универсальным языком — https://habr.com/ru/post/504622/

Передача намерений — https://habr.com/ru/post/326896/

Парсите, а не валидируйте — https://habr.com/ru/post/498042/
ESLint Overrides

Заметил, что часто требуется настроить ESLint для разных директорий по разному. Иногда, вообще нужно выключить некоторые правила в отдельных директориях (например, tests).

Не так давно в eslint config завезли параметр overrides. Позволяет переопределить правила для определенных файлов или директорий.
Выглядит так:

{
overrides: [
{
files: [“PATTERN”, “ANOTHER”],

excludedFiles: “EXCLUDE”,
...CONFIG,
}
]
}


- Вместо PATTERN и ANOTHER можно вписывать relative glob patterns выбирающие файлы, к которым применяются правила описанные в CONFIG.
- CONFIG это буквально такой же конфиг как и обычный eslint конфиг.
- excludedFiles опциональный параметр, позволяет выкинуть некоторые файлы из выбранных раннее.

Примеры

Включить JEST окружение для тестов:
{
files: [“**/*.test.ts”, “**/*.test.tsx”, “tests/**.ts”, “tests/**.tsx”],
env: { jest: true },
}


Включить плагин и конфиг для моделей:
{
files: [“**/model.ts”],
plugins: [“@company-name/eslint-plugin-model”],
extends: [
“plugin:@company-name/eslint-plugin-model/recommended”,
],
}


Документация

Пример конфига из моего проекта