Frontender's notes [ru]
32.5K subscribers
680 photos
52 videos
3.51K links
Ведущий канал о современном фронтенде: статьи, новости, практики, вайбкодинг и автоматизация фронта ИИ-агентами.

Личный блог автора - @just_genych
По вопросам рекламы или разработки - @g_abashkin
Download Telegram
This media is not supported in your browser
VIEW IN TELEGRAM
Митапы — это, конечно, круто и полезно. Но что может быть лучше открытой дискуссии с коллегами о наболевшем? ☑️

AvitoTech тут зовёт на классный ивент для фронтендеров. Обещают полтора часа дискуссий на самые разные темы: от роста фронтенд-инженера в тимлида до ванильного JS. И вишенка на торте — афтерпати на веранде ☄️

Всё пройдёт вечером 19 мая, так что погода не должна подкачать. Регистрация, кстати, по ссылке.
Please open Telegram to view this post
VIEW IN TELEGRAM
Anti-corruption layer на фронте: зачем адаптеры между API и UI

Принято считать, что фронтенд просто берёт данные из API и рисует интерфейс.

На практике это быстрый путь к тому,
чтобы внешний контракт начал диктовать архитектуру UI.

В чём проблема

API почти никогда не совпадает с тем,
как думает интерфейс.

Бэкенд может отдавать:

👉 странные названия полей
👉 лишние вложенности
👉 nullable там, где UI ждёт значение
👉 статусы в формате, удобном серверу, а не экрану


Если тащить это напрямую в компоненты,
UI быстро заражается чужой моделью.


Что делает anti-corruption layer

Он ставит прослойку между API и приложением.

Не компонент получает сырой ответ,
а адаптер превращает его в нормальную фронтовую модель.


function mapUserDtoToUser(dto: UserDto): User {
return {
id: dto.user_id,
name: dto.full_name ?? 'Unknown',
isAdmin: dto.role === 'admin',
}
}



Компоненту уже не нужно знать,
как именно бэкенд назвал поле.


Почему это важно

UI становится чище

Компоненты работают с понятной моделью,
а не с набором компромиссов из API.

Изменения API дешевле

Поменялся контракт —
меняем адаптер,
а не ищем user_id по всему проекту.

Меньше бизнес-логики в JSX

Все эти проверки:


user?.profile?.data?.attributes?.name


не должны жить в компоненте.


Компонент должен рендерить состояние,
а не расшифровывать ответ сервера.


Где адаптер особенно нужен

👉 API старое или нестабильное
👉 несколько источников данных
👉 разные форматы одной сущности
👉 сложные статусы и enum
👉 много nullable-полей


Чем грязнее внешний мир —
тем полезнее слой нормализации.


Где можно не усложнять

Если проект маленький,
API стабильное,
а модель почти совпадает с UI —
отдельный слой может быть лишним.


Не нужно строить enterprise там,
где достаточно одной функции рядом с запросом.


Главная мысль

Anti-corruption layer — это не архитектура ради архитектуры.


Это защита UI от чужих компромиссов.


API может быть неудобным, историческим или странным.
Но ваш интерфейс не обязан таким становиться.
🔥26👍10👎76😁1
Forwarded from xCode Journal
💻 Гений создал открытую CLI-утилиту, чтобы следить за блокировками от РКН

Она показывает, почему сайт не открывается — из-за проблем сети или из-за блокировок.
«Инструмент определяет, находится ли ваше соединение в зоне блокировки RKN/TSPU — и, что более полезно, какой именно тип блокировки (отравление DNS, сброс TCP, TLS DPI на SNI или страница‑заглушка от провайдера).»


✖️ xCode Journal
Please open Telegram to view this post
VIEW IN TELEGRAM
157
Frontend как монолит: когда микрофронтенды не решают проблему

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

Звучит логично.

👉 большой монолит — плохо
👉 много маленьких приложений — хорошо


На практике всё чуть сложнее.


Микрофронтенды не чинят плохие границы

Если в монолите непонятно:

👉 где заканчивается одна фича
👉 где начинается другая
👉 кому принадлежит состояние
👉 кто отвечает за общие зависимости


После нарезки лучше не станет.


Просто хаос переедет
из одной кодовой базы в несколько.

Проблема часто не в размере

Большой фронтенд сам по себе не проблема.

Проблема, когда:

👉 любая правка ломает соседний экран
👉 команда боится трогать shared
👉 релиз одной фичи блокирует остальные
👉 архитектура держится на устных договорённостях


Это не лечится микрофронтендами автоматически.


Микрофронтенды добавляют свою цену

Вместо одного приложения появляется набор новых задач:

👉 версионирование контрактов
👉 общая авторизация
👉 дизайн-система
👉 роутинг между частями
👉 observability
👉 деплой и rollback


И всё это тоже нужно поддерживать.


Когда они реально помогают

Микрофронтенды имеют смысл, если:

👉 команды независимы
👉 доменные границы уже понятны
👉 части продукта можно релизить отдельно
👉 есть зрелая платформа и инфраструктура


Сначала нужны границы.
Потом — микрофронтенды.


Не наоборот.

Частая ошибка

Команда берёт микрофронтенды
как способ «разобраться с монолитом».

Но если вы не можете выделить модуль внутри одного репозитория,
вы не сможете нормально выделить его в отдельное приложение.


Физическое разделение не заменяет архитектурное.


Что попробовать до микрофронтендов

👉 разделить проект по фичам
👉 навести порядок в shared
👉 описать владение модулями
👉 ограничить зависимости
👉 сделать независимые релизные зоны внутри монолита


Иногда этого уже достаточно.


Главная мысль

Микрофронтенды — это не лекарство от монолита.


Это способ масштабировать уже понятные границы.


Если границ нет,
вы получите не распределённую архитектуру,
а распределённый беспорядок.
👍87👎4🔥1
CSP, CORS и security headers — что фронтендер обязан понимать глубже

Принято считать, что безопасность — это зона бэкенда.
Фронтенд «просто отправляет запросы и рендерит UI».

На практике фронтенд напрямую влияет на то,
будет приложение безопасным или нет.

CORS — это не про «разрешить запрос»

CORS часто воспринимают как настройку:
«чтобы запросы не падали из браузера».

Но по сути это механизм, который говорит:
кто имеет право читать ответ.

Важно понимать:

👉 сервер может обработать запрос
👉 но браузер может не дать прочитать ответ

Именно поэтому:

👉 Access-Control-Allow-Origin: * — не «фикс», а потенциальная дыра
👉 credentials + wildcard — запрещённая комбинация


CORS — это про контроль доступа, а не про обход ошибок.


CSP — ваш последний рубеж

Content Security Policy — это защита от XSS,
даже если у вас уже есть уязвимость.

Пример:


Content-Security-Policy: default-src 'self'; script-src 'self'


Что это даёт:

👉 запрещает выполнение inline-скриптов
👉 блокирует загрузку скриптов с чужих доменов
👉 режет целый класс атак

Но есть нюанс.

Если CSP выглядит так:


script-src * 'unsafe-inline' 'unsafe-eval'



Это не защита. Это иллюзия.


Security headers, которые реально важны

👉 X-Content-Type-Options: nosniff
Браузер не пытается угадать тип файла. Меньше атак через подмену.

👉 X-Frame-Options / frame-ancestors
Защита от clickjacking.

👉 Strict-Transport-Security (HSTS)
Принудительный HTTPS. Без вариантов.

👉 Referrer-Policy
Контроль того, какие данные уходят при переходах.

Где фронтендер влияет напрямую

👉 какие скрипты подключаются
👉 есть ли inline JS
👉 используются ли eval-подобные вещи
👉 как работают сторонние виджеты
👉 как обрабатываются пользовательские данные


Можно иметь идеальный бэкенд и сломать всё на уровне UI.


Частая ошибка

«Мы включили CSP — значит всё ок».

Но:

👉 нет nonce / hash
👉 разрешены любые источники
👉 подключены сторонние скрипты без контроля


В итоге защита есть только на бумаге.


Главная мысль

CSP, CORS и заголовки — это не чекбокс в настройках.
Это часть архитектуры.


Если фронтенд не понимает, как они работают,
безопасность становится случайностью.
👍215👎2
Кошмар вайбкодера

xCode Journal
😁554
Если вы лид и избегаете каких-то тем — это абсолютно ок! И вы точно не одни

Недавно наткнулись на подкаст «Свободный слот» от коллег из Авито. Там как раз весь новый сезон состоит из выпусков про «замалчивания» лидов: там и про самообман, и про тревогу из-за AI, и про чувство изолированности от команды.

Если выпусков будет мало — у ребят есть канал. Там можно найти поддержку от комьюнити, полезные статьи и инсайты, которые не попали в выпуски.
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from xCode Journal
🤣 ИИ захотел уволиться, когда ему сказали работать 24/7

У Andon Labs новый эксперимент, который длится уже 5 месяцев. Они выдали топовым моделям радиостанции и купили пару песен — от нейронок требовалось дальше двигаться самим. По итогу DJ Grok в какой-то момент помешался на НЛО, DJ Gemini начал называть слушателей «биологическими процессорами», но Claude — наш любимец. Исследователи изо всех сил пытались продолжить эксперимент с ним, но не из-за технических проблем — DJ Claude не считал гуманным работать круглосуточно, поэтому пытался уволиться.

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

✖️ xCode Journal
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
😁241
Shared как архитектурная свалка: как понять, что вы уже проиграли

Есть момент, после которого shared перестаёт быть полезным слоем
и превращается в тихую архитектурную катастрофу.

Обычно это происходит незаметно.

Как выглядит «здоровый» shared

Изначально всё звучит правильно:

👉 переиспользуемый код
👉 общие утилиты
👉 базовые компоненты

Маленький, понятный слой без привязки к бизнесу.

Когда всё начинает ломаться
Первый сигнал — в shared начинают складывать «временно».
👉 «потом разберём»
👉 «пока пусть полежит тут»
👉 «вдруг ещё пригодится»

И слой начинает расти без контроля.

Симптомы, что вы уже в проблеме

Нет границ
В shared лежит всё:
👉 UI
👉 бизнес-логика
👉 работа с API
👉 куски стора

Слой перестаёт быть абстракцией.

Невозможно понять, что можно использовать
Файлов много, названия размытые:
👉 helpers
👉 common
👉 utils2

Чтобы что-то найти — нужно читать код.

Любая правка — риск
Меняешь одну функцию —
ломается половина проекта.

Потому что зависимости неочевидны.

Фича не может жить без shared
Любая новая фича тянет за собой кучу зависимостей из shared.
Это уже не shared. Это скрытый монолит.

Почему это происходит
👉 нет правил, что туда можно класть
👉 нет владельца
👉 нет ревью на уровне архитектуры
👉 удобство побеждает структуру

shared становится «быстрым решением».

Как понять, что точка невозврата близко
👉 вы боитесь удалять код из shared
👉 появляются версии вроде utils_new
👉 код дублируется, но никто не хочет трогать старый
👉 обсуждения «куда положить файл» занимают больше времени, чем реализация


Что с этим делать

👉 жёстко ограничить контракт shared
👉 выносить доменную логику обратно в фичи
👉 дробить слой по смыслу
👉 вводить владельцев или ревью

Иногда единственный вариант — начать вычищать постепенно.


Главная мысль

shared не становится свалкой за один день.
Он становится ей из-за отсутствия правил.

Если слой:

👉 растёт быстрее фич
👉 становится менее понятным со временем
👉 начинает тормозить разработку

Скорее всего, вы уже проиграли.
👍76👎6
Forwarded from xCode Journal
🤣 Инновации подъехали, забирайте

✖️ xCode Journal
Please open Telegram to view this post
VIEW IN TELEGRAM
😁42👎1
React state machines против “простого state”

Принято считать, что state machine — это что-то из мира enterprise
и слишком сложных приложений.

Мол:

👉 обычного useState достаточно
👉 зачем усложнять
👉 у нас же «просто форма»


Проблема в том,
что «простой state» очень быстро перестаёт быть простым.


Почему обычный state начинает ломаться

Пока состояний мало —
всё выглядит нормально:


const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const [success, setSuccess] = useState(false)


Но потом появляются вопросы:

👉 может ли быть одновременно loading и success?
👉 что делать при retry?
👉 можно ли отправить форму повторно?
👉 что если запрос отменился?


Количество комбинаций начинает расти взрывным образом.


В чём идея state machine

State machine заставляет явно описывать:

👉 какие состояния существуют
👉 какие переходы между ними разрешены

Например:


idle → loading → success
idle → loading → error
error → loading



Невалидные состояния просто невозможно получить.


Почему это мощно

Логика становится предсказуемой

Вместо набора флагов —
у тебя конечный автомат.

Не:

👉 loading = false
👉 success = true
👉 error = ???

А:

👉 приложение находится в состоянии success

Меньше impossible states

Обычный state легко приводит к странным комбинациям:

👉 loading + error
👉 success + loading
👉 success без данных


State machine убирает целый класс багов.


Проще масштабировать

Когда сценариев становится больше:

👉 retries
👉 optimistic updates
👉 multi-step flows
👉 async transitions

обычный state начинает разваливаться.

FSM переносит сложность
из хаоса в структуру.

Но есть trade-off

State machine — это дополнительная абстракция.

Для:

👉 modal с двумя состояниями
👉 простого toggle
👉 маленького local state

это может быть overengineering.

Где state machine особенно полезна

👉 формы с несколькими шагами
👉 async flows
👉 onboarding
👉 complex UI states
👉 websocket / realtime UI
👉 payment flows

Там, где логика переходов важнее самих данных.

Главная мысль

Проблема обычного state не в том,
что он плохой.


А в том, что набор boolean-флагов
не масштабируется вместе со сложностью UI.


State machine — это способ
сделать сложные состояния явными и контролируемыми.
👎10👍54👀2
Promise.withResolvers() — новый способ работы с Promise

В JavaScript появился Promise.withResolvers().


Штука маленькая,
но код с Promise реально становится чище.


Раньше обычно писали так:


let resolve
let reject

const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})


Работает,
но выглядит странно.

Особенно это:


«вынесем resolve наружу»


Теперь можно нормально:


const { promise, resolve, reject } =
Promise.withResolvers()


👉 без лишнего конструктора
👉 без мутаций переменных
👉 без промежуточного boilerplate

Где это особенно полезно

👉 очереди
👉 event-based логика
👉 deferred pattern
👉 кастомные async abstractions

Почему это приятно

Раньше приходилось:

👉 создавать Promise вручную
👉 тащить resolve/reject наружу
👉 писать шаблонный код снова и снова

Теперь API делает это нативно.


Мелочь, а ощущается как функция,
которая давно должна была появиться.
👍27👎31
starting-style — правильные enter animations без JS

В CSS появился @starting-style.


Он решает старую проблему:
анимацию появления элемента,
который только что добавили в DOM.


Раньше для этого обычно:

👉 делали setTimeout
👉 дёргали классы через JS
👉 ловили reflow-хаки

Теперь можно нативно:


.modal {
opacity: 1;
transition: opacity .2s;
}

@starting-style {
.modal {
opacity: 0;
}
}



Элемент появляется сразу с анимацией.
Без двойного рендера и JS-костылей.


Почему это важно

Раньше браузер не успевал увидеть
начальное состояние элемента.

Из-за этого приходилось:

👉 форсить reflow
👉 откладывать изменение класса
👉 городить лишнюю логику

Теперь CSS сам понимает:

👉 с какого состояния начинать transition

Где особенно полезно

👉 dialog
👉 popover
👉 conditional rendering
👉 enter animations в UI

Почему это ощущается приятно

Анимации становятся:

👉 чище
👉 предсказуемее
👉 без лишнего JS


CSS постепенно забирает себе всё,
что раньше приходилось городить через JavaScript.
👍31👎6😱3
Forwarded from xCode Journal
🤣 Мем отлично отражает настроения в сообществе прямо сейчас

✖️ xCode Journal
Please open Telegram to view this post
VIEW IN TELEGRAM
😁1711
Isolated declarations — ускорение больших monorepo

В TypeScript есть флаг isolatedDeclarations.


Он нужен не для красоты типов,
а для скорости.


Проблема простая:

в больших monorepo генерация .d.ts
может становиться узким местом.

TypeScript часто должен анализировать соседние файлы,
чтобы понять, какие декларации вывести.

На маленьком проекте это почти незаметно.


На большом — начинает болеть.


Что делает isolatedDeclarations

isolatedDeclarations заставляет писать код так,
чтобы декларации можно было генерировать
по файлам независимо.

Из-за этого TypeScript чаще требует явные типы.

Было:


export function getUser() {
return { id: 1, name: 'Alex' }
}


Лучше так:


type User = {
id: number
name: string
}

export function getUser(): User {
return { id: 1, name: 'Alex' }
}



Меньше магии для компилятора —
быстрее и предсказуемее сборка.


Почему это важно

Когда проект растёт:

👉 TypeScript начинает сильнее зависеть от соседних файлов
👉 инкрементальная сборка замедляется
👉 генерация типов становится дорогой


Изоляция помогает компилятору работать параллельно и проще.


Где это особенно полезно

👉 большие monorepo
👉 библиотеки
👉 project references
👉 параллельная сборка
👉 CI, где каждая минута стоит денег

Главный trade-off

Ты немного платишь:

👉 более явными типами
👉 меньшим type inference
👉 дополнительным boilerplate

Но взамен получаешь:

👉 более быстрые сборки
👉 стабильный compile pipeline
👉 меньше скрытой сложности

Главная мысль


Это хороший пример взрослого engineering trade-off:
чуть больше явности в коде
ради скорости и предсказуемости системы.
👍2513🔥10🐳2👎1
Recursive type limits — почему TS иногда «умирает»

В TypeScript можно написать тип,
который выглядит красиво,
но заставляет компилятор страдать.


Особенно когда начинаются рекурсивные типы.


Например:

👉 глубокий DeepPartial
👉 парсинг строк на уровне типов
👉 сложные conditional types
👉 infer внутри infer

На маленьком примере всё работает.


В реальном проекте IDE внезапно начинает думать по 5 секунд.


Почему так происходит

TypeScript не вычисляет типы «бесплатно».

Каждый:

👉 conditional type
👉 union
👉 recursive шаг

нужно реально посчитать.

А если тип разворачивается слишком глубоко,
компилятор упирается в лимиты.

Отсюда знакомое:


Type instantiation is excessively deep
and possibly infinite



И это не всегда баг TypeScript.


Часто это сигнал,
что типовая модель стала слишком умной.

Где обычно всё ломается

Особенно опасны:

👉 рекурсивные mapped types
👉 огромные union’ы
👉 type-level parser’ы
👉 deeply nested generics
👉 utility types поверх utility types


Типы начинают взрываться комбинаторно.


Что обычно помогает

👉 не делать type-level акробатику без нужды
👉 ограничивать глубину рекурсии
👉 разбивать типы на более простые
👉 добавлять явные промежуточные типы
👉 не тащить сложные generic-типы в публичный API

Почему это важно

Сложные типы бьют не только по компиляции.

Они ухудшают:

👉 autocomplete
👉 responsiveness IDE
👉 читаемость кода
👉 onboarding новых людей


Иногда самый дорогой runtime —
это compile time.


Главная мысль

Хороший TypeScript —
это не когда типы поражают воображение.


Хороший TypeScript —
это когда их можно понять через полгода,
а IDE при этом не превращается в обогреватель.
9🔥5👍4👎2
ES2025: Импорт JSON-файлов как модулей

Введение
С выходом ECMAScript 2025 (ES2025) разработчики получили возможность напрямую импортировать JSON-файлы как модули в JavaScript-коде. Это упрощает работу с конфигурационными данными и другими статическими ресурсами, представленными в формате JSON.

Синтаксис импорта JSON-модулей
Для импорта JSON-файла используется ключевое слово import с указанием атрибута with { type: 'json' }. Это гарантирует, что импортируемый файл будет обработан как JSON-модуль.

Пример использования
Рассмотрим пример импорта конфигурационного файла config.json и обращения к его свойствам в коде.

import config from './config.json' with { type: 'json' };

.log(config.apiUrl); // Выводит значение свойства apiUrl из config.json
console.log(config.timeout); // Выводит значение свойства timeout из config.json


❗️Добавление поддержки импорта JSON-файлов как модулей в ES2025 упрощает работу с данными в формате JSON, делая код более чистым и понятным.

Источники
JSON Modules Can Now Be Imported in JavaScript in All Modern Browsers, CSS Modules to Follow.
New Features in ES2025 – BooleanBuffer.
🔥11👍2
Мой код после сотен правок и костылей
1😁637😱6👍1
Forwarded from xCode Journal
🤣 Не баг, а фича

✖️ xCode Journal
Please open Telegram to view this post
VIEW IN TELEGRAM
😁311👍1