Senior & Lead Engineer
Interview Mastery Hub
100 real-world, scenario-based architecture & concurrency questions designed for 5–10+ years experienced developers. Free & open for every student and engineer.
100
Real-world Q&As
100%
Scenario-Based
5-10 Yrs
Senior / Lead Depth
5 Stacks
Core Technologies
Showing 25 questions for senior interview preparation
How does Laravel Octane handle state persistence across requests, and how do you prevent catastrophic memory leaks and data bleeding in singletons?
Root Cause Analysis: Traditional PHP-FPM vs. Long-Running Octane
Under traditional PHP-FPM, the entire application lifecycle (memory, static variables, singletons) is instantiated on request arrival and wiped completely when the request terminates (shared-nothing architecture).
Under Laravel Octane (Swoole, RoadRunner, FrankenPHP), the PHP application boots once into memory. Worker processes persist across thousands of requests. Any state attached to a singleton, static property, or service provider persists across subsequent HTTP requests from completely different clients.
Critical Failure Vectors
- Data Bleeding: Storing request-scoped data (like
Auth::user()or tenant IDs) inside a singleton service constructor or static class property. Subsequent requests will read the prior user's cached identity. - Memory Leak: Appending items into static array caches (
private static array $cache = []) or listening to events without unregistering listeners.
Architectural Solution
-
Use
bindScopedInstead ofsingleton: Laravel Octane resets scoped bindings between requests:// BAD in Octane (persists across requests) $this->app->singleton(TenantManager::class, fn () => new TenantManager($request->header('X-Tenant-ID'))); // GOOD in Octane (purged after each request finishes) $this->app->scoped(TenantManager::class, fn () => new TenantManager(request()->header('X-Tenant-ID'))); -
Octane Warmers & Listeners: Flush state explicitly in
config/octane.phplisteners:OperationTerminated::class => [ FlushTemporaryCaches::class, ResetTenantContext::class, ], -
Pass Callbacks or Resolvers: Instead of caching an instantiated
$userin a service, pass aClosureor resolve dynamically viaAuth::user().
How do you prevent Deadlocks when processing concurrent financial transactions or inventory deductions across multiple queue workers in Laravel?
Why Deadlocks Occur in Concurrent Workers
A deadlock occurs when Worker A holds a lock on Resource 1 and requests Resource 2, while Worker B holds a lock on Resource 2 and requests Resource 1. Neither can proceed.
Battle-Tested Remediation Strategy
-
Strict Ordering of Locks: Always sort entity IDs before acquiring database row locks (
lockForUpdate):// Sort items by ID so all workers acquire locks in the exact same sequence $itemIds = collect($cartItems)->pluck('product_id')->sort()->values(); DB::transaction(function () use ($itemIds) { $products = Product::whereIn('id', $itemIds)->lockForUpdate()->get(); // Deduct inventory safely }, 5); // 5 retries on deadlock -
Atomic SQL Decrements: Instead of
SELECT -> calculate in PHP -> UPDATE, perform atomic database decrements:$affected = DB::table('products') ->where('id', $productId) ->where('stock', '>=', $quantity) ->decrement('stock', $quantity); if ($affected === 0) { throw new OutOfStockException("Insufficient inventory."); } -
Redis Atomic Distributed Lock (Redlock / Mutex): Prevent queue workers from touching the same wallet simultaneously:
use Illuminate\Support\Facades\Redis; Redis::throttle("wallet-lock:{$userId}") ->block(10) ->allow(1) ->every(1) ->then(function () use ($userId, $amount) { // Execute transaction safely });
How does the Laravel Service Container resolve dependencies using Reflection, and what are the performance trade-offs in large applications?
How Container Resolution Works
- When you request a class (e.g.
app(PaymentService::class)), if no binding exists, the container checksisBuildable($concrete). - It instantiates
ReflectionClass($concrete). - It inspects
getConstructor()and iterates through parameters viaReflectionParameter. - It recursively resolves parameter typehints from the container or defaults until the dependency graph is fully satisfied.
Optimization Strategies for Large Applications
- Service Provider Deferral: Use
\Illuminate\Contracts\Support\DeferrableProviderso large service providers are only loaded when their bound service is explicitly requested. - Route & Config Caching:
php artisan route:cache,php artisan config:cacheavoids repetitive reflection scans on routes and controllers. - Explicit Bindings: Direct closures (
$this->app->bind(Service::class, fn () => new Service(...))) skip reflection overhead entirely.
How do you execute Zero-Downtime database migrations on tables with 50+ million rows without locking writes in production Laravel?
Root Cause
Standard ALTER TABLE statements in MySQL can acquire exclusive metadata locks (MDL), blocking all incoming INSERT/UPDATE operations until the table rewrite finishes.
Safe Migration Patterns
- Online DDL with Algorithm=INPLACE, Lock=NONE:
ALTER TABLE orders ADD COLUMN fulfillment_status VARCHAR(30) DEFAULT 'pending', ALGORITHM=INPLACE, LOCK=NONE; - The Expand/Contract (Blue-Green Schema) Pattern:
- Step 1 (Expand): Add the new column as
NULLABLE(instant operation). - Step 2 (Dual-Write): Write to both old and new columns via model events or repository layer.
- Step 3 (Backfill in Background Queues): Chunk existing 60M rows with a queue worker in batches of 5,000 using ID-based cursor pagination.
- Step 4 (Switch Reads): Deploy code pointing all read queries to the new column.
- Step 5 (Contract): Drop the old column after zero dependencies remain.
- Step 1 (Expand): Add the new column as
- Use GitHub's
gh-ostor Perconapt-online-schema-change: Creates an ghost shadow table, triggers/binlog replays changes, and performs an atomic table swap without holding locks.
How do you architect Multi-Tenant database isolation in Laravel (Single DB vs Multi DB vs Schema Separation)?
Comparison of Multi-Tenancy Models
| Strategy | Pros | Cons | Ideal For |
|---|---|---|---|
| Single DB (Column Tenant ID) | Low cost, simple migrations, aggregate reporting easy | High risk of data leaks if scope missed, table bloat | B2C or low-tier SaaS |
| Multi-DB (Isolated Databases) | Complete data security, per-tenant backups, GDPR compliant | Migration latency across 10k DBs, connection overhead | Enterprise B2B SaaS |
| PostgreSQL Schemas | Single DB connection pool, isolated table namespaces | Migration complexity | Hybrid enterprise |
Multi-DB Switching Implementation in Laravel
class SwitchTenantConnection
{
public function handle(Request $request, Closure $next): Response
{
$tenant = Tenant::where('domain', $request->getHost())->firstOrFail();
// Dynamically purge and reconnect
Config::set('database.connections.tenant.database', $tenant->database_name);
DB::purge('tenant');
DB::reconnect('tenant');
DB::setDefaultConnection('tenant');
return $next($request);
}
}
Explain the Pipeline pattern in Laravel and how middleware executes using Onion Architecture.
Onion Architecture in Laravel
In Laravel, HTTP requests pass through middleware like layers of an onion. Middleware executes in sequence going inward, reaches the core route handler, and returns outward in reverse order.
Using Pipeline for Custom Business Logic
$sanitizedOrder = app(Pipeline::class)
->send($rawPayload)
->through([
StripHtmlTags::class,
NormalizePhoneNumbers::class,
ApplyTaxExemptions::class,
ValidatePromoCodes::class,
])
->then(fn ($payload) => Order::create($payload));
Each pipe implements a handle($passable, Closure $next) method:
class NormalizePhoneNumbers
{
public function handle($payload, Closure $next)
{
$payload['phone'] = preg_replace('/[^0-9+]/', '', $payload['phone']);
return $next($payload);
}
}
How do you debug and resolve Queue Worker starvation and RabbitMQ/Redis Queue backpressure in Laravel?
Solution: Strict Queue Prioritization & Dedicated Worker Pools
- Never dump all jobs into a single
defaultqueue. - Partition queues by SLA:
high,default,low,notifications:SendPasswordResetNotification::dispatch($user)->onQueue('high'); SendMarketingNewsletter::dispatch($campaign)->onQueue('low'); - Configure Supervisor with weighted prioritization:
# Dedicated High-Priority worker php artisan queue:work --queue=high,default,low --tries=3 --timeout=60 - Queue Rate Limiting:
public function middleware(): array { return [(new WithoutOverlapping($this->userId))->releaseAfter(60)]; }
How does Laravel’s Eloquent ORM prevent N+1 queries during polymorphic and nested eager loading with custom constraints?
Constrained Nested & Morph Eager Loading
$posts = Post::query()
->with([
'author:id,name,avatar',
'comments' => function ($query) {
$query->where('is_approved', true)
->latest()
->take(5)
->with(['user:id,name']);
},
'imageable' => function (MorphTo $morphTo) {
$morphTo->morphWith([
Post::class => ['tags'],
User::class => ['profile'],
]);
}
])
->select(['id', 'user_id', 'title', 'slug', 'created_at'])
->paginate(20);
Prevention in Development:
Enforce strict loading in AppServiceProvider::boot():
Model::preventLazyLoading(! app()->isProduction());
How do you handle Cache Stampede (Dog-Piling) in Laravel when high-traffic cache keys expire simultaneously?
The Cache Stampede Problem
When a hot cache item expires, hundreds of incoming concurrent threads miss the cache at the exact same millisecond and all attempt to compute the expensive query and write back to cache simultaneously.
Solution 1: Cache::flexible() in Modern Laravel
Laravel provides stale-while-revalidate out of the box:
$stats = Cache::flexible('homepage_stats', [300, 600], function () {
return DB::table('orders')->selectRaw('count(*), sum(amount)')->first();
});
// Serves stale cache instantly while dispatching background regeneration after 300s
Solution 2: Atomic Cache Locking (Cache::lock)
$data = Cache::get('homepage_stats');
if (! $data) {
$lock = Cache::lock('lock:homepage_stats', 10);
if ($lock->get()) {
try {
$data = ComputeExpensiveStats();
Cache::put('homepage_stats', $data, now()->addMinutes(10));
} finally {
$lock->release();
}
} else {
// Sleep briefly and read cache populated by the winning thread
usleep(200000);
$data = Cache::get('homepage_stats');
}
}
Explain the architectural differences between Laravel Events vs. Model Observers vs. Action Classes.
Comparison Matrix
| Pattern | Best Used For | Warning / Anti-Pattern |
|---|---|---|
| Model Observers | Passive lifecycle tracking (Audit logs, cache clearing, slug auto-generation) | Calling external APIs or triggering billing inside observers. Observers fire during seeders/batch updates and are hard to trace. |
| Events & Listeners | Asynchronous, cross-boundary side effects (Sending email after order placed, push notifications) | Synchronous coupling where the caller expects immediate return value. |
| Action Classes (Single Responsibility) | Core domain business operations (e.g. RegisterUserAction, ProcessRefundAction) |
Keep business logic testable and explicit with typed parameters. |
How do you safely scale Laravel Horizon with Redis Clusters when handling 50,000 jobs per minute?
Redis Cluster Hash Tag Pinning
In Redis Cluster, commands operating on multiple keys (like atomic queue pops) require all keys to reside on the same cluster node. Use Redis Hash Tags {horizon}:
'prefix' => '{horizon}:',
This forces all related Horizon state keys into the same hash slot, eliminating MOVED cluster exceptions while scaling workers seamlessly across nodes.
How do you implement Idempotent API endpoints in Laravel to prevent duplicate charges upon client network retries?
Idempotency Key Middleware Implementation
class EnsureIdempotency
{
public function handle(Request $request, Closure $next): Response
{
$idempotencyKey = $request->header('Idempotency-Key');
if (! $idempotencyKey) {
return response()->json(['error' => 'Idempotency-Key header is required'], 400);
}
$cacheKey = "idempotency:{$request->user()->id}:{$idempotencyKey}";
$lock = Cache::lock("lock:{$cacheKey}", 15);
if (! $lock->get()) {
return response()->json(['error' => 'Concurrent request in progress'], 409);
}
if ($cachedResponse = Cache::get($cacheKey)) {
$lock->release();
return response()->json($cachedResponse['data'], $cachedResponse['status']);
}
$response = $next($request);
if ($response->isSuccessful()) {
Cache::put($cacheKey, [
'data' => json_decode($response->getContent(), true),
'status' => $response->getStatusCode(),
], now()->addHours(24));
}
$lock->release();
return $response;
}
}
How do you configure and optimize Laravel for High-Concurrency Microservices using gRPC and HTTP/2?
gRPC with Protocol Buffers in PHP
- Binary Serialization vs JSON: Protocol Buffers serialize payloads into compact binary format, reducing payload size by 70% and CPU deserialization time by 80%.
- HTTP/2 Multiplexing: Multiple bidirectional gRPC RPC calls share a single persistent TCP connection, preventing ephemeral port exhaustion.
- RoadRunner gRPC Plugin: Run Laravel worker processes through Spiral RoadRunner gRPC server for native C-level gRPC transport.
How do you handle Dynamic Database Connection Routing (Read/Write Splitting) and Replica Lag in Laravel?
Sticky Connections in Laravel config/database.php
Enable the sticky option:
'mysql' => [
'read' => [
'host' => ['192.168.1.10', '192.168.1.11'],
],
'write' => [
'host' => ['192.168.1.5'],
],
'sticky' => true, // Reads in the same request/lifecycle immediately route to Write DB
],
Force Primary Read on Critical Reads:
$user = User::onWriteConnection()->find($id);
Explain the internal architecture of Laravel Broadcast Channels, Pusher, and Laravel Reverb WebSockets.
Laravel Reverb Architecture
Laravel Reverb is a first-party, high-performance WebSocket server written directly in PHP using asynchronous event loops.
- Epoll Event Loop: Capable of sustaining tens of thousands of open TCP sockets on a single server.
- Horizontal Scaling via Redis Pub/Sub: When server A broadcasts an event, it publishes to Redis; Server B, C, and D receive the pub/sub notification and relay the frame to their local connected client sockets.
- Zero Third-Party Billing: Direct drop-in replacement for Pusher.
How do you design a robust Event-Sourced audit trail with CQRS in Laravel without third-party monolith packages?
Event Sourcing Core Principles
- Append-Only Event Store: State changes are stored as immutable past-tense events (
PatientDiagnosed,PrescriptionIssued). The database table has NOUPDATEorDELETEpermissions. - Replaying State:
$record = new MedicalRecord(); foreach ($eventStream as $event) { $record->apply($event); } - Projections (Read Models): Asynchronous queue listeners consume domain events and project denormalized read-optimized views into Elasticsearch or Postgres for fast querying.
How does Laravel Sanctum issue and validate SPA cookies vs Mobile Personal Access Tokens?
Sanctum Dual-Mode Authentication
- SPA (Cookie-Based Stateful Auth):
- Client calls
GET /sanctum/csrf-cookieto setXSRF-TOKENcookie. - Client includes
X-XSRF-TOKENheader andwithCredentials: true. - Domain must match
SANCTUM_STATEFUL_DOMAINSin.env.
- Client calls
- Mobile Apps (Bearer Tokens):
- Issue token via
$user->createToken('iphone')->plainTextToken. - Stored in hashed format in
personal_access_tokenstable. - Included as
Authorization: Bearer <token>header without CSRF requirements.
- Issue token via
How do you optimize Laravel Eloquent Subqueries and Window Functions for enterprise analytics dashboards?
Utilizing selectSub and addSelect with Window Functions
$rankedStudents = User::query()
->select('id', 'name', 'email')
->addSelect([
'last_quiz_score' => QuizAttempt::select('score')
->whereColumn('user_id', 'users.id')
->latest('completed_at')
->limit(1),
'rank' => DB::table('quiz_attempts')
->selectRaw('DENSE_RANK() OVER (ORDER BY SUM(score) DESC)')
->whereColumn('user_id', 'users.id')
])
->orderByDesc('rank')
->take(100)
->get();
Avoids loading intermediate Eloquent collections in memory.
How do you securely implement dynamic custom validation rules in Laravel that query external Microservice APIs?
Implementing Invokable Rule with Circuit Breaker & Timeout
class ValidEnterpriseTaxNumber implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try {
$response = Http::timeout(2)
->retry(2, 100)
->get("https://api.taxservice.com/validate/{$value}");
if (! $response->successful() || ! $response->json('is_valid')) {
$fail("The {$attribute} is not a verified business tax entity.");
}
} catch (\Throwable $e) {
// Log warning and fail gracefully or flag for manual review
Log::warning("Tax API unavailable: " . $e->getMessage());
}
}
}
How do you implement Dynamic Multi-Factor Authentication (MFA/TOTP) with backup recovery codes in Laravel 11/12/13?
TOTP Implementation Workflow
- Secret Generation: Generate 32-character base32 secret and store encrypted in database (
Crypt::encryptString($secret)). - QR Code Provisioning: Generate
otpauth://totp/App:user@email?secret=...&issuer=App. - Verification: Validate code within drift window (+/- 30 seconds).
- Hashed Recovery Codes: Generate 8 random recovery codes, hash them with
Hash::make()before saving, and burn upon single use.