Головна / Статті

Як інженерія фронтенду еволюціонувала від стилізації до масштабування систем

Інженерія фронтенду розвивалася від HTML та CSS до компонентів React, кешування, інструментів для монорепо та можливостей моніторингу. Що змінювалося на кожному етапі та чому це відбувалося.

Видавець: REACTAPP.TOP10 хв читання2240 слів

Джерела: I Thought Frontend Was Colors and Fonts. Then I Shipped to a Million Users.

Інструменти еволюціонували, але наш спосіб мислення змінився ще більше.

Якщо запитати когось, що означає розробка фронтенду, ви зазвичай почуєте одну й ту саму відповідь: HTML, CSS, JavaScript. Кольори, шрифти, зображення, макет.

Це прийнятна відправна точка. Проте історія тут не закінчується.

Те, що починалося зі створення веб-сторінок, перетворилося на розробку повних систем, які мають залишатися швидкими, коректними та прозорими під час одночасної роботи з ними тисяч людей.

Інструменти розвивалися в цьому напрямку. Але ще більше змінився набір запитань, які ми навчилися ставити собі.

Ось весь шлях розвитку.

1. Усе почалося з HTML, CSS та JavaScript

Ранній веб залишав усе простим.

HTML відповідав за структуру. CSS — за візуальне оформлення. JavaScript додавав елементи інтерактивності.

Місія можна було описати одним реченням:

Створити сторінку, яка гарно виглядає та належним чином функціонує.

Більшість сторінок були статичними. Сервери надсилали повний HTML-код, браузери його відображали, а сам браузер майже не містив логіки.

З часом сторінки почали виконувати більше функцій. Форми почали перевіряти введену інформацію під час вводу користувачем. Контент оновлювався без повної перезавантаження сторінки.

JavaScript постійно брав на себе все більше обов’язків та ніколи не передавав їх іншим компонентам.

  1. Від сторінок до додатків

Веб-сайти перетворилися на додатки. Це означало наявність динамічних інтерфейсів, викликів API, форм з кількома кроками, процедур автентифікації та зберігання даних у браузері між окремими взаємодіями.

Ця зміна виявила проблему, яку старі технології не були призначені вирішувати.

Коли ви прямо маніпулюєте DOM, видимий екран стає вашим єдиним джерелом істини. Одна й та сама інформація може знаходитися у змінній, всередині вузла DOM та у відповіді від сервера. Ніщо не гарантує, що ці три джерела будуть узгоджені.

Якщо оновити одне з них та проігнорувати решту, у результаті з’явиться баг, який проявляється лише після певної послідовності кліків.

Була потреба у способі визначити як має виглядати інтерфейс та делегувати завдання з виконання фактичних операцій над DOM комусь іншому.

3. React: мислення через компоненти

Справжнім проривом React була не його синтаксис, а ментальна модель, що стояла за нею.

UI = f(state)

Ви описуєте вигляд екрана для певного стану, а фреймворк визначає найменший набір оновлень DOM, необхідних для досягнення цього стану.

Замість того, щоб писати element.appendChild(...), потрібно вказати те, що має бути істинним.

Ось що дає такий підхід:

Многоразове використання. Створіть компонент один раз, і ви зможете використовувати його у багатьох місцях.

Локальне розуміння. Ви можете зрозуміти один компонент, не читаючи всю програму.

Можливість тестування. Передайте йому параметри, перевірьте результат — браузер не потрібен.

Складовість. Маленькі компоненти поєднуються, утворюючи більш складні.

Центральне питання змінилося з "Як створити цю сторінку?" на "Які елементи складають це додаток?"

4. Next.js: від інтерфейсу до архітектури

Чисто клієнтський React мав свої недоліки.

Браузер спочатку завантажує порожню HTML-шкаралупу. Потім він завантажує значний пакет JavaScript. Після цього виконує цей пакет. Далі отримує фактичні дані. Лише після всього цього користувач бачить вміст.

Це нормально на потужному ноутбуці. Але на телефоні середнього класу з слабким сигналом це означає кілька секунд очікування на порожньому екрані. Крім того, пошукові роботи можуть взагалі не індексувати нічого корисного.

Фреймворки на кшталт Next.js надають розробникам контроль над тим, де та коли насправді генерується HTML.

SSG. Рендеринг відбувається під час створення проекту, а статичний файл доставляється через CDN. Це найшвидший варіант, проте контент залишається незмінним до наступного створення проекту.

ISR. Контент залишається статичним, але оновлюється на фоні через певний проміжок часу. Ви отримуєте швидкість статичних сайтів з контентом, який залишається досить свіжим.

SSR. Рендеринг відбувається на сервері для кожного запиту. Контент завжди є актуальним, але сервер виконує нові обчислення щоразу.

CSR. Рендеринг відбувається повністю у браузері. Це підходить для сторінок, доступ до яких обмежений входом, де індексування не має значення.

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

Маркетингові сторінки виграють від SSG. Сторінки продуктів — від ISR. Панелі керування — від CSR.

Саме тут вступає в игру гідратація. Сервер надсилає HTML, а потім клієнт знову обробляє дерево компонентів для підключення обробників подій. Візуально сторінка виглядає готовою ще до того, як стає справді інтерактивною.

React Server Components та стрімінг існують переважно для подолання цього пробілу. HTML надсилається як тільки він стає доступним, а для частин сторінки, які ніколи не мали бути інтерактивними, надсилається менше JavaScript.

На цьому етапі робота над фронтендом більше не є просто „шаром користувацького інтерфейсу“. Рішення щодо стратегії відображення, маршрутизації та кешування стають архітектурними рішеннями, і саме інженери фронтенду їх приймають.

5. Потім з’явилась масштабованість

Створення додатку — це одна з викликів. Створення додатку, який обслуговує мільйони користувачів, — це зовсім інший виклик.

Питання, які ви ставите, починають змінюватися:

Що має відбуватися, коли API на наступному етапі обробки сповільнюється, замість того щоб просто припинити працювати?

Як шість окремих команд можуть викладати код у спільну базу без конфліктів?

Як розгорнути зміни лише для 1% користувачів та відкликати їх протягом хвилини, якщо щось піде не так?

Як поводиться додаток на трьохрічному Android-телефоні в місці, де ви його ніколи не тестували?

Яка з вчорашніх розгорток є причиною сьогоднішнього стрибка кількості помилок?

Це питання рівня системи. Вони просто виникають у браузері.

6. Швидкість — це не додаткова опція, а базова вимога

Елегантний інтерфейс, який генерується протягом шести секунд, можна вважати несправним. Користувачі вже помічають затримку, перш ніж зможуть точно описати проблему.

Сьогодні дисципліна виконання завдань ґрунтується на вимірюваннях, а не на інтуїції. Core Web Vitals надають командам спільну мову для обговорення швидкості роботи сайту.

LCP (Largest Contentful Paint) позначає момент, коли основний контент стає видимим. Мета — триматися під рівнем 2,5 секунди.

INP (Interaction to Next Paint) відображає, наскільки швидко інтерфейс реагує після натискання або кліку. Мета — менше 200 мілісекунд.

CLS (Cumulative Layout Shift) вимірює ступінь зміщення елементів під час завантаження сторінки. Мета — показник нижче 0,1.

Кожна техніка оптимізації відповідає одному з цих показників.

Розділення коду та його поступове завантаження. Надсилайте лише той JavaScript, який дійсно потрібен для поточного маршруту, а не весь пакет додатку. Це покращує як LCP, так і INP.

Налаштування зображень. Використовуйте сучасні формати, правильно визначайте їхні розміри та завжди вказуйте ширину та висоту заздалегідь, щоб нічого не «перескакувало». Це допомагає покращити показники LCP та CLS.

Ретельне керування шрифтами. Завантажуйте їх заздалегідь, застосовуйте параметр font-display: swap та скорочуйте набір символів до того, що насправді використовується. Це допомагає усунути проблеми, пов’язані з CLS.

Розповсюдження через CDN. Надання даних з сервера, розташованого географічно близько до користувача, одночасно покращує майже всі показники.

Уникнення виконання важких завдань на основній смузі. У браузері є одна смуга виконання, яка обробляє як відображення контенту, так і ваші скрипти. Завдання, яке виконується протягом 300 мілісекунд, зупиняє кожну дію протягом усього цього часу. Саме тут проявляється користь від параметра INP.

Основний принцип, сформульований просто:

Забезпечте найкращий досвід, використовуючи якомога менше ресурсів.

Одна порада: налаштовуйте параметри на основі фактичних показників, а не припущень. Цифри, отримані з ноутбука розробника в лабораторних умовах, часто відрізняються від того, що переживають справжні користувачі в реальних умовах, і саме дані з реального середовища відображають дійсність.

7. Кешування: уникайте повторного обчислення того, що ви вже розв’язали

Коли обсяг трафіку зростає, постійне генерування однакових відповідей стає марнотратним. Кешування існує для того, щоб ви не платили двічі за однакову роботу.

Кожен запит проходить крізь кілька рівнів, і кожен з них є можливістю скоротити обсяг виконуваних операцій.

Browser → CDN → Application / ISR → Redis → Database

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

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

Це протиріччя визначає всю практику кешування.

Що має бути в кеші? Дані, які є стабільними, спільними для кількох користувачів та їх створення є дорогим. Ніколи не слід кешувати щось індивідуальне для користувача, якщо ключ кешу не враховує особливості конкретного користувача.

Як довго вона має зберігатися? Короткі терміни дії є безпечнішими, але приносять менше переваг. Довгі терміни дозволяють швидше працювати, але створюють більше ризиків.

Як її очищувати? Серед варіантів — тимчасове закінчення терміну дії (TTL), автоматичне очищення після певної події (після опублікування) або гешування імен файлів за їхнім вмістом, щоб повна видалення навіть не була потрібною.

Що відбувається, коли пошук зазнає невдачі? Якщо термін дії широко використовуваного ключа закінчується, а низка запитів одночасно надходить на сервер, це призводить до перевантаження. Щоб уникнути цього, використовуйте механізм блокування або подавайте застарілу версію, поки окремий запит обробляє оновлення.

Шаблон, який варто запам’ятати, — це stale-while-revalidate. Негайно повертайте збережену версію, тихо оновлюйте її на фоні, і наступний відвідувач отримає оновлений контент. Користувачі бачать швидкість, а ви забезпечуєте точність із невеликою затримкою.

Зверніть увагу на те, яку ціну може мати помилка в цьому процесі. Пошкоджений кеш не лише уповільнює роботу — він також може показати неправильну ціну, неправильне ім’я або чужі дані.

Кешування здається технікою покращення продуктивності, але насправді є рішенням щодо точності даних.

8. Структурування для зростання

Коли кілька команд працює над одним кодовим базисом, спосіб його організації стає настільки ж важливим, як і швидкість його роботи.

Межі між модулями. Вирішіть, що буде доступне для публіки, що залишиться внутрішнім, та які залежності дозволені.

Контракти API. З типизацією та версіонуванням, щоб зміни на бекенді порушували лише процес компіляції, а не функціонування сайту.

Де знаходиться стан. Стан сервера, URL та локальний стан клієнта — це принципово різні категорії. Більшість помилок, пов’язаних із станом, виникають через плутанину між ними.

Ізоляція збоїв. Межі помилок гарантують, що один несправний елемент не призведе до зупинки всієї сторінки.

Впровадження змін. Флаги функцій, поступове розгортання та можливість миттєвого скасування змін.

Спосіб мислення змінюється від простого функціонування функції до забезпечення того, щоб ця функція не ускладнювала кожну майбутню зміну.

9. Спільний код у монорепо

Як тільки кілька додатків починають використовувати однакові заголовки, однакові дизайн-токени та однакові інструменти автентифікації, копіювання та вставка коду стає нежиттєздатним підходом.

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

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

Фактичною метою є повторне використання без тісного зв’язку.

Корисна гіпотеза: діліться елементами, які рідко змінюються та мають однакове значення незалежно від місця їх використання — такими як дизайн-токени, компонент кнопки чи визначення типів.

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

Мікро-фронтенди розвивають цю ідею далі, дозволяючи командам незалежно розробляти продукт, але це супроводжується збільшеною складністю під час виконання та дублюванням залежностей. Такий компроміс може бути доцільним для великої організації з чітким розмежуванням між командами. Поза цим контекстом він рідко є ефективним.

10. Спостереження за системою у продакшені

У масштабних проектах фрази на кшталт „у мене все працює“ чи „у тестовому середовищі все працювало добре“ вже майже нічого не означають.

Те, що вам насправді потрібно, — це можливість бачити, як працює додаток у реальних умовах, на справжніх пристроях, у реальному часі.

Відстеження помилок у поєднанні з завантаженими картами вихідного коду, щоб сліди виконання програми вказували на ваш справжній вихідний код, а не на спрощений набір символів.

Моніторинг справжніх користувачів для збору даних Core Web Vitals від реальних користувачів, сортованих за пристроєм, країною та якістю з’єднання.

Структуроване журналювання, пов’язане з ідентифікатором запиту, який дозволяє відстежувати процес від браузера аж до кожної з послуг сервера, які він використовує.

Попередження, пов’язані з конкретними симптомами. Информуйте людей, коли показники кількості помилок чи затримки перевищують певний поріг, а не просто тоді, коли зростає навантаження на CPU сервера.

Відстеження випусків. Позначайте кожну помилку конкретним випуском, який її спричинив, щоб відповісти на запитання „що змінилося?“ займало секунди, а не години.

Запитання, на які ця система дозволяє відповісти:

Де саме користувачі стикаються з помилками? Який виклик API погіршує їхній досвід? Який маршрут працює повільно та для якої групи користувачів? Чи погіршив останній випуск ситуацію порівняно з попереднім?

Раніше код фронтенду був найскладнішою частиною стеку для спостереження. Тепер це вже не обов’язково має бути так.