Розробка бекенду: де система ламається під навантаженням
Бекенд рідко падає одразу — частіше він деградує під навантаженням поступово: спочатку час відгуку зростає на піках, потім окремі операції починають відвалюватися за таймаутом, і лише згодом система стає недоступною повністю. Між цими станами можна жити місяцями, списуючи скарги на повільний канал у клієнта.
Перше місце відмови — важкі запити до сховища.
Вибірка за неіндексованим полем, агрегат по всій таблиці або добір пов’язаних сутностей по одній у циклі при десятикратному зростанні трафіку перетворюється з десятків мілісекунд у секунди, і пул з’єднань вичерпується швидше, ніж встигає звільнятися.
Друге — блокування в базі. Довга транзакція, яка оновлює рядки, потрібні іншим запитам, збирає чергу очікувачів; якщо всередину транзакції потрапляє звернення до зовнішнього сервісу, черга зростає пропорційно до його відгуку.
Частина запитів відвалюється, клієнт повторює їх, і повтори додають навантаження поверх уже забитої черги.
Третє — черги й дублювання логіки. Черга без обмеження за розміром і без контролю повторів зростає швидше, ніж обробник встигає її розбирати, а кожна повторна спроба множить навантаження на ті самі вузли.
Сценарій, розмножений по контролеру, консольній команді та обробнику, з часом розходиться: правка в одному місці ламає поведінку в двох інших, і спливає це лише на інциденті.
Для продукту все це обертається зростанням часу відгуку в години пік, відмовами частини операцій і неможливістю назвати вузьке місце застосунку без профілювання. Розбираємо, як це закривається.
Прикладний ефект: швидкість відгуку, відмови, простій
Архітектурні рішення видно не в схемах, а у вимірюваних величинах: скільки тримає система під піком, яка частка операцій завершується успішно, за скільки хвилин піднімається після збою. Профілювання та черги на Symfony дають ці цифри до релізу, а не після інциденту. Нижче — чотири ефекти, які замовник перевіряє на своїх метриках.
Час відгуку під навантаженням
Розділяємо читання й запис, виносимо важкі операції у фонову обробку, кешуємо те, що читається частіше, ніж змінюється.
Час відгуку основного сценарію залишається в робочому коридорі при кратному зростанні звернень: деградація починається пізніше й видна за метриками раніше, ніж за скаргами.
Передбачуваність системи та клас відмов
Зовнішній сервіс відповідає із затримкою або мовчить — це не повинно валити оформлення замовлення. Обмеження повторних спроб, ізоляція збоїв та ідемпотентні операції переводять частину помилок у клас самовідновлюваних: черга доопрацьовує, користувач отримує підтвердження. Зникає клас відмов, який раніше вимагав ручної правки даних.
Час відновлення та вартість простою
Розгортання без зупинки сервісу, міграції окремо від коду, відкат на попередній реліз однією командою — відновлення вимірюється хвилинами, а не годинами погоджень. Чим коротше вікно простою, тим менше втрачених замовлень і звернень до підтримки.
Формати розробки бекенду: моноліт, модулі, сервіси
Архітектура серверної частини визначає, скільки коштує підключення другої команди та наскільки швидко можна переписати одну функцію, не зачепивши інші.
На Symfony реалізовні всі три варіанти, різниця не в інструментах, а в тому, де проходять межі модулів і наскільки сильно пов’язаний код між ними.
Тому вибір архітектури — це відповідь на питання «скільки людей працює над проєктом одночасно» та «чи навантажені його частини по-різному».
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Єдиний моноліт | Одна команда, домен ще уточнюється, важлива швидкість першої версії | Межі модулів розмиваються, зв’язність коду зростає, одна правка вимагає повного прогону перевірок і випуску релізу цілком |
| Модульний моноліт | 2–4 команди, домен зрозумілий, частини системи розвиваються з різною швидкістю | Потрібна дисципліна: правила залежностей між модулями перевіряються автоматично, інакше межі зникають за пару місяців |
| Сервісна архітектура | Частини системи навантажені нерівномірно або потребують окремого масштабування та свого циклу випуску | Експлуатація дорожча: узгодженість даних між сервісами, мережеві відмови, окремі середовища й спостережуваність для кожного |
Ми в більшості випадків починаємо з модульного моноліту: він дає явні межі модулів і низьку зв’язність коду, але залишається одним розгортуваним застосунком — це дешевше і в розробці, і в підтримці.
Виділення сервісів — продовження еволюції, а не стартове рішення: коли навантаження на конкретну частину зростає в рази, модуль виноситься окремо, решта коду не переписується.
Порядок впровадження: від аудиту коду до передачі в експлуатацію
Роботу ведемо етапами, і кожен завершується перевірюваним артефактом, а не звітом про виконане.
Порядок фіксуємо до старту: спочатку розбираємося, що вже працює, потім змінюємо схему даних і конфігурацію, далі перевіряємо поведінку під навантаженням — і лише після цього передаємо в експлуатацію з регламентами.
- Аудит коду та інфраструктури. Дивимося структуру проєкту, дублювання бізнес-логіки, ділянки без тестів, неявні залежності між модулями. На виході — перелік вузьких місць з оцінкою ризику та черговістю правок.
- Схема даних і межі відповідальності. Визначаємо, де джерело істини, які зв’язки обов’язкові, які індекси потрібні під реальні вибірки. Межі між модулями описуємо схемою та правилами обміну — це знімає половину спірних правок у майбутньому.
- Налаштування середовищ і конфігурації. Приводимо локальне, тестове та робоче середовища до одного вигляду, виносимо секрети з коду, описуємо міграції так, щоб вони проходили без ручних кроків і не блокували викладку.
- Перевірка під навантаженням. Знімаємо профіль реальних запитів, відтворюємо піковий сценарій, дивимося, де зростає час відгуку і як система деградує — чергами, відмовами чи відкладеними операціями.
- Правки за вузькими місцями. Важкі вибірки, проміжне зберігання частих відповідей, винесення фонових задач — змінюємо те, що підтверджено вимірюваннями, а не те, що «зазвичай повільне».
- Передача в експлуатацію. Віддаємо проєкт разом із регламентом оновлень: порядок викладки, умови відкату на попередню версію, доступи, точки контролю та порядок дій при інциденті.
Що залишається в проєкті після здачі: схеми, політики, регламенти
Здача коду без документації означає, що система живе до першого серйозного збою або зміни підрядника. Тому передача — це набір артефактів, за якими інженер, який не бачив проєкт, розгортає систему, відновлює її та змінює права доступу. Усе зберігається поряд із кодом і оновлюється разом із ним, а не в окремій папці.
- Опис архітектури: межі сервісів, потоки даних, черги, кеш, зовнішні інтеграції та точки відмови — зі схемою взаємодії.
- Схеми даних: модель сутностей і зв’язків, історія міграцій, правила зберігання та видалення персональних даних.
- Політики доступу та ролі: матриця «роль — дія — ресурс», порядок видачі й відкликання доступів, місце зберігання секретів.
- Схема розгортання: склад середовищ, конфігурація та змінні середовища, порядок викладки й відкат на попередній реліз.
- Регламент резервного копіювання: що і як часто копіюється, строк зберігання, протокол перевірки відновлення на чистому середовищі.
- Регламент оновлень: порядок встановлення оновлень платформи та залежностей, вікна робіт, перевірка сумісності.
- Передача знань команді: розбори з інженерами замовника, записи сесій, перелік типових операцій і відомих обмежень.
Підсумок — систему можна розвивати своєю командою або передати іншому підряднику без розбору коду. На бекенді на Symfony такий пакет збирається штатно: конфігурація, міграції та політики вже описані в проєкті — їх залишається зафіксувати в документі.
Кейс: скоротили час відгуку та прибрали цілий клас відмов
Сервіс з особистими кабінетами та піковим трафіком: за хвилини ранкового напливу кількість звернень зростала в рази. Серверна частина на Symfony трималася на одному вузлі застосунку, і в пік час відгуку сягав секунд, а частина запитів обривалася за таймаутом.
Кейс проєкту показовий тим, що метрики ми зняли до правок, а переробку серверної частини вели за вузькими місцями, а не за відчуттями.
Де були втрати
У кожному запиті списку або картки виконувалися важкі синхронні операції — перерахунок агрегатів і звернення до зовнішніх сервісів прямо в обробнику.
До цього додавалися кратні запити до бази на вибірку пов’язаних сутностей і повна відсутність кешу гарячих даних: вузьким місцем ставала база, а не процесор.
У пік накопичувалися блокування, воркери черги не встигали розбирати задачі, а недоступність одного зовнішнього сервісу тягла за собою таймаути всього кабінету.
Що змінилося
Перерахунки та зовнішні виклики винесли в чергу з обмеженням паралелізму й роздільними пулами під типи задач. Гарячі вибірки закрили кешем з інвалідацією за пов’язаними сутностями, важкі запити переписали й підкріпили складеними індексами.
Для зовнішніх залежностей задали жорсткі таймаути та деградацію: кабінет віддає останні збережені дані замість помилки.
Метрики знімали одним і тим самим профілем навантаження. До: час відгуку на 95-му процентилі — секунди, частка відповідей з помилкою сервера — одиниці відсотків, у пік сервіс деградував.
Після: час відгуку скоротився в сім разів, частка відмов сервера — частки відсотка, пропускна здатність на тому самому парку зросла втричі.
Усунення відмов підтвердили навантажувальними випробуваннями: тримаємо чотириразовий пік від звичайного вечірнього, час відгуку зростає лінійно, без обвалу та ручного втручання.
Що робити, якщо система вже працює і її не можна зупиняти?
Зупиняти сервіс не потрібно — переробка йде поряд із працюючою системою. Новий код піднімається на тих самих даних і поступово приймає навантаження, поки попередній контур продовжує обслуговувати запити.
Такий режим вимагає дисципліни: розпаралелювання логіки, поетапний перенос і зворотна сумісність на кожному кроці.
На межі застосунку з’являється шар зіставлення: він направляє запит або в поточну реалізацію, або в нову. Обидва контури працюють з одним сховищем — запис спочатку йде в попередній, а зміни дублюються, розбіжності логуються й розбираються до того, як нова гілка стане основною.
Контракти при цьому розширюються, а не ламаються: нові поля необов’язкові, форма попередніх відповідей зберігається, тому зовнішні споживачі переїзду не помічають.
Перемикання йде частинами — за окремими операціями, групами користувачів або сегментами трафіку. Частку можна піднімати поступово й на будь-якому кроці повернути назад керівним флагом, без розгортання та простою.
У цьому відмінність від повного переписування: ризик локалізований в одній ділянці, а не розмазаний по всьому релізу.
Для застосунків на Symfony такий шар лягає на штатну маршрутизацію та контейнер сервісів — окрема платформа для паралельної роботи не потрібна.
До старту фіксуємо контракти, схему відповідності даних, набір метрик і порядок відкату: далі перенос іде передбачуваними кроками, а система весь цей час залишається працюючою.
Технічні сумніви перед стартом робіт
Перед стартом робіт замовник перевіряє не швидкість команди, а ризики: хто тримає код, що з доступами і що залишиться після відходу підрядника. Ці сумніви знімаються не обіцянками, а схемою роботи. Нижче — відповіді на те, що запитують найчастіше.
У нас свій штат розробки — навіщо зовнішня команда?
Зазвичай це змішана модель: наші інженери закривають серверну частину на Symfony та інфраструктуру, ваш штат — предметну логіку та інтерфейси. Межі фіксуємо за модулями, щоб правки не стикалися в одному коді, а ваші розробники бачили кожну зміну.
Кому належать доступи до коду та середовищ?
Репозиторій, сервери та домени оформлені на вас з першого дня. Ми працюємо під вашими обліковими записами з обмеженими правами, ви відкликаєте їх у будь-який момент. Передача проєкту — фон роботи, а не окремий етап наприкінці.
У нас застаріле рішення — ви візьметеся?
Спочатку розбираємо фактичний стан: тести, міграції, залежності, гарячі запити. Далі або поетапна заміна модулями без зупинки продукту, або ізоляція проблемних ділянок і поступовий вивід. Оцінку даємо після розбору, а не до нього.
Що з обсягом даних і продуктивністю?
Дивимося профіль навантаження: співвідношення читання й запису, зростання таблиць, найважчі запити. Під нього налаштовуємо індекси, кеш і черги, а звітність виносимо з основного контуру. Навантажувальний прогін повторюємо перед кожним великим релізом.
Як проходить передача проєкту іншій команді?
Документація, схеми даних, налаштування середовищ і регламент викату живуть у репозиторії разом із кодом. Передача — це супровід нового підрядника на час входу, а не папка з файлами на запит.
Обговоримо задачу: що потрібно для оцінки та плану
Оцінка робіт із бекенду спирається на вхідні дані проєкту, а не на загальні побажання. Чим точніше описані сценарії та поточні показники, тим менше припущень у плані робіт і тим реалістичніші строки етапів і закладений в архітектуру запас за навантаженням.
Щоб обговорити задачу предметно, достатньо того, що вже є: опису процесів, доступів до репозиторію та середовищ, даних моніторингу.
Ідеальних документів не чекаємо — якщо чогось не вистачає, зафіксуємо припущення в плані робіт і уточнимо його на першому етапі, не зсуваючи загальний строк.
- Сценарії та ролі: які дії виконують користувачі, адміністратори та зовнішні системи, які операції критичні за часом відгуку.
- Профіль навантаження: пікові періоди, співвідношення читання й запису, сезонність, очікуване зростання кількості звернень.
- Поточний код: доступ до репозиторію, гілка з актуальним станом, міграції, залежності, вимоги до середовища виконання.
- Інтеграції: перелік зовнішніх сервісів, протокол обміну, сторона, що відповідає за їхню доступність і зміни.
- Інфраструктура та доступи: схема середовищ, доступ до логів і систем моніторингу, права для запуску навантажувальних перевірок.
- Обмеження: вимоги до безпеки, роботи з персональними даними, аудиту дій, регламент випуску оновлень.
- Поточні показники: час відгуку критичних операцій, частота інцидентів, відомі вузькі місця та обхідні рішення.
- Критерії приймання: який результат вважається досягнутим і за якими метриками ми його перевіряємо разом.
З цього набору збираємо план робіт: розбивку на етапи, їхні строки, склад команди та точки проміжної перевірки, де замовник бачить результат до фінальної здачі.







