Коротка відповідь. На Senior-раунді перевіряють не «чи знаєш ти патерни», а чи вмієш ти обирати між ними і чесно називати ціну вибору. Сильна відповідь має структуру «контекст → варіанти → вибір → ціна». Слабка – це універсальний рецепт без умов застосування. Нижче – як мислити про межі компонентів, місце для стану й обробку помилок так, щоб це звучало як досвід, а не як конспект.

Що насправді оцінюють на архітектурному раунді

Інтерв'юер ставить відкрите питання: «як би ти організував стан у великому застосунку?», «як структурувати компоненти фічі?». Правильної відповіді не існує – оцінюють процес:

  • чи уточнюєш ти контекст перед відповіддю (розмір команди, вимоги, обмеження);
  • чи бачиш більше одного варіанта;
  • чи називаєш ціну свого вибору сам, до того як спитають;
  • чи відрізняєш «так модно» від «так потрібно тут».

Формальний рівень компетенцій за грейдами я розбирав у статті Middle → Senior Frontend – тут зосередимось саме на аргументації.

Межі компонентів і шарів

Базове архітектурне рішення в React – де провести межі. Робочий поділ на три шари:

  1. UI-компоненти – рендерять пропси, не знають про API і бізнес-правила. Їх легко переносити й тестувати.
  2. Логіка – хуки з бізнес-поведінкою: useCart, useCheckoutForm. Тут живуть правила, валідація, оркестрація запитів.
  3. Дані – шар доступу до API: функції запитів, типи відповідей, серіалізація.

Сигнали порушених меж, які варто вміти називати: компонент на 400 рядків із fetch-ами всередині JSX; UI-компонент, що імпортує API-клієнт; бізнес-правило, продубльоване у трьох компонентах. Рефакторинг тут – не «розбити на менші файли», а винести причину зміни в один шар: правило змінюється в одному місці.

Важливий нюанс для співбесіди: поділ на шари – це інструмент, а не догма. Для одноразової фічі з одним екраном три шари можуть бути надлишковими, і сказати це вголос – плюс, а не мінус.

Де жити стану: критерії, а не бібліотеки

Найчастіша помилка кандидатів – відповідати назвою бібліотеки. Сильніша відповідь – критерії вибору місця для стану:

Тип стану Де жити Критерій
Локальний UI (відкритий дропдаун, значення інпута) useState у компоненті Ніхто інший його не читає
Спільний для гілки дерева Піднятий до спільного предка або context Читають кілька сусідів
Серверні дані (списки, профілі) Кеш запитів (будь-яка бібліотека кешування або власний шар) Джерело правди – сервер; потрібні refetch, інвалідація, статуси
Справді глобальний клієнтський (тема, auth-сесія) Глобальний store або context на верхньому рівні Читається скрізь, змінюється рідко

Ключова теза, що відрізняє Senior: серверні дані – не клієнтський стан. Складати відповіді API у глобальний store і вручну синхронізувати – це відтворення кешу без інвалідації. Якою бібліотекою користуватись – деталь; розуміння, що це різні категорії стану, – суть.

Помилки й loading як архітектурне рішення

Junior обробляє помилки в кожному компоненті окремо. Senior проєктує політику:

  • Межі помилок (error boundaries) на рівні маршруту або великої фічі: падіння віджета не кладе всю сторінку.
  • Єдина семантика статусів: кожен запит має явні loading / error / empty / success стани, і компоненти не вигадують їх щоразу заново.
  • Розділення відновлюваних і фатальних помилок: 401 → редірект на логін; мережева помилка → retry із повідомленням; помилка рендера → boundary із fallback.

На співбесіді достатньо намалювати це словами: «на рівні маршруту – boundary, на рівні запитів – єдиний хук зі статусами, на рівні форм – локальна валідація». Це показує системне мислення краще за будь-який список бібліотек.

Формат «контекст → варіанти → вибір → ціна»

Приклад повної аргументації на типове питання «винести логіку фільтрів у context чи тримати в компоненті?»:

  • Контекст: фільтри читають три компоненти на одній сторінці; інші сторінки їх не використовують; команда – четверо людей.
  • Варіанти: (1) підняти стан до спільного предка і передати пропсами; (2) локальний context сторінки; (3) глобальний store.
  • Вибір: варіант 2 – context на рівні сторінки: споживачів більше двох, але стан не глобальний.
  • Ціна: будь-яка зміна фільтрів рендерить усіх споживачів context; якщо з'явиться дорогий компонент-споживач, розіб'ю context на «значення» і «сеттери» або повернусь до пропсів.

Чотири речення – і інтерв'юер бачить усе: ти зважував альтернативи, обрав під контекст і знаєш слабке місце свого рішення заздалегідь.

Слабкі vs сильні відповіді: приклади

Питання: «Чому ти б використав глобальний store?»

  • Слабко: «Бо в великих проєктах завжди потрібен store, це стандарт».
  • Сильно: «Якщо є справді глобальний клієнтський стан – сесія, тема, фіча-флаги. Для серверних даних store не потрібен: їх місце в кеші запитів. У поточному проєкті глобального стану в мене два поля, тож вистачає context».

Питання: «Як ти організуєш структуру папок?»

  • Слабко: «components, hooks, utils, services – так усі роблять».
  • Сильно: «Групую за фічами: усе, що змінюється разом, лежить поруч – компоненти, хуки й запити фічі в одній папці. Спільне виношу лише після другого-третього використання. Ціна: іноді складніше знайти, де межа фічі, тому домовляємось про правила імпортів між фічами».

Різниця не в знаннях – обидва кандидати знають однакові слова. Різниця в тому, що сильна відповідь прив'язана до умов і чесно називає ціну.

Ще один повний приклад: форми

Друге за популярністю архітектурне питання після стану – «як ти працюєш із формами?». Той самий формат:

  • Контекст: у застосунку і прості форми (логін, пошук), і складні (багатокрокове оформлення з залежними полями).
  • Варіанти: (1) контрольовані інпути з ручним useState на кожне поле; (2) неконтрольовані поля + читання значень на submit; (3) бібліотека форм зі схемою валідації.
  • Вибір: для простих форм – варіант 1 або 2, без залежностей; для складних – варіант 3, бо ручна синхронізація валідації, touched-станів і залежних полів швидко стає джерелом багів.
  • Ціна: бібліотека – це залежність і її API, який треба знати всій команді; схема валідації дублюється з серверною, тому домовляємось про спільне джерело правди для правил.

Зверни увагу на структуру: рішення різне для різних частин застосунку. «Одна технологія на всі випадки» – маркер шаблонного мислення; диференціація за складністю – маркер досвіду.

Червоні прапорці у відповідях

Фрази, які на Senior-раунді працюють проти тебе:

  • «Так прийнято» / «це best practice» – без пояснення, чому practice став best і чи застосовний тут.
  • «Ми завжди використовуємо X» – відсутність варіантів означає, що вибору не було.
  • «Це масштабованіше» – без відповіді, яке саме зростання очікується і що конкретно зламається без цього рішення.
  • Мовчання про недоліки власного вибору. Якщо ціну рішення першим називає інтерв'юер – ти її не бачив.

Дзеркальне правило: чесне «тут я б не ускладнював» на просте питання – сильна відповідь. Уміння НЕ застосувати патерн цінується не менше, ніж уміння застосувати.

Як тренувати аргументацію

  1. Візьми будь-яке своє минуле рішення (стан, структура, бібліотека) і письмово розклади за форматом «контекст → варіанти → вибір → ціна».
  2. Зроби те саме для протилежного рішення – аргументуй, за яких умов правильним був би інший варіант. Якщо не виходить – ти не обирав, а слідував звичці.
  3. Потренуйся вголос: архітектурні відповіді на письмі й у розмові – різні навички.

Продовження цієї теми в масштабі цілого застосунку – дизайн стрічки, кешування, продуктивність – розбираю в статті про frontend system design. А про те, коли оптимізація стає архітектурним питанням, – у матеріалі про продуктивність React без передчасного useMemo.