Frontend Senior Developer

Курс для опытных фронтенд-разработчиков, нацеленный на переход на уровень Senior: архитектура, производительность, безопасность и качество. Разбираем системный дизайн UI, масштабирование приложений, инженерные практики и лидерство в команде.

1. Роль Senior: ожидания, ответственность, рост

Роль Senior: ожидания, ответственность, рост

Зачем вообще нужна «роль Senior»

Senior Frontend Developer — это не «самый быстрый кодер» и не «человек, который знает все фреймворки». Senior — это роль, в которой ценность создаётся через ответственность и влияние на результат команды и продукта.

Типичные причины, почему компаниям нужен senior-уровень:

  • Снижать риски в сложных изменениях.
  • Ускорять команду за счёт правильных решений и процессов.
  • Делать систему поддерживаемой при росте продукта.
  • Выращивать людей и распространять практики.
  • В этом курсе мы будем опираться на идею: senior — это человек, который управляет неопределённостью (технической, продуктовой, организационной) и превращает её в предсказуемую поставку ценности.

    Чем Senior отличается от Middle и Lead

    Важно разделять уровень и позицию.

  • Middle — уверенно решает задачи в рамках существующих практик и архитектуры.
  • Senior — улучшает практики и архитектуру, закрывает сложные и рискованные зоны, повышает эффективность других.
  • Lead (Tech Lead / Team Lead) — это чаще роль с дополнительными управленческими обязанностями: приоритизация, планирование, выстраивание команды. Lead может быть senior’ом по уровню, но не каждый senior обязан быть lead.
  • Senior в фронтенде обычно не «владельцем всего», но он точно владелец последствий своих решений.

    Ожидания от Senior Frontend Developer

    Ожидания удобно смотреть как на несколько «контуров» ответственности.

    Технический контур

    Senior ожидаемо умеет:
  • Проектировать изменения так, чтобы они масштабировались и не ломали продукт.
  • Делать осознанные компромиссы между скоростью, качеством и риском.
  • Работать с существующим кодом (legacy): улучшать, не переписывая «ради красоты».
  • Разбираться в производительности, доступности и безопасности на уровне практических решений.
  • Термины, которые важно понимать:

  • Legacy-код — существующая кодовая база, которую сложно менять из-за накопленных решений, отсутствия тестов, устаревших подходов или нехватки документации.
  • Регрессия — когда новое изменение ломает уже работающий функционал.
  • Технический долг — последствия компромиссов (осознанных или случайных), которые замедляют развитие в будущем.
  • Продуктовый контур

    Senior не обязан быть менеджером продукта, но обязан понимать:
  • Что именно пользователь пытается сделать и почему.
  • Какая метрика или эффект стоит за задачей (например, конверсия, удержание, снижение ошибок).
  • Как уменьшить объём работы, не потеряв ценность (сильный навык — предлагать более простое решение).
  • Практика: прежде чем реализовывать, senior уточняет критерии готовности.

  • Критерии готовности (Definition of Done на уровне задачи) — список условий, при которых работа считается завершённой: поведение, тесты, аналитика, документация, доступность, локализация и т.д.
  • Командный контур

    Senior повышает общую пропускную способность команды:
  • Делится знаниями так, чтобы они оставались в команде (а не в голове).
  • Делает код-ревью не как «поиск ошибок», а как инструмент выравнивания качества.
  • Помогает менее опытным разработчикам расти через контекст и объяснение причин.
  • Уточнение:

  • Код-ревью — процесс проверки изменений другими разработчиками до вливания в основную ветку. Цель — качество, общие стандарты и снижение рисков.
  • Процессный контур

    Senior влияет на то, как команда поставляет изменения:
  • Предлагает и внедряет практики, которые уменьшают хаос (например, договорённости по релизам, тестам, фича-флагам).
  • Помогает разбивать большие инициативы на поставляемые части.
  • Предотвращает повторение инцидентов через разбор причин.
  • Инцидент — ситуация, когда продукт работает неправильно (ошибка, деградация производительности, недоступность), и это влияет на пользователей или бизнес.
  • Разбор причин (postmortem) — разбор, почему случилось, что сработало, что нет, и какие системные изменения нужны, чтобы не повторилось.
  • !Диаграмма показывает, что senior отвечает не только за код, но и за продукт, команду и процесс.

    Ответственность: как она выглядит на практике

    Ответственность senior’а — это способность взять проблему целиком и довести её до результата, не перекладывая неизвестное на других.

    Принцип «владения» (ownership)

    Владение — это когда вы:
  • Видите проблему, даже если она «не в таске».
  • Проясняете требования и ограничения.
  • Предлагаете план.
  • Делаете поставку.
  • Следите за последствиями в продакшене.
  • Важно: владение — не значит делать всё в одиночку. Наоборот, senior часто достигает результата через вовлечение других.

    Умение принимать решения в условиях неопределённости

    В реальных проектах редко есть идеальные вводные. Поэтому senior:
  • Формулирует варианты решения.
  • Явно описывает риски и стоимость.
  • Делает выбор и фиксирует его.
  • Полезная практика — короткий технический дизайн.

  • Технический дизайн — краткое описание подхода к решению: цель, контекст, варианты, выбранный путь, риски, план внедрения.
  • Не обязательно писать «документ на 20 страниц». Часто достаточно 1–2 страниц или даже описания в задаче, но с ясной структурой.

    Коммуникация как часть инженерной работы

    Senior регулярно коммуницирует:
  • С продуктом: про ценность и приоритеты.
  • С дизайном: про ограничения и системность.
  • С бэкендом: про контракты API и совместные риски.
  • С QA: про сценарии, критичные проверки, автоматизацию.
  • Ключевой навык — говорить не только что вы сделали, но и почему так.

    Senior в фронтенде: типичные зоны ответственности

    Ниже — примеры областей, где senior-уровень проявляется особенно ярко.

    Архитектура интерфейса

    Senior помогает удерживать проект в состоянии, когда он:
  • Понятно устроен.
  • Масштабируется по командам и фичам.
  • Не превращается в «комок взаимозависимостей».
  • Что сюда входит:

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

    Senior думает о том, как изменение будет жить через месяц и год:
  • Как его тестировать.
  • Как его откатывать.
  • Как его наблюдать.
  • Термины:

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

    Senior на фронтенде отвечает не за «оптимизацию ради оптимизации», а за перформанс, который чувствует пользователь:
  • Быстрый старт.
  • Плавность интерфейса.
  • Отсутствие деградации со временем.
  • Надёжный источник по ключевым метрикам производительности:

  • Web Vitals от Google
  • Доступность

    Доступность (accessibility) — это когда продуктом могут пользоваться люди с разными ограничениями (зрение, моторика, восприятие), а также разные устройства и условия.

    Senior умеет:

  • Встраивать доступность в привычные практики, а не «добавлять в конце».
  • Объяснять команде, какие проверки важны.
  • Официальная база знаний:

  • WAI-ARIA Authoring Practices
  • Безопасность на уровне UI

    Senior не заменяет security-специалиста, но обязан понимать типовые риски:
  • Небезопасный вывод пользовательского ввода.
  • Ошибки в работе с токенами и сессиями.
  • Утечки данных через логи/аналитику.
  • База по веб-рискам:

  • OWASP Top 10
  • Рост до Senior и рост внутри Senior

    Рост — это не чек-лист технологий. Это увеличение масштаба влияния.

    Признаки, что вы готовы к senior-уровню

    Вы близки к senior’у, если обычно вы:
  • Берёте задачи с неясными требованиями и доводите до ясного плана.
  • Видите системные проблемы и предлагаете реалистичные улучшения.
  • В продакшене думаете о последствиях (метрики, инциденты, обратная связь).
  • Делаете других сильнее: через ревью, пары, объяснения, документацию.
  • Признаки «псевдо-senior» (типовые ловушки)

    Эти паттерны часто мешают росту:
  • «Я перепишу всё правильно» вместо улучшения по шагам.
  • «Я знаю лучше всех» вместо совместного решения.
  • Сильный код без коммуникации и прозрачности.
  • Оптимизация без цели и метрик.
  • Как senior растёт дальше

    Дальнейшие траектории обычно такие:
  • Углубление в архитектуру и системный дизайн на фронтенде.
  • Рост в техлида: больше координации и ответственности за поставку.
  • Рост в staff/principal (если в компании есть такие уровни): влияние на несколько команд и стратегические решения.
  • Специализация: производительность, дизайн-системы, инфраструктура фронтенда, наблюдаемость.
  • Практические привычки Senior, которые можно начать тренировать сразу

    Эти привычки дают эффект независимо от стека.

  • Всегда фиксировать допущения: что вы считаете правдой, но не уверены.
  • Писать краткий план перед реализацией, особенно для задач от 2–3 дней.
  • На ревью объяснять причину замечания и предлагать альтернативу.
  • Делать изменения маленькими и поставляемыми.
  • После релиза проверять ключевые сценарии и метрики.
  • Итог

    Senior Frontend Developer — это роль, где ценность создаётся через ответственность и влияние: на код, продукт, команду и процесс. В следующих материалах курса мы будем разбирать конкретные техники, которые превращают эти ожидания в практику: как проектировать изменения, выстраивать качество, держать производительность и масштабировать разработку без потери управляемости.

    2. Архитектура фронтенд-приложений и дизайн систем

    Архитектура фронтенд-приложений и дизайн систем

    Связь с ролью Senior

    В прошлой теме мы зафиксировали ключевую идею: Senior Frontend Developer создаёт ценность через ответственность и влияние — на код, продукт, команду и процесс.

    Архитектура и дизайн-система — две области, где это влияние проявляется особенно заметно:

  • Архитектура делает продукт предсказуемо изменяемым: снижает риски регрессий, ускоряет поставку, помогает масштабироваться.
  • Дизайн-система делает интерфейс предсказуемо единообразным: ускоряет разработку, повышает качество UX, уменьшает стоимость изменений.
  • Эта статья про то, как мыслить архитектурно на фронтенде и как связывать технические решения с системой дизайна так, чтобы команда могла стабильно поставлять ценность.

    Что такое архитектура фронтенда

    Архитектура фронтенд-приложения — это набор структурных решений и правил, которые определяют:
  • Из каких модулей состоит приложение и кто за что отвечает.
  • Как проходят данные: от API до UI.
  • Где лежит бизнес-логика и как она тестируется.
  • Как приложение развивается: добавляются фичи, меняется дизайн, растёт команда.
  • Важно не путать архитектуру с:

  • Стеком: React/Vue/Svelte — инструмент, не архитектура.
  • Папками в репозитории: структура папок должна отражать архитектуру, но сама по себе ею не является.
  • Красивыми абстракциями: сложность без выгоды ухудшает архитектуру.
  • Критерии хорошей архитектуры

    Хорошая архитектура — та, которая помогает достигать целей продукта при реальных ограничениях.

    Практичные критерии:

  • Изменяемость: добавление фичи не требует каскадных правок по всему коду.
  • Локализация ответственности: понятно, где искать проблему и где вносить изменение.
  • Контролируемые зависимости: модули не превращаются в «всё зависит от всего».
  • Тестируемость: критичная логика покрывается тестами без сложной подготовки окружения.
  • Наблюдаемость: ошибки и деградации можно обнаружить и объяснить.
  • Эволюционность: можно улучшать постепенно, без переписывания «с нуля».
  • Сеньорская оптика: архитектура оценивается не по «идеальности», а по тому, как она снижает риск и ускоряет поставку.

    Слои типичного фронтенд-приложения

    Ниже — удобная модель, которая работает для SPA и для MPA (когда страниц несколько, но принципы те же).

    Термины:

  • SPA (Single Page Application) — приложение, где навигация чаще происходит без полной перезагрузки страницы.
  • MPA (Multi Page Application) — сайт/приложение с несколькими страницами, где навигация обычно ведёт к загрузке нового документа.
  • Один из устойчивых подходов — разделять код по ответственности:

  • App shell: инициализация приложения, провайдеры, конфигурация, роутинг.
  • Pages: страницы как композиция фич и виджетов.
  • Features: пользовательские сценарии (например, «поиск», «оплата», «редактирование профиля»).
  • Entities: доменные сущности (например, User, Order) и операции вокруг них.
  • Shared: переиспользуемые UI-компоненты и утилиты.
  • Infrastructure: HTTP-клиент, логирование, аналитика, работа с хранилищем, фича-флаги.
  • Ключевое правило для снижения связанности:

  • Зависимости должны быть направлены от верхних уровней к нижним (страницы используют фичи, фичи используют shared), а не наоборот.
  • !Схема показывает типичную слоистую архитектуру и направление зависимостей

    Модульность: как правильно «резать» код

    Модуль — это часть системы с чёткой зоной ответственности и явным публичным API.

    Два распространённых способа декомпозиции

  • По слоям (layer-first): components/, services/, utils/.
  • По фичам (feature-first): features/search/, features/checkout/.
  • На практике senior чаще ведёт проект к декомпозиции по фичам (или гибридной модели), потому что это:

  • Упрощает параллельную работу.
  • Ограничивает радиус изменений.
  • Помогает держать бизнес-логику ближе к месту использования.
  • Одна из известных методологий с фокусом на слой + фича — Feature-Sliced Design:

  • Feature-Sliced Design
  • Публичный API модуля

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

    Практика:

  • В каждом модуле есть точка входа (например, index.ts), где перечислены публичные экспорты.
  • Снаружи запрещены «глубокие импорты» вида features/search/internal/something.
  • Это снижает риск случайной привязки к внутренностям, которые потом трудно менять.

    Данные и состояние: разделяйте типы, а не инструменты

    Состояние на фронтенде удобно делить по природе:
  • Server state: данные с сервера, кеш, синхронизация, повторные запросы.
  • Client state: бизнес-состояние на клиенте, которое не является прямым отражением сервера.
  • UI state: состояние конкретного UI (открыт модал, активная вкладка, фильтр в форме).
  • Ошибочный паттерн, который часто приводит к «разрастанию стора»:

  • Хранить всё в одном глобальном состоянии без причин.
  • Практичный подход:

  • Server state держите в библиотеке, которая умеет кеш и инвалидации.
  • UI state держите как можно ближе к компонентам.
  • Client state выносите наверх только если оно действительно разделяется между несколькими областями.
  • Примеры реальных инструментов и документации:

  • TanStack Query
  • Redux Toolkit
  • Инфраструктурный слой: невидимое, но критичное

    Инфраструктура — это код, который не про фичи напрямую, но влияет на надёжность и скорость разработки.

    Что обычно входит:

  • HTTP-клиент и политика ошибок.
  • Логирование и обработка исключений.
  • Аналитика событий.
  • Фича-флаги и конфигурация.
  • Интеграции с A/B тестами.
  • Сеньорский фокус: стандартизировать инфраструктуру так, чтобы каждая фича не «изобретала» запросы, обработку ошибок и трекинг заново.

    Микрофронтенды как архитектурный выбор

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

    Важно: микрофронтенды — не цель, а инструмент для организационного масштаба.

    Когда микрофронтенды оправданы

  • Несколько команд поставляют крупные части продукта параллельно и часто.
  • Есть реальная проблема координации релизов в монорепозитории или монолите.
  • Нужно изолировать риск: отдельные домены продукта можно выкатывать независимо.
  • Когда это почти наверняка преждевременно

  • Команда маленькая.
  • Основная проблема — качество и процессы, а не размер.
  • Нет зрелой инфраструктуры сборки, версионирования и наблюдаемости.
  • Один из распространённых механизмов на вебе — Module Federation:

  • Документация Module Federation в Webpack
  • Сравнение подходов интеграции

    | Подход | Плюсы | Минусы | Типичный кейс | |---|---|---|---| | Монолитный фронтенд | Простая разработка и отладка | Сложнее масштабировать по командам | 1 команда, быстрый старт | | Микрофронтенды (runtime интеграция) | Независимые релизы | Сложнее перформанс и наблюдаемость | Несколько команд, разные домены | | Встраивание через iframe | Хорошая изоляция | Плохая интеграция UX, коммуникация сложнее | Админки, внешние модули |

    Дизайн-система: что это и чем не является

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

    Не путайте:

  • UI kit — набор компонентов. Это часть дизайн-системы, но не вся система.
  • Style guide — гайд по стилям (цвета, типографика). Тоже часть, но не всё.
  • Сеньорский взгляд: дизайн-система — это продукт внутри продукта. У неё есть пользователи (команды), SLA (скорость и стабильность), релизы и обратная совместимость.

    Из чего обычно состоит дизайн-система

    | Артефакт | Что это | Зачем нужно | |---|---|---| | Design tokens | Переменные дизайна: цвета, отступы, типографика, радиусы | Единые значения и быстрые изменения темы | | UI-компоненты | Кнопки, инпуты, модальные окна и т.д. | Переиспользование и единое поведение | | Паттерны | Готовые решения: формы, таблицы, empty states | Снижение продуктовых ошибок | | Гайдлайны | Правила использования компонентов и контента | Единообразие UX | | Доступность | Роли, фокус, контраст, клавиатурная навигация | Инклюзивность и соответствие стандартам |

    Практические источники по системному дизайну интерфейсов:

  • Material Design
  • Apple Human Interface Guidelines
  • Инструмент для документирования и разработки UI-компонентов:

  • Storybook
  • Дизайн-токены: мост между дизайном и кодом

    Дизайн-токены — это именованные значения, которые выражают решения дизайна (например, color.text.primary, space.2, radius.md).

    Практика, которая уменьшает хаос:

  • Токены описывают смысл, а не конкретное значение (например, text.primary, а не gray900).
  • Компоненты используют токены, а не «сырые» значения.
  • Токены позволяют делать темы (например, светлая/тёмная) без переписывания компонентов.
  • Инструмент для сборки и распространения токенов:

  • Style Dictionary
  • Версионирование и обратная совместимость дизайн-системы

    Дизайн-система почти всегда живёт как библиотека (внутренняя или публичная). Значит, нужны правила совместимости.

    Термины:

  • Breaking change — изменение, после которого потребителям нужно менять код.
  • SemVer (семантическое версионирование) — соглашение о версиях MAJOR.MINOR.PATCH.
  • База по SemVer:

  • Semantic Versioning
  • Практика для зрелой дизайн-системы:

  • Депрекации: сначала помечаем устаревшее, даём срок миграции, потом удаляем.
  • Migration guide: короткая инструкция «как перейти».
  • Автоматизация: линтеры, codemod-скрипты, проверки в CI.
  • Как связать архитектуру приложения и дизайн-систему

    Частая ошибка: дизайн-система живёт отдельно и «не пронизывает» продукт.

    Рабочая связка выглядит так:

  • UI-уровень приложения использует компоненты дизайн-системы.
  • Бизнес-логика и данные остаются в приложении (в фичах/сущностях), а не в дизайн-системе.
  • Дизайн-система предоставляет расширяемые примитивы (например, Button, Input, Modal), но не тащит продуктовые зависимости.
  • Практичное правило границ:

  • Дизайн-система знает про UI и доступность.
  • Приложение знает про сценарии, данные и домен.
  • !Схема показывает границы ответственности между дизайн-системой и продуктовым кодом

    Документирование решений: когда senior должен писать

    Архитектура не живёт в голове. Чтобы команда работала согласованно, решения нужно фиксировать.

    Удобный лёгкий формат — ADR (Architecture Decision Record): короткая запись решения с контекстом и последствиями.

    Описание подхода и примеры:

  • Architecture Decision Records
  • Минимальная структура ADR:

  • Контекст: какая проблема и ограничения.
  • Варианты: 2–3 разумных подхода.
  • Решение: что выбрали.
  • Последствия: плюсы, минусы, риски, что нужно сделать.
  • Сеньорская ценность ADR: снижает повторные споры и делает систему решений прозрачной для новых участников.

    Типовые ошибки в архитектуре и дизайн-системах

    Ниже — проблемы, которые часто выглядят как «технические», но на самом деле бьют по поставке.

  • Слишком ранняя сложность: микрофронтенды или абстракции до появления реальных болей.
  • Глобальное состояние без правил: любой компонент может менять что угодно.
  • Смешивание домена и UI: бизнес-логика прячется в компонентах и её трудно тестировать.
  • Дизайн-система как «кладбище компонентов»: много похожих вариантов без единого API.
  • Отсутствие контрактов: нет правил, что считается публичным API модуля.
  • Нет стратегии миграций: обновление библиотеки превращается в кризис.
  • Итог

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

    Как senior, вы не просто выбираете паттерны, а:

  • Формулируете границы ответственности.
  • Описываете решения и последствия.
  • Договариваетесь о правилах и делаете их исполнимыми.
  • Строите систему, в которой изменения дешевеют со временем, а не дорожают.
  • 3. Продвинутый TypeScript и управление сложностью

    Продвинутый TypeScript и управление сложностью

    Связь с предыдущими темами курса

    В теме про роль Senior мы зафиксировали: senior создаёт ценность через ответственность и управление рисками — не только через написание кода. В теме про архитектуру мы говорили про границы модулей, публичные API и предсказуемость изменений.

    TypeScript — один из самых практичных инструментов, который помогает:

  • превращать неявные договорённости в проверяемые контракты;
  • удерживать модульные границы (и не допускать «глубоких импортов» на уровне типов);
  • снижать стоимость изменения архитектуры и дизайн-системы;
  • делать ошибки раньше и дешевле.
  • Эта статья — про продвинутые возможности TypeScript и, главное, про то, как не утонуть в них, сохранив систему простой для команды.

    Что такое «сложность» в TypeScript-коде

    Сложность во фронтенде часто возникает не из-за алгоритмов, а из-за комбинации факторов:
  • много состояний UI и переходов между ними;
  • много форматов данных (API, кеш, формы, доменная модель);
  • долгоживущие контракты между модулями и командами;
  • дизайн-система как библиотека с обратной совместимостью.
  • TypeScript может как уменьшить, так и увеличить сложность.

    Практичный критерий

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

  • требуют «расшифровки» в каждом ревью;
  • маскируют отсутствие нормальной архитектуры;
  • создают иллюзию безопасности без реальных гарантий на рантайме.
  • Официальная база по языку:

  • TypeScript Handbook
  • Базовые настройки строгости как фундамент

    Продвинутый TypeScript почти всегда начинается не с условных типов, а с дисциплины настроек компилятора.

    Рекомендуемая отправная точка

    Обычно выгодно включать strict и постепенно усиливать режимы, которые заставляют «платить» за неаккуратность прямо в момент разработки:
  • strict
  • noUncheckedIndexedAccess
  • exactOptionalPropertyTypes
  • Документация по настройкам:

  • TSConfig Reference
  • Почему это senior-практика

  • Настройки строгости превращают стиль разработки в повторяемый процесс для всей команды.
  • Они уменьшают риск регрессий при больших изменениях: ошибки «всплывают» на этапе сборки.
  • Они делают архитектурные границы видимыми: плохие зависимости часто проявляются через «дырявые» типы.
  • Моделирование домена: союзы, дискриминаторы и исчерпывающие проверки

    Один из самых сильных паттернов TypeScript — моделировать вариативность через объединения типов (union types).

    Дискриминируемые объединения

    Идея: у каждого варианта есть поле-дискриминатор (например, status или type), по которому TypeScript сужает тип.

    Это напрямую снижает количество «невозможных состояний», вроде loading === true и data уже есть одновременно.

    Исчерпывающая проверка через never

    Проблема: кто-то добавит новый статус, а switch забудут обновить. Senior-решение — сделать так, чтобы компилятор заставил обновить обработку.

    Если вы добавите новый вариант в RequestState, компилятор покажет ошибку в assertNever.

    unknown вместо any: дисциплина данных на входе

    Для senior-уровня важно различать:
  • any отключает типобезопасность и распространяет «неизвестность» дальше.
  • unknown заставляет явно доказать, что значение имеет нужную форму.
  • Правило, которое снижает хаос: на границах системы используйте unknown, внутри — точные типы.

    Runtime-валидация и типы: где TypeScript не спасает

    TypeScript проверяет типы только во время компиляции. Но данные приходят:
  • из сети (API),
  • из localStorage,
  • из query-параметров,
  • из аналитики,
  • из внешних SDK.
  • Если данные приходят снаружи, вам нужны рантайм-проверки.

    !Схема показывает, что типы защищают код внутри приложения, но на входе нужны рантайм-проверки данных

    Один из популярных инструментов для рантайм-схем и вывода типов:

  • Zod
  • Ключевая инженерная мысль: TypeScript не отменяет необходимость проверять входные данные — он помогает сделать это системно и переиспользуемо.

    Generics как способ удержать переиспользование под контролем

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

    Пример: типобезопасная обёртка над fetch

    Для больших проектов это поддерживает единый контракт локализации.

    satisfies: проверять форму, не теряя конкретику

    Оператор satisfies помогает одновременно:
  • проверить, что объект соответствует контракту;
  • сохранить узкие литеральные типы, чтобы не потерять точность.
  • Это полезно для архитектурных конфигов: роуты, фича-флаги, таблицы ролей, карты событий аналитики.

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

  • TypeScript Handbook: The satisfies Operator
  • Типы как инструмент архитектуры: публичные API модулей

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

    Практика: экспортируйте только нужное

  • Делайте один входной файл index.ts для модуля.
  • Не экспортируйте внутренние типы, которые вы не готовы поддерживать.
  • Не допускайте «глубоких импортов» в потребителях.
  • Эта дисциплина помогает:

  • менять внутреннее устройство без массовых правок;
  • проводить депрекации как в дизайн-системе;
  • снижать связанность между фичами.
  • import type как сигнал границы

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

    TypeScript и дизайн-система: типизация компонентов без ловушек

    Дизайн-система — библиотека. Значит, типы должны быть:
  • стабильными;
  • предсказуемыми;
  • совместимыми при минорных обновлениях.
  • Паттерн: ограничение вариантов через объединения

    Это делает невозможным случайный вариант вроде "primari".

    Паттерн: «брендированные» типы для доменных сущностей

    Иногда полезно различать одинаковые по форме значения, например UserId и OrderId, оба строки.

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

    Управление сложностью: правила, которые держат TypeScript «в форме»

    Ниже — практики, которые обычно дают больше пользы, чем точечные «магические» типы.

    Договорённость о том, где живут типы

    Хорошая стратегия:
  • доменные типы рядом с доменными сущностями (entities);
  • типы API рядом с инфраструктурой (клиент, адаптеры);
  • UI-пропсы рядом с компонентами дизайн-системы;
  • типы фич рядом со сценариями (features).
  • Это поддерживает архитектурный принцип: ответственность локализована.

    Разделение типов по слоям

    Полезно разделять:
  • типы данных «как пришли» (DTO);
  • типы доменной модели «как используем»;
  • типы форм и UI.
  • Это снижает «протекание» формата API в UI и упрощает миграции.

    Минимизируйте «умные типы» в продуктовом коде

    Если условный тип или mapped type занимает половину файла, задайте два вопроса:
  • можно ли сделать проще и понятнее для команды;
  • можно ли перенести сложность в инфраструктурную утилиту с тестами типов.
  • Добавляйте тесты типов там, где типы — часть API

    Для библиотечных модулей (дизайн-система, утилиты) разумно иметь проверки типов на уровне компиляции. Один из распространённых инструментов:
  • tsd
  • Стандартизируйте линтинг TypeScript

    Чтобы правила были едины, используйте стандартные плагины:
  • typescript-eslint
  • Это помогает удержать базовую гигиену: запреты на any, неиспользуемые переменные, опасные приведения типов.

    Типовые ошибки senior-уровня и как их предотвращать

  • as как «молоток»
  • Глобальные типы без границ
  • Смешивание DTO и доменной модели
  • Типы, которые не отражают реальные состояния
  • Перегрузка дизайн-системы бизнес-логикой
  • Полезная эвристика для ревью: если типизация не делает код проще, вероятно, она делает его хуже.

    Итог

    Продвинутый TypeScript — это не набор трюков, а дисциплина управления контрактами и сложностью:
  • строгие настройки компилятора задают базовый уровень качества;
  • дискриминируемые объединения и never помогают моделировать состояния без дыр;
  • unknown и рантайм-валидация защищают границы системы;
  • дженерики и условные типы полезны в инфраструктуре и библиотеках, но требуют меры;
  • типы — часть архитектуры: они должны поддерживать границы модулей и обратную совместимость.
  • В следующем развитии курса эти принципы будут напрямую применяться к стабильной поставке изменений: качеству, тестированию, наблюдаемости и безопасной эволюции больших фронтенд-систем.

    4. Производительность: Core Web Vitals, рендеринг, кэш

    Производительность: Core Web Vitals, рендеринг, кэш

    Связь с предыдущими темами курса

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

    Производительность напрямую связывает все эти идеи:

  • это риск (падение конверсии, рост отказов, деградация UX);
  • это архитектура (границы фич, стратегия загрузки, кэширование, SSR/CSR);
  • это контракты (что именно загружается, когда и почему, и как это измеряется).
  • Senior-подход к производительности — не «оптимизировать всё», а:

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

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

    Производительность как восприятие пользователем

    Пользователь не думает категориями «миллисекунды JS» или «размер бандла». Он воспринимает:
  • как быстро появился основной контент;
  • как быстро интерфейс начал реагировать на действия;
  • насколько интерфейс стабилен (не прыгает).
  • Именно это отражают Core Web Vitals.

    Лабораторные и полевые измерения

  • Лабораторные измерения: запуск в контролируемых условиях (например, Lighthouse). Удобно для сравнения до/после, но может не отражать реальный мир.
  • Полевые измерения: данные реальных пользователей (например, CrUX или собственный RUM). Это истина для продукта, но сложнее интерпретировать.
  • Полезные источники:

  • Core Web Vitals
  • Chrome User Experience Report (CrUX)
  • Core Web Vitals: что измеряем и почему

    Core Web Vitals — набор метрик, которые описывают ключевые аспекты пользовательского опыта.

    LCP: Largest Contentful Paint

    LCP — время от начала загрузки страницы до момента, когда в области видимости отрисован крупнейший значимый элемент контента (часто это hero-изображение, крупный заголовок, блок карточки).

    Что обычно ухудшает LCP:

  • медленный серверный ответ и отсутствие CDN;
  • слишком тяжёлые изображения и шрифты;
  • блокирующие рендер ресурсы (CSS/JS);
  • большой объём JavaScript до первой отрисовки.
  • Что обычно улучшает LCP:

  • оптимизация изображений (формат, размеры, srcset);
  • критический CSS и уменьшение блокировок;
  • SSR/пререндер (если оправдано) и стриминг;
  • корректная кэш-стратегия статических ресурсов.
  • Источник по LCP:

  • Оптимизация LCP
  • INP: Interaction to Next Paint

    INP — метрика отзывчивости: насколько быстро после взаимодействия пользователя (клик, ввод, тап) интерфейс может выполнить обработчики и показать следующий кадр.

    INP заменяет FID как более репрезентативную метрику интерактивности.

    Что обычно ухудшает INP:

  • длинные задачи на main thread (тяжёлый JS, большие синхронные вычисления);
  • «дорогие» ререндеры и частые перерасчёты layout;
  • обработчики событий, которые делают много работы до следующей отрисовки.
  • Что обычно улучшает INP:

  • дробление долгих задач и перенос в Web Worker, где уместно;
  • виртуализация больших списков;
  • мемоизация и уменьшение перерисовок;
  • оптимизация обработчиков ввода.
  • Источник по INP:

  • Interaction to Next Paint (INP)
  • CLS: Cumulative Layout Shift

    CLS — суммарная величина неожиданных сдвигов макета во время жизни страницы. Проще: «прыгает ли интерфейс».

    Что обычно ухудшает CLS:

  • изображения/видео без заранее заданных размеров;
  • вставка баннеров/виджетов выше контента;
  • подгрузка шрифтов, меняющая метрики текста;
  • компоненты, которые появляются без резервирования места.
  • Что обычно улучшает CLS:

  • явные размеры медиа (или правильное резервирование места);
  • аккуратная стратегия загрузки шрифтов;
  • skeleton-экраны и стабильные контейнеры.
  • Источник по CLS:

  • Оптимизация CLS
  • !Схема помогает запомнить, что LCP про появление основного контента, INP про отзывчивость, CLS про стабильность макета.

    Как устроен рендеринг в браузере: где «теряется» время

    Чтобы оптимизировать, нужно понимать, что именно происходит в браузере.

    Упрощённый pipeline

    Типичный путь от байтов до пикселей:
  • загрузка HTML;
  • парсинг HTML и построение DOM;
  • загрузка и парсинг CSS, построение CSSOM;
  • построение дерева рендера;
  • layout (расчёт размеров и позиций);
  • paint (рисование);
  • compositing (сборка слоёв и вывод).
  • JavaScript может вмешиваться на каждом этапе: блокировать загрузку, триггерить перерасчёт layout, провоцировать частые перерисовки.

    Практичный источник:

  • Rendering performance
  • Main thread как «узкое горлышко»

    Многое на вебе выполняется на main thread: парсинг, layout, выполнение JS, обработка событий, часть работы фреймворка.

    Если main thread занят:

  • ухудшается INP;
  • откладывается отрисовка;
  • появляются пропуски кадров (jank).
  • Что делать senior’у:

  • искать длинные задачи и причины;
  • уменьшать объём синхронной работы в момент взаимодействия;
  • проектировать фичи так, чтобы тяжёлая работа не попадала в критический путь.
  • Источник:

  • Long tasks API
  • Layout thrashing: частая причина «залипаний»

    Layout thrashing — паттерн, когда код чередует чтение и запись layout-зависимых свойств так, что браузер вынужден многократно пересчитывать layout.

    Примеры действий, которые могут триггерить перерасчёт:

  • чтение offsetHeight, getBoundingClientRect();
  • затем запись стилей, меняющих геометрию;
  • затем снова чтение.
  • Senior-практика:

  • группировать чтения и записи;
  • использовать requestAnimationFrame для визуальных апдейтов;
  • избегать измерений DOM в горячих путях без необходимости.
  • Источник:

  • Оптимизация анимаций и layout
  • JavaScript, React-подобные фреймворки и стоимость интерактивности

    Современные приложения часто платят за интерактивность большим количеством JS.

    Типовая проблема: много JS до того, как пользователь может действовать

    Даже если контент уже виден (LCP приемлемый), интерфейс может быть «тяжёлым»:
  • бандл большой;
  • гидрация (при SSR) занимает время;
  • много компонентов подписаны на глобальное состояние;
  • слишком много логики выполняется на старте.
  • Что обычно помогает:

  • code splitting по маршрутам и фичам;
  • отложенная инициализация второстепенного функционала;
  • уменьшение зависимости UI от глобального состояния;
  • профилирование ререндеров.
  • SSR, SSG и CSR: выбор как архитектурное решение

  • CSR (Client-Side Rendering): контент формируется в браузере. Проще инфраструктурно, но риск по LCP и по объёму JS.
  • SSR (Server-Side Rendering): сервер отдаёт HTML. Часто улучшает LCP, но может усложнить систему и увеличить стоимость гидрации.
  • SSG (Static Site Generation): HTML генерируется заранее. Отлично для контентных страниц, но не решает всё для динамических сценариев.
  • Senior-правило: не выбирать стратегию рендера «по моде». Выбор должен быть привязан к:

  • критическим сценариям (что важно пользователю);
  • данным (насколько они динамичны);
  • ограничениям команды и инфраструктуры;
  • измеримым метрикам.
  • Кэширование: как сделать быстро и стабильно

    Кэш — один из самых сильных рычагов улучшения производительности, но он требует дисциплины и понимания слоёв.

    Слои кэша

    Полезно мыслить кэшем как набором уровней:

    | Уровень | Что кэшируем | Кто управляет | Типичная цель | |---|---|---|---| | CDN/edge | статика, иногда HTML | инфраструктура | уменьшить TTFB, ускорить LCP | | HTTP cache браузера | JS/CSS/изображения/шрифты | серверные заголовки | быстрые повторные заходы | | Service Worker cache | запросы и ресурсы по правилам | фронтенд/платформа | офлайн/плохая сеть, контроль стратегии | | Кэш данных (server state) | ответы API | приложение | меньше запросов, быстрее UI | | In-memory кэш | вычисления/селекторы | приложение | ускорить CPU-пути, улучшить INP |

    HTTP-кэш: базовые правила, которые должен знать senior

    Кэширование в браузере в основном управляется заголовками ответа.

    Ключевые понятия:

  • Cache-Control: политика кэширования.
  • max-age: сколько секунд ресурс считается свежим.
  • immutable: подсказка, что ресурс не изменится (уместно для хэшированных файлов).
  • ETag и If-None-Match: условные запросы, чтобы получать 304 Not Modified.
  • Практичная схема для статики сборки:

  • файлы с контентным хэшем в имени: кэшировать долго (max-age большой, immutable);
  • HTML и конфиги: кэшировать осторожно или валидировать через ETag.
  • Источник:

  • HTTP caching (MDN)
  • CDN: ускорение и уменьшение риска деградации

    CDN даёт эффект за счёт:
  • географической близости;
  • оптимизации TLS/соединений;
  • разгрузки origin.
  • Senior-аспект: CDN — часть архитектуры и надёжности. Важно понимать:

  • как инвалидировать кэш;
  • как откатывать ошибочные заголовки кэширования;
  • какие ресурсы можно отдавать с edge.
  • Service Worker: когда нужен и чем опасен

    Service Worker позволяет перехватывать запросы и кэшировать по стратегиям:
  • cache-first;
  • network-first;
  • stale-while-revalidate.
  • Плюсы:

  • контроль над повторными заходами;
  • устойчивость на плохой сети;
  • возможность офлайн-режимов.
  • Риски:

  • сложность отладки;
  • ошибки обновления (пользователь «застрял» на старой версии);
  • несоответствие кэша и бекенда.
  • Если у команды нет зрелых процессов релиза и наблюдаемости, Service Worker легко превращается в источник инцидентов.

    Источник:

  • Service Worker API (MDN)
  • Кэш данных: избегайте «самописных» ошибок

    Если вы кэшируете данные API, важно иметь ответы на вопросы:
  • что является ключом кэша;
  • когда данные устаревают;
  • как делать инвалидации;
  • что показывать пользователю при ошибке.
  • Для server state часто выгодно использовать готовые инструменты с понятными контрактами:

  • TanStack Query
  • Senior-правило: кэш данных — часть доменной модели и UX. Его нельзя делать «втихаря» внутри отдельных компонентов.

    !Схема показывает, что кэш бывает на разных уровнях и влияет на разные метрики.

    Практический чек-лист улучшений с привязкой к метрикам

    Ниже — не «магические советы», а ориентиры, которые удобно применять через измерения.

    Улучшение LCP

  • оптимизировать hero-изображение: правильные размеры, современные форматы, srcset, разумный loading;
  • убрать лишний блокирующий JS до первого контента;
  • минимизировать блокирующий CSS, избегать тяжёлых CSS-импортов;
  • использовать CDN и корректный HTTP-кэш для статики;
  • проверить шрифты: предзагрузка критичных, избегать лишних начертаний.
  • Улучшение INP

  • найти длинные задачи в Performance профиле и убрать их из критического пути;
  • дробить тяжёлые вычисления (или переносить в Worker, если это действительно вычисления, а не работа с DOM);
  • уменьшить количество ререндеров (мемоизация, локализация состояния, правильные селекторы);
  • виртуализировать большие списки;
  • избегать layout thrashing.
  • Улучшение CLS

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

    Senior-уровень — это не только «ускорить», но и доказать, что стало лучше, и не сломать другое.

    Быстрый набор инструментов

  • Lighthouse: лабораторные метрики, рекомендации.
  • Chrome DevTools Performance: профилирование main thread, long tasks, кадры.
  • WebPageTest: тесты на разных устройствах/сетях, waterfall.
  • CrUX: полевые данные по происхождению.
  • Как интерпретировать результаты по-взрослому

  • не сравнивать цифры без одинаковых условий;
  • всегда фиксировать сценарий (страница, маршрут, тип пользователя, кэш тёплый/холодный);
  • помнить, что оптимизация может улучшить одну метрику и ухудшить другую (например, агрессивная предзагрузка может ухудшить конкуренцию за сеть).
  • Производительность как часть архитектуры и процесса

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

    Бюджеты производительности

    Бюджет — договорённость, что вы не превышаете некоторые пределы (размер JS, количество запросов, время выполнения).

    Senior-подход:

  • выбрать 1–2 бюджета, которые реально контролировать;
  • встроить проверку в CI (например, Lighthouse CI);
  • договориться, что превышение — это осознанное решение с объяснением.
  • Источник:

  • Lighthouse CI
  • Наблюдаемость в продакшене

    Чтобы performance не деградировал незаметно:
  • отправляйте CWV метрики в аналитику как RUM (с сегментацией по устройствам, маршрутам, релизам);
  • связывайте метрики с изменениями (релизами) и инцидентами;
  • держите простую процедуру расследования: что изменилось, где регресс, какой компонент или запрос виноват.
  • Практическая библиотека для сбора метрик:

  • web-vitals
  • Связь с модульностью и контрактами

    Архитектурные идеи из прошлой статьи напрямую помогают производительности:
  • границы фич упрощают code splitting и ленивую загрузку;
  • единая инфраструктура запросов и кэширования уменьшает дубли и гонки;
  • дизайн-система снижает риск «дорогих» компонентов без контроля.
  • TypeScript-оптика тоже применима:

  • стабильные типизированные контракты событий аналитики помогают собирать performance-данные без хаоса;
  • типизированные конфиги роутов и фича-флагов уменьшают случайные загрузки не того, что нужно.
  • Типовые ошибки и как их избегать

  • оптимизация без измерений: много работы, мало эффекта;
  • «микрооптимизации» вместо устранения больших затрат (тяжёлые изображения, блокирующие ресурсы, long tasks);
  • агрессивный кэш без стратегии обновления;
  • перенос бизнес-логики в UI-компоненты (дорогие ререндеры, сложная диагностика);
  • отсутствие владельца метрик: «это само как-то должно быть быстрым».
  • Итог

    Производительность на senior-уровне — это управляемая система:
  • Core Web Vitals задают общий язык про пользовательское восприятие: LCP, INP, CLS;
  • понимание рендеринга помогает находить реальные узкие места: main thread, long tasks, layout/paint;
  • кэширование — рычаг, который работает на разных слоях, но требует дисциплины;
  • измерения должны быть и в лаборатории, и в продакшене;
  • архитектура, инфраструктура и процессы определяют, будет ли производительность улучшаться со временем или деградировать.
  • 5. Качество: тестирование, CI/CD, наблюдаемость

    Качество: тестирование, CI/CD, наблюдаемость

    Связь с предыдущими темами курса

    В теме про роль Senior мы определили, что senior создаёт ценность через предсказуемость результата и снижение рисков. В теме про архитектуру — что границы модулей, публичные API и инфраструктурные решения делают систему эволюционной. В теме про TypeScript — что контракты уменьшают стоимость изменений. В теме про производительность — что метрики и измерения важнее «оптимизации на глаз».

    Качество объединяет всё это в одну практику поставки:

  • тестирование снижает риск регрессий;
  • CI/CD делает поставку повторяемой и управляемой;
  • наблюдаемость позволяет увидеть последствия в продакшене и быстро локализовать проблему.
  • Senior-оптика здесь простая: качество — это не «больше тестов», а система обратной связи, которая делает изменения безопасными и быстрыми.

    Что такое качество во фронтенде

    Качество фронтенд-продукта — это способность системы:
  • корректно работать в ключевых сценариях;
  • изменяться без непредсказуемых регрессий;
  • быть измеряемой и диагностируемой в продакшене;
  • поддерживать скорость команды.
  • Полезно разделять качество на несколько измерений:

  • Функциональная корректность: приложение делает то, что нужно.
  • Надёжность поставки: релизы не превращаются в лотерею.
  • Наблюдаемость: если что-то сломалось, вы быстро понимаете что и почему.
  • UX-качество: доступность, производительность, стабильность поведения.
  • Типичная ошибка — считать качество исключительно задачей QA. На senior-уровне качество — это инженерная система, где QA, разработка, продукт и платформа работают вместе.

    Тестирование: что и зачем мы проверяем

    Термины

  • Регрессия — новое изменение ломает существующий сценарий.
  • Юнит-тест (unit test) — проверка небольшой единицы логики в изоляции.
  • Интеграционный тест (integration test) — проверка взаимодействия нескольких частей системы (например, компонент + стор + роутинг + сетевой слой).
  • E2E-тест (end-to-end) — проверка пользовательского сценария в максимально близком к реальности окружении.
  • Флейковый тест (flaky test) — тест, который иногда падает без изменения кода.
  • Пирамида тестов как практичная эвристика

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

  • На нижнем уровне много быстрых проверок логики.
  • На среднем уровне — тесты сценариев на уровне UI без настоящего бекенда.
  • На верхнем уровне — немного E2E для критических цепочек.
  • Важно: это не догма. Senior подход — балансировать покрытие по рискам.

    !Пирамида тестов как способ распределять проверки по стоимости и рискам

    Юнит-тесты: где они дают максимум пользы

    Юнит-тесты особенно полезны для:
  • чистых функций преобразования данных;
  • бизнес-правил (валидации, вычисления, маппинги);
  • утилит инфраструктуры (например, форматирование ошибок, построение query-key).
  • Практика senior-уровня:

  • не тестировать детали реализации, тестировать контракт;
  • не превращать юнит-тесты в тесты DOM;
  • держать тесты быстрыми и детерминированными.
  • Инструменты:

  • Jest
  • Vitest
  • Компонентные и интеграционные тесты: основная рабочая лошадка

    Для большинства продуктовых команд максимальный эффект дают тесты пользовательских сценариев на уровне компонентов:
  • рендерим компонент/страницу;
  • эмулируем действия пользователя;
  • проверяем поведение и видимый результат.
  • Ключевой принцип: тесты должны быть ближе к тому, как пользователь взаимодействует с UI.

    Библиотека и подход:

  • Testing Library
  • Моки сети: почему мокать лучше на уровне HTTP, а не функций

    Если вы мокаете внутренние функции (например, напрямую подменяете клиент), вы рискуете:
  • закрепить тесты за деталями реализации;
  • потерять проверку контрактов запроса.
  • Более устойчивый подход — мокать сеть на уровне запросов, сохраняя реалистичность:

  • стабильно воспроизводимые ответы;
  • проверяем корректные параметры запроса;
  • минимально зависим от внутренней структуры кода.
  • Инструмент:

  • Mock Service Worker (MSW)
  • Контрактные тесты: как не ломать интеграции между командами

    Если фронтенд и бекенд развиваются параллельно, регрессии часто появляются не в UI, а в контракте:
  • поле стало необязательным;
  • изменился формат даты;
  • поменялась семантика статусов.
  • Варианты решения:

  • общие схемы и рантайм-валидация на границе (например, схемы данных);
  • контрактное тестирование провайдер/консьюмер.
  • Инструмент для контрактных тестов:

  • Pact
  • E2E: проверяем критические цепочки, а не всё подряд

    E2E имеет смысл для:
  • логина;
  • покупки/оплаты;
  • критических админ-операций;
  • сценариев, где важна реальная интеграция нескольких систем.
  • Типовая стратегия:

  • небольшой набор E2E на самые дорогие для бизнеса сценарии;
  • всё остальное закрывается интеграционными тестами.
  • Инструменты:

  • Playwright
  • Cypress
  • Визуальные регрессионные тесты

    Визуальные тесты полезны, когда дизайн-система и продукт боятся:
  • незаметных сдвигов отступов;
  • поломки состояний компонентов;
  • некорректных тем (светлая/тёмная).
  • Обычно это делается через снапшоты UI-историй.

    Инструменты и экосистема:

  • Storybook
  • Chromatic
  • Что считать хорошим тестом

    Хороший тест:
  • устойчив к рефакторингу;
  • проверяет поведение, а не внутренности;
  • даёт ясную диагностику при падении;
  • не зависит от случайных таймингов.
  • Плохой тест:

  • завязан на структуру DOM и CSS-классы без необходимости;
  • использует ожидания вида «подождать 5 секунд»;
  • зависит от внешних сервисов без контроля.
  • CI: непрерывная интеграция как система предотвращения регрессий

    Термины

  • CI (Continuous Integration) — автоматические проверки при каждом изменении (обычно на PR).
  • Quality gate — правило, что в основную ветку попадает только то, что прошло набор проверок.
  • Цель CI: сделать так, чтобы код без базового уровня качества не мог быть слит.

    Типовой набор проверок в CI для фронтенда

    Минимальный практичный набор:
  • установка зависимостей с воспроизводимостью (lockfile);
  • линтинг (включая TypeScript проверки);
  • юнит/интеграционные тесты;
  • сборка (чтобы не было сюрпризов на релизе);
  • при необходимости: E2E или их поднабор;
  • при необходимости: проверка производительности (например, Lighthouse CI) и размер бандлов.
  • Инструменты CI:

  • GitHub Actions
  • GitLab CI/CD
  • Практики, которые делают CI полезным, а не формальным

  • Делайте CI быстрым: быстрый feedback снижает стоимость качества.
  • Параллельте независимые шаги.
  • Кэшируйте зависимости и артефакты сборки.
  • Отдельно выделяйте быстрый набор проверок для PR и более тяжёлый набор для ночных прогонов.
  • Боритесь с флейками как с инцидентами: флейки подрывают доверие к системе.
  • !Типовой CI пайплайн и точка контроля качества перед merge

    CD: непрерывная доставка и управляемые релизы

    Термины

  • CD (Continuous Delivery/Deployment) — автоматизация доставки в окружения и, в идеале, выкладка в продакшен.
  • Артефакт — собранный результат (например, статические файлы), который деплоится одинаково в разные окружения.
  • Фича-флаг (feature flag) — переключатель, который включает функциональность для части пользователей или окружений.
  • Канареечный релиз (canary) — выкладка на небольшую долю пользователей для снижения риска.
  • Senior-цель CD: сделать релиз рутинной операцией, а не событием.

    Типовые модели релиза для фронтенда

  • Trunk-based: короткоживущие ветки, частые слияния, фича-флаги для незавершённых фич.
  • Release branches: отдельные ветки релиза, уместно при более тяжёлых циклах и требованиях к стабилизации.
  • Выбор зависит от:

  • частоты поставки;
  • зрелости тестов и наблюдаемости;
  • требований комплаенса.
  • Фича-флаги как инструмент снижения риска

    Фича-флаги помогают:
  • выкатывать код без включения функциональности;
  • делать постепенный rollout;
  • быстро отключать проблемную фичу без отката всего релиза.
  • Риски фича-флагов:

  • «кладбище флагов», когда старые флаги не удаляются;
  • усложнение логики (ветвления в коде);
  • разрастание матрицы тестирования.
  • Senior-практика:

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

    Для библиотек (дизайн-система, общие пакеты) важны:
  • предсказуемые релизы;
  • депрекации;
  • миграционные гайды.
  • Здесь напрямую применяются идеи из темы про архитектуру и публичные API.

    Наблюдаемость: как понимать, что происходит в продакшене

    Термины

  • Наблюдаемость — способность ответить на вопрос «что произошло и почему» по сигналам системы.
  • Логи — события и сообщения (например, ошибки, важные переходы состояний).
  • Метрики — числовые измерения во времени (например, доля ошибок, INP по маршрутам).
  • Трейсы — связанная цепочка операций, помогающая увидеть путь запроса/действия.
  • RUM (Real User Monitoring) — сбор данных производительности и ошибок от реальных пользователей.
  • Три сигнала наблюдаемости и их роль во фронтенде

  • Ошибки и исключения: что ломается и у кого.
  • Производительность: как это ощущает пользователь (Core Web Vitals, время загрузки маршрутов).
  • Бизнес-события: что пользователь сделал (или не смог сделать), и где воронка ломается.
  • Инструменты:

  • Sentry
  • OpenTelemetry
  • web-vitals
  • Что именно стоит инструментировать в продукте

    Практичный минимум:
  • сбор ошибок JavaScript и unhandled promise rejections;
  • сбор CWV метрик (LCP, INP, CLS) с привязкой к маршруту и версии релиза;
  • измерение критических пользовательских действий (например, «поиск выполнен», «оплата начата/успешна/ошибка»);
  • идентификаторы корреляции, чтобы связывать события фронтенда с бекендом (например, requestId).
  • Важно: наблюдаемость должна быть типизирована и стандартизирована.

    Пример подхода с контрактом событий аналитики:

    Такой контракт снижает хаос в названиях событий и помогает поддерживать обратную совместимость.

    !Качество как замкнутый цикл обратной связи от кода до продакшена

    Алерты и дашборды: чем senior отличается от «просто всё логировать»

    Типовые ошибки:
  • собирать данные, но не смотреть;
  • алертить на всё и получить «усталость от алертов»;
  • не иметь привязки к релизу и маршруту.
  • Senior-практика:

  • алерты только на то, что требует реакции;
  • сегментация по платформам (mobile/desktop), браузерам, маршрутам;
  • обязательная метка версии релиза;
  • дашборды по ключевым сценариям и метрикам продукта.
  • Инциденты и разбор причин как часть системы качества

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

    Разбор причин (postmortem) — это документ и процесс, который отвечает:

  • что произошло;
  • какой был эффект;
  • почему это стало возможным (не один «виноватый», а цепочка причин);
  • какие действия предотвращают повторение.
  • Senior-правило: постмортем должен приводить к системным изменениям, например:

  • добавить проверку в CI;
  • добавить алерт или дашборд;
  • уточнить контракт API;
  • внедрить фича-флаг или стратегию постепенного rollout.
  • Практичная стратегия качества для команды

    Ниже — рабочая схема, которую можно адаптировать под большинство продуктов.

    Договориться о «что критично»

  • ключевые пользовательские сценарии;
  • зоны риска (оплата, авторизация, права доступа);
  • метрики, которые нельзя ухудшать (например, CWV, доля ошибок).
  • Зафиксировать уровни проверок

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

  • обязательные проверки на PR;
  • деплой через один и тот же артефакт;
  • фича-флаги для рискованных изменений;
  • наблюдаемость по релизам.
  • Удерживать стоимость

  • борьба с флейками;
  • удаление устаревших тестов;
  • регулярная ревизия того, что реально ловит дефекты.
  • Типовые ошибки и как их избегать

  • Покрытие ради покрытия: метрика процентов не равна снижению риска.
  • Слишком много E2E: дорого и хрупко, команда начинает игнорировать падения.
  • Нет единого контракта событий: аналитика и диагностика становятся шумом.
  • CI медленный и нестабильный: качество превращается в формальность.
  • Нет связи релизов и метрик: регрессии находят пользователи, а не команда.
  • Итог

    Качество на senior-уровне — это связанная система:
  • тесты ловят регрессии на разных уровнях стоимости;
  • CI превращает качество в повторяемый процесс и не пускает плохие изменения в main;
  • CD делает релизы управляемыми через артефакты, фича-флаги и постепенный rollout;
  • наблюдаемость показывает реальный эффект в продакшене и сокращает время диагностики;
  • постмортемы превращают инциденты в улучшения системы.
  • Если архитектура отвечает на вопрос «как системе жить долго», то качество отвечает на вопрос «как системе безопасно меняться каждый день».

    6. Безопасность и надежность в веб-приложениях

    Безопасность и надежность в веб-приложениях

    Связь с предыдущими темами курса

    В прошлых статьях мы выстроили основу senior-подхода:

  • Роль Senior — это ответственность за последствия и управление рисками.
  • Архитектура и дизайн-система — это границы, публичные API и предсказуемость изменений.
  • TypeScript — это контракты, которые уменьшают хаос и стоимость рефакторинга.
  • Производительность — это измеримые метрики, критический путь и дисциплина кэша.
  • Качество — это система обратной связи: тестирование, CI/CD, наблюдаемость.
  • Безопасность и надежность — логическое продолжение: это способность продукта не причинять вред и стабильно работать под реальными нагрузками и сбоями. На senior-уровне это не набор «советов по OWASP», а инженерная система, встроенная в архитектуру, процессы релиза и наблюдаемость.

    Полезная база терминов и угроз:

  • OWASP Top 10
  • MDN Web Security
  • Базовые определения

  • Уязвимость — дефект, который позволяет атакующему нарушить конфиденциальность, целостность или доступность.
  • Угроза — потенциальный сценарий атаки или вреда.
  • Атака — фактическая эксплуатация уязвимости.
  • Поверхность атаки — все точки входа, через которые можно повлиять на систему: UI, параметры URL, API, сторонние скрипты, cookies, localStorage.
  • Надежность — способность системы корректно работать при ошибках сети, деградации зависимостей, частичных отказах и неожиданных входных данных.
  • Сеньорская установка: безопасность и надежность начинаются с явных границ и контрактов, а не с «героического» фикса багов перед релизом.

    !Диаграмма связывает безопасность и надежность через цели CIA и реальные сценарии

    Модель мышления Senior: угроза, риск, контроль

    Взрослая инженерная модель строится вокруг трёх вопросов:

  • Что мы защищаем: аккаунты, платежи, персональные данные, админ-функции, репутацию.
  • От кого защищаем: случайные ошибки, злоумышленники, боты, компрометированные зависимости.
  • Как измеряем эффект: частота инцидентов, время обнаружения, время восстановления, доля затронутых пользователей.
  • Мини-угрозмоделирование для фронтенда

    Для фронтенда удобно начать с простого шаблона:

  • Активы: токен доступа, пользовательские данные, права доступа, платежные действия.
  • Точки входа: формы, query-параметры, deep links, iframe-встраивания, сторонние виджеты, события постMessage.
  • Границы доверия: браузер пользователя, ваш домен, API домен, CDN, сторонние домены.
  • Злоупотребления: подмена контента, запуск чужого JS, кража токена, несанкционированный запрос, UI-обман.
  • Практика: фиксируйте результат в коротком ADR, как мы обсуждали в теме архитектуры.

    Аутентификация и сессии: где фронтенд часто ошибается

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

    Где хранить токены и почему это важно

    Главный конфликт: удобство разработки против устойчивости к XSS.

    | Подход | Плюсы | Минусы | Когда уместно | |---|---|---|---| | Cookie с HttpOnly | Токен недоступен JS, лучше защищает от кражи при XSS | Нужно правильно настроить CSRF-защиту, SameSite, домены | Классические сессии, BFF, сайты с формами | | localStorage | Просто в реализации | При XSS токен читается и утекает, высокий риск | Почти всегда нежелательно для чувствительных токенов | | Память (in-memory) | Токен не переживает перезагрузку, меньше риск утечки из хранилища | Нужно уметь обновлять сессию, возможны разлогины | SPA с короткоживущими токенами и refresh-механикой |

    Сеньорское правило: если вы не уверены, что XSS исключён, не храните долгоживущие секреты там, где их может прочитать JS.

    Дополнительно про cookies и атрибуты:

  • HttpOnly — JS не может прочитать cookie.
  • Secure — cookie отправляется только по HTTPS.
  • SameSite — ограничивает отправку cookie в кросс-сайтовых контекстах.
  • Документация:

  • MDN Set-Cookie
  • MDN SameSite cookies
  • Авторизация: фронтенд не является источником истины

    Типичная ошибка: считать, что скрыть кнопку или закрыть роут на фронтенде — это и есть защита.

    Правильная модель:

  • Фронтенд может улучшать UX и снижать случайные ошибки.
  • Источник истины для прав доступа — сервер.
  • Фронтенд должен корректно обрабатывать 401 и 403, не «падать» и не показывать приватные данные из кэша.
  • XSS: главная угроза фронтенда

    XSS (Cross-Site Scripting) — ситуация, когда атакующий добивается выполнения своего JavaScript в контексте вашего сайта.

    Откуда берётся XSS

    Чаще всего:

  • небезопасный вывод пользовательского ввода в HTML;
  • использование innerHTML и аналогов без санации;
  • небезопасные шаблоны в SSR;
  • уязвимые сторонние виджеты.
  • Документация:

  • OWASP Cross Site Scripting Prevention Cheat Sheet
  • Защита от XSS на практике

  • Не используйте innerHTML, если можно собрать DOM безопасно.
  • Для неизбежного HTML используйте санацию с проверенной библиотекой и строгой политикой.
  • Экранируйте данные при выводе в разные контексты: HTML, атрибуты, URL, JS-строки.
  • Ограничивайте возможности выполнения скриптов через CSP.
  • CSP как «страховка», но не единственная защита

    CSP (Content Security Policy) — заголовок, который ограничивает источники скриптов, стилей, изображений и других ресурсов.

  • CSP уменьшает ущерб от части XSS, но не заменяет правильную обработку данных.
  • CSP особенно полезен для контроля сторонних скриптов.
  • Документация:

  • MDN Content-Security-Policy
  • !Иллюстрация показывает, где XSS возникает и какие есть точки контроля

    CSRF и CORS: две разные вещи, которые часто путают

    CSRF

    CSRF (Cross-Site Request Forgery) — атака, при которой браузер пользователя отправляет запрос на ваш сайт с его cookie, но без его намерения.

    CSRF актуален прежде всего для cookie-based сессий.

    Типовые меры:

  • SameSite=Lax или SameSite=Strict, где возможно.
  • CSRF-токен (обычно в заголовке или теле) и проверка на сервере.
  • Проверка Origin и Referer на сервере для чувствительных операций.
  • Источник:

  • OWASP CSRF Prevention Cheat Sheet
  • CORS

    CORS (Cross-Origin Resource Sharing) — механизм браузера, который контролирует, можно ли JS-коду с одного origin читать ответы другого origin.

    Важно:

  • CORS не является «серверной авторизацией».
  • Неправильный CORS может открыть чтение данных третьими сайтами.
  • Документация:

  • MDN CORS
  • Clickjacking и защита UI

    Clickjacking — атака, когда ваш интерфейс встраивают в невидимый или маскирующий iframe, чтобы пользователь кликал «не туда».

    Типовые меры:

  • X-Frame-Options: DENY или SAMEORIGIN.
  • CSP frame-ancestors.
  • Документация:

  • MDN X-Frame-Options
  • Безопасная работа с внешними данными: TypeScript не достаточно

    Из темы про TypeScript ключевой вывод: типы защищают ваш код, но не гарантируют корректность данных на входе.

    Практика для безопасности и надежности:

  • На границах системы принимайте данные как unknown.
  • Валидируйте и нормализуйте ответы API и данные из localStorage.
  • Держите слой адаптеров DTO в доменную модель.
  • Это одновременно:

  • уменьшает риск падений от неожиданных данных;
  • снижает вероятность логических уязвимостей (например, неправильных прав/статусов);
  • улучшает наблюдаемость, потому что ошибки становятся классифицируемыми.
  • Supply chain и зависимости: реальная угроза для фронтенда

    Большая часть фронтенд-приложения — это зависимости: сборка, транспайлеры, UI-библиотеки, SDK аналитики.

    Риски:

  • компрометация пакета или его владельца;
  • вредоносное обновление;
  • внедрение трекеров и утечек данных;
  • уязвимости в транзитивных зависимостях.
  • Практики senior-уровня:

  • Lockfile обязателен и должен быть в репозитории.
  • Обновления зависимостей должны быть управляемыми и наблюдаемыми.
  • Для критичных пакетов — ревью чейнджлогов и репутации.
  • Автоматическая проверка уязвимостей в CI.
  • Реальные источники:

  • npm audit
  • GitHub Dependabot
  • SRI для внешних скриптов

    Subresource Integrity помогает убедиться, что внешний ресурс (например, скрипт с CDN) не был подменён.

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

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

  • MDN Subresource Integrity
  • Надежность фронтенда: проектируем поведение при сбоях

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

    Типовые классы сбоев

  • Сеть: таймауты, потери пакетов, медленные ответы.
  • API: частичные ошибки, несовместимые изменения контрактов.
  • Клиент: низкая память, медленный CPU, фоновые вкладки.
  • Зависимости: падение CDN, проблемы сторонних SDK.
  • Принципы надежного UX

  • Graceful degradation — ухудшаемся аккуратно: показываем упрощённый интерфейс, но не ломаем сценарий полностью.
  • Fail closed для безопасности — при сомнениях блокируем опасное действие.
  • Fail open для неопасных вещей — например, если аналитика недоступна, продукт всё равно должен работать.
  • Ошибки и исключения: делаем их управляемыми

    Из темы про качество: наблюдаемость и постмортемы превращают сбои в улучшения.

    Практики для фронтенда:

  • Централизованный обработчик ошибок и unhandled promise rejections.
  • Классификация ошибок: сеть, валидация, права, неизвестное.
  • Error boundary для UI, чтобы не «ронять» всё приложение.
  • Понятные сообщения пользователю без утечки чувствительной информации.
  • Инструменты:

  • Sentry for JavaScript
  • Сетевой слой: таймауты, отмена, ретраи и идемпотентность

    Фронтенд часто «сам» делает приложение ненадёжным, если не контролирует сетевые сценарии.

    Таймауты и отмена

  • У запросов должен быть таймаут.
  • При смене маршрута или закрытии модалки запросы стоит отменять, чтобы не обновлять состояние «после ухода».
  • Практичная база:

  • AbortController
  • Ретраи и backoff

    Ретраи полезны для временных сетевых проблем, но опасны для операций, которые нельзя повторять без последствий.

    Сеньорская логика:

  • Ретрай уместен для идемпотентных запросов, чаще GET.
  • Для операций оплаты, создания заказа, списания — нужна идемпотентность на сервере и явная политика повторов.
  • Документация по устойчивым паттернам:

  • Google Cloud Architecture: Retry and exponential backoff
  • Кэш и безопасность: когда ускорение создаёт риск

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

    Безопасностные аспекты кэша:

  • Не кэшируйте персональные данные в общедоступных кэшах.
  • Аккуратно используйте Service Worker: он может «закрепить» старую версию приложения и политики безопасности.
  • Учитывайте, что кэшированные ответы могут пережить смену пользователя на одном устройстве.
  • Документация:

  • MDN HTTP caching
  • MDN Service Worker API
  • Логи и аналитика: не утекать данными

    Наблюдаемость полезна, пока она не превращается в канал утечек.

    Риски:

  • токены, email, телефоны, адреса в логах;
  • данные форм в событиях аналитики;
  • stacktrace с чувствительными параметрами URL.
  • Практики:

  • Маскирование и редактирование данных перед отправкой.
  • Явная схема допустимых полей в событиях.
  • Разделение окружений и ключей доступа (dev, staging, prod).
  • Сеньорская связка с TypeScript: типизируйте события и запрещайте отправку лишних полей через контракт.

    Безопасность в процессе: как сделать её повторяемой

    Как и качество, безопасность должна быть частью процесса, а не «разовой проверкой».

    Что стоит встроить в CI

  • SAST-линтинг для типовых ошибок (например, небезопасные паттерны).
  • Проверка зависимостей и уязвимостей.
  • Проверка CSP и security headers на staging как часть smoke.
  • Источники:

  • ESLint
  • OWASP Cheat Sheet Series
  • Релизы, фича-флаги и снижение blast radius

    Из темы про CD:

  • Фича-флаги позволяют быстро выключить рискованную фичу без отката всего релиза.
  • Канареечные релизы уменьшают долю пользователей под ударом.
  • Наблюдаемость по версиям релиза ускоряет диагностику.
  • Практичный чек-лист для senior-ревью

    На уровне кода

  • Нет небезопасного HTML-рендера без санации.
  • Нет хранения чувствительных токенов в localStorage без ясного обоснования.
  • Ошибки обрабатываются, есть понятные состояния UI.
  • Сетевой слой имеет таймауты, отмену, предсказуемую политику ретраев.
  • На уровне платформы

  • Включён HTTPS, правильные cookie-атрибуты.
  • Настроены CSP и защита от iframe-встраивания.
  • В CI есть проверка зависимостей.
  • На уровне процесса

  • Есть минимальная угрозмодель для критичных сценариев.
  • Есть runbook: что делать при утечке, XSS-инциденте, массовых 401.
  • Есть метрики: доля ошибок, скорость восстановления, сегментация по релизам.
  • Итог

    Безопасность и надежность на senior-уровне — это инженерная система, а не набор частных советов:

  • безопасность строится вокруг границ доверия, защиты от XSS, корректной работы с сессиями и зависимостями;
  • надежность обеспечивается продуманным сетевым слоем, управляемыми ошибками, корректным кэшированием и graceful degradation;
  • процессы из тем про CI/CD и наблюдаемость делают безопасность и надежность повторяемыми: проверки в CI, управляемые релизы, метрики в продакшене и постмортемы.
  • В результате команда получает не просто «менее уязвимый» фронтенд, а продукт, который предсказуемо развивается и сохраняет доверие пользователей.

    7. Техлидство: code review, коммуникация, принятие решений

    Техлидство: code review, коммуникация, принятие решений

    Связь с предыдущими темами курса

    В предыдущих статьях мы выстроили основу senior-подхода:

  • Роль Senior — это ответственность за результат и снижение рисков.
  • Архитектура и дизайн-система — это границы, публичные API и эволюционность.
  • TypeScript — это контракты, которые уменьшают хаос.
  • Производительность — это метрики и управление критическим путём.
  • Качество — это система обратной связи: тестирование, CI/CD, наблюдаемость.
  • Безопасность и надёжность — это контроль рисков на границах доверия и при сбоях.
  • Техлидство соединяет эти темы в ежедневную практику команды. Это не должность и не набор встреч, а способность:

  • превращать неопределённость в план действий;
  • принимать решения так, чтобы система оставалась изменяемой;
  • выстраивать коммуникацию, которая предотвращает инциденты, регрессии и “разъезд” ожиданий;
  • делать качество и безопасность повторяемыми, а не героическими.
  • В этой статье разберём три ключевых инструмента техлида на фронтенде: code review, коммуникацию и принятие решений.

    !Три опоры техлидства и их связь с управлением рисками

    Что такое техлидство во фронтенде

    Уровень, роль и ожидания

    Важно не путать:

  • Senior как уровень — умеете брать ответственность за результат, улучшать систему и снижать риски.
  • Tech Lead как роль — координируете техническую сторону поставки, согласуете решения, делаете команду эффективнее.
  • Техлидство может быть:

  • формальной ролью;
  • временной ответственностью на инициативу;
  • “мышцей”, которую senior применяет в любой команде.
  • Три результата техлидства

    Хорошее техлидство почти всегда проявляется в трёх наблюдаемых результатах:

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

    Зачем нужен code review на senior-уровне

    Code review — это не “проверка кода на ошибки”. Это механизм управления рисками:

  • предотвращает регрессии и уязвимости;
  • удерживает архитектурные границы и публичные API;
  • выравнивает стандарты качества и DX;
  • распространяет контекст и знания.
  • Полезная база практик:

  • Google Engineering Practices: Code Review
  • Что именно проверять в ревью

    Удобно мыслить ревью как проверку нескольких “контуров”. Ниже не чек-лист “на всё”, а карта внимания.

    #### Корректность и состояние системы Проверяйте:

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

    #### Архитектурные границы Проверяйте:

  • направление зависимостей между слоями;
  • отсутствие “глубоких импортов” и протекания внутренностей модулей;
  • корректную локализацию бизнес-логики;
  • отсутствие случайного связывания фич через общий глобальный стор без правил.
  • Если в курсе про архитектуру вы приняли модель модулей, то ревью — способ сделать её исполнимой.

    #### Контракты данных и TypeScript-дисциплина Проверяйте:

  • что на границах системы входные данные не считаются “по умолчанию правильными”;
  • что any не разносит неизвестность по проекту;
  • что DTO отделены от доменной модели и UI-форматов;
  • что добавленные типы уменьшают сложность, а не маскируют её.
  • #### Производительность и UX Проверяйте:

  • влияние на LCP, INP, CLS в критичных местах;
  • появление тяжёлых вычислений на main thread;
  • добавление крупной зависимости или рост бандла;
  • ненужные перерисовки и избыточные подписки на состояние.
  • #### Безопасность Проверяйте:

  • отсутствие небезопасного HTML-рендера без санации;
  • корректную работу с токенами и приватными данными;
  • отсутствие утечки PII в логи и аналитику;
  • корректную обработку 401 и 403 без показа приватных данных из кэша.
  • Как делать ревью эффективным: “малые PR” и понятные цели

    #### Размер PR как управляемый риск Чем больше PR, тем выше вероятность:

  • пропустить проблему;
  • потерять контекст;
  • затянуть цикл обратной связи.
  • Практика техлида:

  • просить разбивать изменения на поставляемые части;
  • разрешать “подготовительные PR” без функционального эффекта, если они снижают риск следующего шага;
  • поощрять фича-флаги для безопасной поэтапной интеграции.
  • #### В ревью должна быть ясная цель Перед ревью полезно требовать от автора краткое описание:

  • что меняется для пользователя;
  • какие риски и как они снижены;
  • как проверить;
  • что затронуто (архитектура, перформанс, безопасность).
  • Это уменьшает “угадывание” и делает ревью быстрее.

    Стиль комментариев: как не демотивировать и не размыть качество

    Ревью — коммуникация. И здесь у техлида есть задача: сохранять качество без токсичности.

    #### Классифицируйте замечания Снижает конфликты простое правило: помечайте тип замечания.

  • Блокер: нельзя мёржить, риск высок.
  • Важно: лучше исправить сейчас, но можно договориться.
  • Нит: стиль или мелочь, не про риск.
  • Вопрос: уточнение контекста.
  • Это помогает автору понять приоритет и не тонуть в равнозначных комментариях.

    #### Пишите комментарии как предложение, а не как приговор Плохой паттерн:

  • “Сделано неправильно, переделай.”
  • Лучше:

  • “Здесь риск регрессии: состояние может стать неконсистентным при X. Можно ли смоделировать это как дискриминируемое объединение и добавить исчерпывающую проверку?”
  • То есть:

  • называем риск;
  • объясняем “почему”;
  • предлагаем вариант.
  • #### Автоматизируйте вкус, оставьте ревью для смысла Если команда спорит о форматировании и импортах, техлид должен “вынести вкус” в инструменты:

  • линтер;
  • автоформаттер;
  • хуки;
  • стандартные конфиги.
  • Тогда ревью остаётся для того, что реально снижает риски.

    Как техлиду строить процесс ревью

    #### Определите SLA ревью Ревью без ожиданий часто превращается в очередь. Простая договорённость помогает:

  • целевое время первого ответа;
  • правило “не блокировать критичные фиксы”;
  • приоритеты (инциденты, релизы, фичи).
  • #### Ротация и владение доменами Если одна область держится на одном человеке, это риск. Практики:

  • назначение ответственных по доменам;
  • парные ревью на критичные изменения;
  • постепенное расширение “зоны владения” у middle-разработчиков.
  • Коммуникация: управлять ожиданиями и снижать неопределённость

    Коммуникация как часть инженерной работы

    Техлидство ломается не на “неправильном React”, а на:

  • несогласованных ожиданиях;
  • скрытых рисках;
  • неявных предположениях;
  • потерянном контексте.
  • Коммуникация — это механизм сделать предположения видимыми.

    Ключевые каналы коммуникации во фронтенде

    #### С продуктом Техлид помогает команде получить ясность:

  • какие сценарии критичны;
  • какие метрики важны;
  • какие компромиссы допустимы.
  • Практика: фиксировать критерии готовности и критерии приёмки до реализации.

    #### С дизайном Фокус на:

  • согласование поведения состояний (ошибки, пустые, загрузка);
  • соответствие дизайн-системе и токенам;
  • доступность как часть “готово”, а не отдельная задача.
  • #### С бекендом Техлид должен сделать интеграцию предсказуемой:

  • договориться о контракте API и версионировании;
  • определить стратегию деградации при частичных ошибках;
  • синхронизировать изменения, которые могут сломать фронтенд.
  • #### С QA и поддержкой Цель:

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

    Техлидство “масштабируется письмом”. Несколько практичных артефактов:

  • короткий дизайн-док или RFC для изменений от нескольких дней;
  • ADR для архитектурных решений;
  • release notes для команды и стейкхолдеров;
  • runbook для инцидентов и типовых сбоев.
  • Ссылки по ADR:

  • Architecture Decision Records
  • Как проводить технические обсуждения без хаоса

    #### Договоритесь о формате Техлид заранее задаёт рамку:

  • цель обсуждения;
  • входные данные и ограничения;
  • варианты решений;
  • критерии выбора;
  • кто принимает решение и когда.
  • #### Работайте с “неявными” рисками Часто конфликт в обсуждении — это конфликт скрытых критериев:

  • “быстрее релиз” против “дешевле сопровождение”;
  • “гибкость” против “простота”;
  • “идеально” против “достаточно хорошо”.
  • Техлид делает критерии явными и фиксирует компромисс.

    #### Эскалация — это нормально Если компромисс влияет на бизнес и сроки, техлид:

  • формулирует варианты с последствиями;
  • выносит решение на уровень, где есть полномочия.
  • Это тоже ответственность, а не “перекладывание”.

    Принятие решений: от неопределённости к выбранному пути

    Почему решения важнее “правильности”

    Большие системы умирают от:

  • отсутствия решений;
  • непоследовательных решений;
  • решений, которые никто не помнит и не может объяснить.
  • Техлидство — это умение выбирать и фиксировать.

    Лёгкий процесс принятия решений

    Ниже — рабочий минимальный процесс для большинства фронтенд-команд.

    #### Сформулировать проблему и границы Нужно зафиксировать:

  • что болит и где метрика/риск;
  • какие ограничения (сроки, команда, инфраструктура, безопасность);
  • что считается успехом.
  • #### Собрать варианты Обычно достаточно 2–3 вариантов, чтобы избежать “ложной дихотомии”.

    #### Выбрать критерии Примеры критериев:

  • влияние на поставку и скорость изменений;
  • риск регрессий и инцидентов;
  • стоимость сопровождения;
  • влияние на Core Web Vitals;
  • совместимость с дизайн-системой;
  • безопасность.
  • #### Принять решение и записать последствия Фиксация решения — часть решения. Удобный формат — ADR:

  • контекст;
  • варианты;
  • решение;
  • последствия и риски;
  • план внедрения и миграции.
  • Кто принимает решение: ясность важнее схемы

    У команды должен быть понятный ответ на вопрос: “кто решает в спорной ситуации?”. Возможные модели:

  • техлид принимает решение после обсуждения;
  • владелец домена принимает решение;
  • архитектурная группа для кросс-командных решений.
  • Сеньорская цель — не “демократия” и не “диктатура”, а минимизация времени и повторных споров.

    Решения и обратная связь из продакшена

    Решение считается качественным, если оно проверяемо:

  • есть метрики и наблюдаемость;
  • есть план отката или выключения через фича-флаг;
  • есть критерий “хуже/лучше”, а не ощущение.
  • Это связывает техлидство с темами про CI/CD и наблюдаемость.

    !Цикл принятия решений с проверкой в продакшене

    Практические паттерны техлидства во фронтенде

    Паттерн “Стандартизируй, чтобы не обсуждать заново”

    Техлид снижает нагрузку на коммуникацию, когда делает решения исполнимыми:

  • шаблоны PR и issue;
  • стандарты обработки ошибок;
  • единый HTTP-клиент и политика ретраев;
  • единый контракт аналитики;
  • правила модульных границ.
  • Паттерн “Один раз договориться об определении готовности”

    Техническая “готовность” часто ломается о детали. Рабочая практика:

  • для критичных фич держать минимальный Definition of Done;
  • заранее включить: тесты, наблюдаемость, доступность, план отката.
  • Паттерн “Делай риски видимыми”

    Техлид регулярно делает одно и то же:

  • называет риски вслух;
  • ранжирует их;
  • предлагает контроль.
  • Это особенно важно в ситуациях:

  • миграций дизайн-системы;
  • рефакторинга архитектуры;
  • изменений авторизации;
  • внедрения Service Worker;
  • перехода на SSR.
  • Типовые ошибки техлида и как их предотвращать

    Техлид как “узкое горлышко”

    Симптомы:

  • все решения и ревью проходят через одного человека;
  • команда ждёт ответа вместо движения.
  • Что помогает:

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

    Симптомы:

  • встречи вместо артефактов;
  • решения забываются;
  • люди не в курсе контекста.
  • Что помогает:

  • письменные RFC и ADR;
  • короткие, но фиксируемые итоги;
  • асинхронные обсуждения там, где возможно.
  • Ревью превращается в стиль и вкусовщину

    Симптомы:

  • много замечаний без влияния на риск;
  • демотивация;
  • медленный цикл PR.
  • Что помогает:

  • автоматизация форматирования;
  • классификация замечаний;
  • фокус на контрактах, рисках, тестируемости, наблюдаемости.
  • Решения не проверяются реальностью

    Симптомы:

  • выбор “потому что так правильно”, без метрик;
  • регрессии в продакшене находят пользователи.
  • Что помогает:

  • фича-флаги и постепенный rollout;
  • наблюдаемость по релизам;
  • постмортем-культура.
  • Ссылка на практику постмортемов:

  • Google SRE Book: Postmortem Culture
  • Итог

    Техлидство на фронтенде — это практики, которые делают команду и систему предсказуемыми:

  • Code review удерживает качество, архитектурные границы, безопасность и распространяет знания.
  • Коммуникация делает ожидания и риски явными, снижает хаос и стоимость координации.
  • Принятие решений превращает неопределённость в выбранный путь, зафиксированный и проверяемый метриками.
  • Если senior отвечает за результат “своими руками”, то техлидство отвечает за результат “через систему”: людей, процессы, стандарты, измерения и решения, которые живут дольше одного PR.