Питання
Найпопулярніші питання з реальних Laravel/PHP співбесід для всіх рівнів
121 питань
DDD фокусується на моделюванні бізнес-домену спільною мовою з експертами. Ключові поняття: Entities, Value Objects, Aggregates, Domain Events, Bounded Contexts.
У Laravel це зазвичай означає відхід від стандартної структури (app/Models, app/Http) на користь організації за доменами:
app/Domain/Ordering/
Models/Order.php
Actions/PlaceOrder.php
ValueObjects/Money.php
Events/OrderPlaced.php
- Бізнес-логіка живе в домені, а не в контролерах чи моделях-«божках».
- Контролери стають тонкими адаптерами, що викликають доменні дії.
DDD виправданий у складних доменах; для CRUD він додає зайвий оверхед.
Деплой без простою: користувачі весь час бачать робочу версію.
Atomic (symlink) deploy - кожен реліз клонується в нову папку, там встановлюються залежності й збираються ассети, після чого current атомарно перемикається через symlink:
releases/2026_06_05_120000/ ← новий
current → releases/... ← атомарне перемикання
Кроки на деплої: composer install --no-dev, npm run build, migrate --force, кеш конфіг/маршрутів, перезапуск воркерів (queue:restart) і OPcache.
Інструменти: Envoyer, Deployer, CI/CD-пайплайни, Kubernetes (rolling update). Окрема увага - сумісність міграцій із попередньою версією коду під час перемикання.
Idempotency (ідемпотентність) - багаторазове виконання операції дає той самий результат, що й однократне. Критично для платежів і повторів задач у чергах (де доставка «at least once»).
Реалізація для API - idempotency key:
$key = $request->header('Idempotency-Key');
return Cache::lock("idem:$key")->block(5, function () use ($key) {
if ($cached = Cache::get("idem:result:$key")) {
return $cached; // повернути попередній результат
}
$result = $this->charge(); // виконати один раз
Cache::put("idem:result:$key", $result, now()->addDay());
return $result;
});
Для завдань: перевірка «вже оброблено» за унікальним ключем, ShouldBeUnique, або БД-обмеження, що відсікають дублі.
Ключове правило - не тримати весь файл у пам'яті.
- Стрімінг замість читання цілком:
return Storage::disk('s3')->response($path); // стрім на скачування Storage::writeStream($path, fopen($source, 'r')); // стрім на запис - Direct uploads на S3 - клієнт вантажить напряму в сховище за pre-signed URL, минаючи PHP-процес (не блокує воркер, обходить ліміти
upload_max_filesize). - Chunked upload - великі файли частинами (resumable).
- Фонова обробка - конвертацію відео/зображень виносити в черги.
- Враховувати
max_execution_time, таймаути nginx і ліміти пам'яті воркера.
П'ять принципів ООП-дизайну. У Laravel вони реалізуються природно завдяки сервіс-контейнеру.
S - Single Responsibility: клас має одну причину для зміни. На практиці - виносити бізнес-логіку з «товстих» контролерів у Service/Action-класи, валідацію - у Form Requests, логіку життєвого циклу моделі - в Observers.
O - Open/Closed: відкритий для розширення, закритий для модифікації. Приклад - драйвери Laravel (cache, queue, filesystem): новий драйвер додається через extend(), не змінюючи ядро.
L - Liskov Substitution: реалізації взаємозамінні через спільний інтерфейс без поломки логіки - напр., будь-який драйвер кешу можна підставити замість іншого.
I - Interface Segregation: багато вузьких інтерфейсів краще за один «товстий»; клас не має реалізовувати методи, які не використовує.
D - Dependency Inversion: залежати від абстракцій, а не від реалізацій. Сервіс-контейнер - пряме втілення:
class OrderController
{
public function __construct(private PaymentGateway $gateway) {} // інтерфейс
}
$this->app->bind(PaymentGateway::class, StripeGateway::class); // реалізація в провайдері
Користь: тестованість (легко підставити mock), гнучкість (зміна реалізації в одному місці). Водночас Senior знає, коли не переускладнювати - надмірна абстракція заради «чистоти» шкодить не менше за її відсутність.
Telescope - інструмент дебагу/спостереження. Збирає й показує у дашборді: запити, винятки, SQL-запити (з часом і дублями), завдання черг, листи, нотифікації, кеш, події, HTTP-клієнт.
php artisan telescope:install
- Незамінний для пошуку N+1 (бачиш усі запити сторінки), повільних місць, помилок у чергах.
- Дані зберігаються в БД; у проді обмежують доступ через gate
viewTelescopeта вмикають sampling, бо обсяг записів великий.
На відміну від Horizon (керування чергами), Telescope - про діагностику всього застосунку. У продакшені часто доповнюють зовнішнім APM (Sentry, Datadog).
У кластері з кількох інстансів локальний лічильник не годиться - потрібен спільний стан у Redis. Фасад RateLimiter під капотом використовує атомарні операції Redis (INCR + EXPIRE), тож підрахунок точний між усіма серверами.
RateLimiter::attempt(
key: 'send-sms:'.$user->id,
maxAttempts: 5,
callback: fn () => $this->sendSms(),
decaySeconds: 60,
);
Для складніших схем - алгоритми sliding window чи token bucket на Lua-скриптах (атомарність на стороні Redis, без гонок). Ключове: лічильник і його TTL мають змінюватися атомарно, інакше за конкурентного доступу ліміт «протікає».
Circuit Breaker захищає від каскадних збоїв при зверненні до ненадійної залежності (зовнішнє API, що «лежить»). Якщо помилок забагато - «ланцюг розривається», і запити певний час відхиляються миттєво, не витрачаючи ресурси на марні спроби.
Стани:
- Closed - усе працює, запити йдуть.
- Open - поріг помилок перевищено; запити одразу падають (fail fast).
- Half-Open - через таймаут пропускаються пробні запити; успіх → Closed, провал → знову Open.
// концептуально через Cache як лічильник збоїв
if (Cache::get('cb:payments') === 'open') {
throw new ServiceUnavailableException;
}
У Laravel реалізують через лічильники в Redis/Cache або пакети-обгортки HTTP-клієнта. Часто поєднують із retry + backoff.
CI автоматично перевіряє кожен пуш, CD - автоматично доставляє код.
Типовий пайплайн (GitHub Actions / GitLab CI):
- composer install
- vendor/bin/pint --test # стиль
- vendor/bin/phpstan analyse # статичний аналіз
- php artisan test --parallel # тести
- npm ci && npm run build # ассети
# → деплой при успіху
Деплой: SSH-скрипт, Docker-образ у реєстрі + rolling update у Kubernetes, або сервіси на кшталт Forge/Envoyer. На етапі деплою - migrate --force, кешування конфіга/маршрутів, queue:restart. CI/CD дає швидкий зворотний зв'язок і знижує ризик людської помилки при релізі.
- Stateful - сервер зберігає стан клієнта між запитами (наприклад, сесія у файлі/пам'яті конкретного інстансу). Тоді потрібна «липкість» (sticky sessions) або спільне сховище.
- Stateless - сервер не зберігає стану; кожен запит самодостатній і містить усе потрібне (наприклад, JWT/токен з даними автентифікації).
Stateful: сесія на сервері → потрібен спільний Redis/sticky LB
Stateless: токен у запиті → будь-який інстанс обробить запит
Stateless легше масштабувати горизонтально - інстанси взаємозамінні. У Laravel веб-частина зазвичай stateful (сесії в Redis), API - stateless (Sanctum-токени). Для масштабування цей стан виносять у спільні Redis/БД.
Sharding - горизонтальне розбиття даних між кількома незалежними БД (шардами) за ключем (tenant_id, user_id, geo). Дозволяє вийти за межі одного сервера.
shard 1: користувачі 1–1M
shard 2: користувачі 1M–2M
- Shard key обирають так, щоб дані рівномірно розподілялись і більшість запитів влучали в один шард.
- Складнощі: запити між шардами, унікальність ID (часто UUID/Snowflake), ребалансування при додаванні шарда, відсутність крос-шардових операцій JOIN і транзакцій.
У Laravel реалізують через множинні з'єднання й маршрутизацію за shard key. Sharding - крайній захід, коли вертикальне масштабування та репліки вже не справляються.
Версіонування дозволяє розвивати API, не ламаючи наявних клієнтів. Стратегії:
URI versioning (найпоширеніше) - версія в шляху:
Route::prefix('v1')->group(base_path('routes/api_v1.php'));
Route::prefix('v2')->group(base_path('routes/api_v2.php'));
Header/Media-type versioning - Accept: application/vnd.app.v2+json. Чистіші URL, але складніше тестувати.
Практики:
- Окремі неймспейси контролерів і API Resources на версію (
V1\PostResource,V2\PostResource). - Бізнес-логіку виносити в спільні Action/Service, щоб не дублювати між версіями.
- Політика deprecation: підтримувати стару версію певний строк, повертати заголовки
Deprecation/Sunset.
Deadlock - дві транзакції взаємно блокують одна одну, чекаючи на ресурси, які тримає інша. СУБД виявляє це й «вбиває» одну з транзакцій.
Запобігання:
- Єдиний порядок доступу до таблиць/рядків у всіх транзакціях.
- Тримати транзакції короткими, блокувати якомога пізніше.
- Правильні рівні ізоляції (не завищувати без потреби).
Обробка в Laravel - автоматичний повтор:
DB::transaction(function () {
// ...
}, attempts: 3); // повторити при deadlock
Діагностика: SHOW ENGINE INNODB STATUS (MySQL), логи БД, моніторинг частоти deadlock. Інколи допомагає optimistic locking замість тривалих блокувань.
Serverless виконує код без керування серверами: провайдер (AWS Lambda) сам масштабує під навантаження й тарифікує за фактичні виклики.
Laravel Vapor - платформа для деплою Laravel на AWS Lambda + API Gateway, з керованими БД, чергами (SQS), кешем і CDN.
Особливості й обмеження:
- Авто-масштабування до нуля й під пік; платиш лише за використання.
- Cold start - затримка першого запиту після простою.
- Файлова система ефемерна → файли лише в S3.
- Обмеження часу виконання Lambda → довгі задачі в черги.
- Stateless за дизайном (сесії/кеш у Redis/DynamoDB).
Альтернатива - контейнери (ECS/Kubernetes), коли потрібен повний контроль або стабільні довготривалі процеси.
WebSocket - постійне двостороннє з'єднання поверх одного TCP, що дає реальний час без поллінгу.
У Laravel сервер WebSockets - Reverb (офіційний), Soketi або Pusher; події транслюються через Broadcasting, клієнт слухає через Echo.
Масштабування: коли інстансів WebSocket-сервера кілька, клієнти на різних інстансах не «бачать» одне одного. Рішення - Redis Pub/Sub як спільна шина: інстанс публікує повідомлення в Redis, усі інстанси отримують і розсилають своїм підключеним клієнтам.
client A ─ inst 1 ┐
├─ Redis Pub/Sub ─┤
client B ─ inst 2 ┘
Окрема увага: авторизація private/presence-каналів, ліміти відкритих з'єднань, sticky sessions на балансувальнику.