Навыки и развитиеНавыки и развитие07.05.2026

Один разработчик — весь фронтенд: проектирование, реализация и оптимизация интерфейса

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

Cover
Георгий Ноздрин

Георгий Ноздрин

React frontend-разработчик продукта HT Platform

12 мин12 мин
Article image

Аннотация

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

В этой статье я делюсь практическим опытом создания клиентской части HR-платформы (соискатели, работодатели, вакансии, чаты) с нуля до продакшена силами одного инженера. Я расскажу о выборе стека, адаптации Feature-Sliced Design, укрощении модальных окон, работе с API и глобальном перехвате ошибок. В финале поделюсь результатами «боевого крещения» архитектуры при наплыве пользователей.

Введение — почему это интересно

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

Передо мной стояла задача: написать клиентскую часть HR-платформы. Продукт включал личные кабинеты кандидатов и работодателей, систему публикации и импорта вакансий, встроенный чат и информационные разделы (статьи, публикации).

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

1. Что было важно принять в начале (мой чек-лист)

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

Article image
  1. Выбрать строгую структуру проекта (FSD) и не отступать от нее.
  2. Сделать единый API-слой для всех HTTP-вызовов (никаких fetch внутри компонентов).
  3. Перехватывать все серверные ошибки глобально, чтобы не писать try/catch на каждой странице.
  4. Настроить E2E-тесты для критических путей (я один, я не смогу тестировать руками всё).
  5. Максимально автоматизировать деплой.

2. Стек технологий — почему именно он

Когда ресурсов мало, инструменты должны помогать, а не вставлять палки в колеса. Я остановился на проверенном и современном наборе:

Article image
  • React + TypeScript. TypeScript в строгом режиме здесь не просто дань моде. Для соло-разработчика это бесплатный тестировщик, который ловит 80% глупых опечаток (вроде user.id вместо user.uuid) еще на этапе компиляции.
  • Vite. После тяжелого Webpack сборка на Vite ощущается как глоток свежего воздуха. Мгновенные перезапуски и HMR экономят десятки минут каждый день. Конфиг vite.config.ts занимает 30 строк и закрывает все потребности.
  • Redux (RTK). Встроенного Context API не хватит для сложного приложения с чатами и ролями, он провоцирует лишние рендеры. Redux Toolkit дал мне предсказуемый контейнер состояния.
  • Cypress. Быстрая уверенность в работоспособности сценариев (логин, профиль, создание вакансии).
  • Docker + Nginx. Готовый и пуленепробиваемый рецепт для стабильного продакшена.

3. Структура проекта (Feature-Sliced Design на практике)

В начале пути всегда есть соблазн сложить все компоненты в папку components. Для пет-проекта это работает, для большой HR-платформы — это путь в ад. Я адаптировал методологию Feature-Sliced Design.

Мой проект разделен на четкие слои:

Рисунок 1. Архитектура слоев приложения
Рисунок 1. Архитектура слоев приложения
  • app/ — точка входа: инициализация App.tsx, настройка store, провайдеры (with-providers.tsx).
  • pages/ — маршруты. Они почти не содержат логики, их задача — просто собрать страницу из фичей.
  • widgets/ — крупные агрегаты UI (Header, Footer, SideBar, ModalManager).
  • features/ — конкретные бизнес-флоу (авторизация, чат, импорт вакансий).
  • entities/ — доменные сущности (vacancy, candidate, employer). Внутри каждой сущности лежат свои папки: api, model, ui, service.
  • shared/ — «глупый» переиспользуемый код: UI-библиотека (кнопки, инпуты), глобальный ApiClient, константы.

Это дает понятные границы. Изменение логики карточки вакансии в entities/vacancy физически не может сломать процесс регистрации в features/auth.

4. ApiClient и история про Refresh Token

Разбрасывать запросы к серверу по всем компонентам — ошибка. Я создал единый ApiClient.ts в слое shared. Любой HTTP-запрос в приложении проходит через него.

Реальная проблема, с которой я столкнулся:

Однажды баг с race condition (состоянием гонки) в refresh-token начал сводить меня с ума. Когда у пользователя истекал access_token, страница отправляла сразу 4 параллельных запроса (за профилем, вакансиями, чатами). Все 4 получали 401 ошибку, и приложение 4 раза пыталось обновить токен, сбрасывая сессию.

Как я это решил:

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

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

Как я это решил:

5. Глобальный перехват ошибок (чтобы не писать try/catch)

Писать обработку ошибок в каждом компоненте мучительно. Я вынес это в Redux, написав errorListenerMiddleware.ts.

Этот middleware слушает все экшены, которые завершились с ошибкой (rejected). Если сервер отвечает 403 (Нет прав) — всплывает глобальный Toast. Если 500 — Toast «Сервер временно недоступен». А если 401 — пользователя мягко редиректит на логин.

Это сделало компоненты страниц невероятно чистыми: они просто дергают данные из API и рендерят их. О том, что сеть может отвалиться, думает errorListenerMiddleware.

Рисунок 2. Типичный поток данных и отлов ошибок
Рисунок 2. Типичный поток данных и отлов ошибок

6. Маршрутизация по ролям и ленивая загрузка

Интерфейсы соискателя и работодателя кардинально отличаются. Сердцем маршрутизации стал мой компонент RoleBasedRoute.tsx.

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

6. Маршрутизация по ролям и ленивая загрузка

UX-совет: показывайте пользователю только те действия, которые ему разрешены, вместо того чтобы бить его по рукам всплывающими окнами «Ошибка доступа» постфактум.

Также я активно использовал Code-Splitting (React.lazy). Модули работодателя физически не загружаются в браузер соискателя, что сильно уменьшило начальный бандл.

7. Укрощение UI: Стили и ModalManager

Модальные окна — вечная боль фронтенда. Если каждый компонент рендерит свою модалку, начинаются конфликты z-index. Я решил это централизованно: создал ModalManager в слое widgets.

Article image

В Redux хранится стейт activeModalType. Вызвать окно редактирования профиля можно из любой глубины приложения одной строкой: dispatch(openModal('EDIT_PROFILE')). ModalManager видит изменение стейта и рендерит нужный попап через React Portals поверх всего приложения. Чисто и элегантно.

Стили лежат в двух местах: src/styles/* и index.css отвечают за глобальные токены (переменные цветов, шрифтов), а специфика страниц изолирована в CSS Modules (например, contestInstructionsPage.module.css).

8. Тестирование: Какой охват нужен одному разработчику?

Писать тесты одному кажется потерей времени, пока ты не сломаешь прод. Я выбрал компромисс: юнит-тесты только для сложной бизнес-логики, а основное время — на E2E в Cypress.

В папке cypress/e2e/ лежат сценарии, которые прокликивают критичные пути: регистрация, поиск вакансии, успешный отклик, создание вакансии работодателем.

Если эти тесты зеленые, я уверен, что ядро продукта работает. Плюс строгий ESLint и commitlint для гигиены кода. Эти выходные, потраченные на Cypress, окупились сполна при регрессионном тестировании крупных фичей.

8. Тестирование: Какой охват нужен одному разработчику?

9. Деплой и проверка боем (Стресс-тест)

Я терпеть не могу деплоить руками. В корне проекта лежит Dockerfile, использующий multi-stage сборку. Сначала стартует Node, запускает npm run build через Vite, а затем берет легкий образ Nginx и подкладывает туда готовую папку dist. Файл nginx.conf настроен на отдачу index.html и сжатие статики.

Рисунок 3. Процесс сборки и деплоя контейнера
Рисунок 3. Процесс сборки и деплоя контейнера

История из продакшена

Теория архитектуры — это прекрасно, но настоящая проверка происходит в бою. На сегодняшний день на платформе успешно зарегистрировано уже более 2 000 пользователей.

Но главное крещение архитектуры состоялось, когда платформа выступила площадкой для масштабного профильного конкурса. В час X мы получили резкий наплыв: около 600 человек одновременно ринулись на сайт. Они читали инструкции, заполняли профили и откликались на вакансии и выполняли тестирование.

Для бэкенда это был огромный стресс. А фронтенд справился безупречно. Поскольку мы использовали статический деплой (SPA, отдаваемый через Nginx), генерация страниц вообще не требовала серверных процессорных мощностей. Nginx просто отдавал кэшированные статические файлы JS и CSS.

Те редкие API-запросы, которые в первые минуты падали по таймауту из-за перегрузки бекенда, мягко перехватывались нашим errorListenerMiddleware, показывая дружелюбные уведомления. Архитектура выдержала пик конкурса без единого сбоя на стороне клиента.

Заключение — что взять с собой

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

Мои главные выводы:

  1. Структура важнее фичей. FSD спас проект от превращения в легаси.
  2. Единые точки входа экономят время. ApiClient и errorListenerMiddleware освободили UI от сетевого бойлерплейта.
  3. Не плодите модалки. Один ModalManager на все приложение решает 99% проблем с интерфейсом.
  4. Тесты — не роскошь. E2E покрытие критичных путей спасает релизы, когда нет QA-инженера.
  5. Слушайте пользователей. Иногда быстрая кнопка «Сохранить черновик» важнее идеальной анимации.

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

Список литературы и ресурсы

  1. Официальная документация React
  2. Vite: Next Generation Frontend Tooling
  3. Feature-Sliced Design (FSD) Architectural Methodology
  4. Документация Redux Toolkit
  5. Cypress End-to-End Testing
  6. Nginx Official Documentation
Георгий Ноздрин

Георгий Ноздрин

React frontend-разработчик продукта HT Platform

#frontend#React#FSD#архитектура#опыт