Laravel interviews should show whether a developer can turn a request into a secure, reliable application outcome—not simply name Artisan commands. Recruiters can distinguish hands-on experience by asking candidates to explain an Eloquent query, an authorization boundary, and what happens when a queued job runs twice. The framework version and hosting model matter, especially when comparing traditional PHP requests with long-running workers.
This guide to 100+ Laravel Interview Questions and Answers contains 120 questions across beginner, intermediate, and experienced levels. It covers the application lifecycle, dependency injection, routing, validation, Blade, Eloquent, database consistency, authentication, queues, testing, and production operations. Use the levels to match the role, then ask candidates to explain the assumptions behind their code.
How can recruiters identify potential AI cheating, deepfakes, impersonation, and unauthorized assistance during Laravel interviews?
Ask a candidate to trace middleware execution, repair an N+1 query, or explain why a retried payment job must be idempotent. Memorized framework vocabulary is not the same as independently reasoning about these implementation details.
Sherlock AI helps recruiters detect potential AI-assisted cheating, deepfakes, candidate impersonation, and unauthorized real-time assistance during Laravel interviews.
Combine integrity checks with a focused code review and follow-up questions about authorization, database behavior, and worker lifecycle. Evaluate suspicious signals in context rather than treating any single signal as proof of misconduct.
Laravel Interview Questions
Choose questions around the application the developer will maintain. State the Laravel and PHP versions, database engine, authentication model, queue backend, and hosting model before comparing implementation choices.
Laravel Interview Questions for Beginners
Establish the request lifecycle, framework vocabulary, safe input handling, persistence basics, and essential web security.
What is Laravel, and when would you choose it?
How does Laravel use the MVC pattern?
Which PHP and Laravel versions should these examples target?
What do Composer and Artisan do in a Laravel project?
Which directories and bootstrap files matter in a modern Laravel application?
What happens during a Laravel HTTP request?
What is Laravel's service container, and how does dependency injection work?
What is a service provider, and how do register and boot differ?
What is a Laravel facade, and is it the same as a static class?
How should Laravel applications use .env files and configuration?
How do you define routes and distinguish web routes from API routes?
Why use named routes and route groups?
What is implicit route model binding?
What belongs in a controller, and what is a resource controller?
What is middleware, and where is it configured in Laravel 13?
How do you read and validate request input safely?
How do Laravel controllers return views, JSON, and redirects?
What happens when Laravel validation fails?
When should you use a Form Request?
What is Blade, and how do layouts and components help?
How does Blade escape output, and what does that prevent?
How do you protect HTML forms from CSRF in Laravel 13?
What is Eloquent, and how does a model map to a database table?
How do you create, read, update, and delete an Eloquent model?
What is mass assignment, and why do fillable attributes matter?
What are Eloquent attribute casts?
What is a Laravel collection, and how is it different from a query builder?
How do you define a basic one-to-many Eloquent relationship?
What are migrations, and how do up and down work?
How do seeders and model factories differ?
How do you paginate a Laravel list?
What is the difference between authentication and authorization?
How should passwords be stored and checked in Laravel?
How do sessions and flash data work?
How should a beginner log useful information without exposing secrets?
What is the difference between a unit test and a feature test?
What do APP_KEY and encryption do, and why is key:generate not a routine production fix?
How do Laravel views receive data from a controller?
How do you generate URLs without hard-coding paths?
How are application errors handled, and why should debug mode be disabled in production?
Laravel Interview Questions for Intermediate
Explore implementation choices in data access, validation, authentication, queues, caching, storage, and automated tests.
How do you choose between bind, singleton, and scoped container bindings?
When would you use contextual binding instead of a single interface binding?
How do middleware order and configuration affect a request?
How do scoped route model bindings protect nested resource routes?
How do you validate nested input without accepting unexpected keys?
How do you validate uniqueness safely when updating a record?
How should API resources control fields and relationship loading?
How do you diagnose and fix an Eloquent N+1 query problem?
When should you use withCount, whereHas, and withWhereHas?
How do local scopes differ from global scopes?
When should you use an accessor, a mutator, or a cast?
How do polymorphic relationships work, and why use a morph map?
How do you update a many-to-many relationship and its pivot data?
When should you choose the query builder instead of Eloquent?
How do indexes and query plans guide Laravel query optimization?
How do chunkById, lazyById, and cursor differ for large datasets?
How do you choose between offset and cursor pagination?
How do foreign keys and unique indexes complement validation?
How should a Laravel database transaction handle exceptions and retries?
When is pessimistic locking appropriate in a Laravel transaction?
What is the difference between an authentication guard and a user provider?
How do you enforce record-level authorization using policies?
How does Sanctum authentication differ for first-party SPAs and API tokens?
How do you design a user-aware API rate limiter?
What data should a queued job contain, and what does model serialization imply?
How do you configure job attempts, backoff, and timeouts?
Why dispatch a job after a database transaction commits?
How do events and queued listeners separate application responsibilities?
How should a scheduled Laravel task be configured and operated?
How do you cache a query result and invalidate it correctly?
What is the difference between cache data, a cache lock, and the session?
How do you store and deliver private files through Laravel disks?
How do you validate and store a user-uploaded image safely?
How should Laravel's HTTP client handle failures and timeouts?
How do you isolate Laravel test environments and parallel test databases?
How do factories and RefreshDatabase support reliable database tests?
How do you test authentication, authorization, and validation failures?
How do queue and event fakes help test behavior without hiding bugs?
How do you test file uploads and outbound HTTP requests?
When should application logic use a service class, an observer, or an event listener?
Laravel Interview Questions for Experienced
Use production scenarios to assess consistency, tenant isolation, worker state, resilience, deployment, observability, and architectural trade-offs.
How would you structure a large Laravel application without prematurely creating microservices?
How would you prevent cross-tenant data leakage in a Laravel SaaS application?
How would you make an inventory reservation safe under concurrent requests?
How would you design an idempotent payment endpoint when the provider can time out?
What problem does a transactional outbox solve, and how would you implement one?
How would you coordinate a multi-service workflow when a single database transaction is impossible?
Where do Laravel database transactions stop protecting application consistency?
How would you diagnose and reduce deadlocks in a busy Laravel application?
How would you perform a zero-downtime database schema change?
What consistency problems arise with read replicas, and how would you address them?
How would you investigate a slow endpoint before adding a cache?
How would you make a queued job safe when it is delivered more than once?
How should retry, timeout, and backoff settings work together?
How would you handle poison jobs and safely replay failed work?
How do unique jobs differ from overlap prevention and business-level deduplication?
How would you preserve business ordering while scaling queue workers?
How would you configure and operate Horizon without overloading downstream systems?
How would you run scheduled work safely on multiple application servers?
How would you prevent a cache stampede after a popular entry expires?
How would you design cache invalidation for tenant-specific mutable data?
What can go wrong with a distributed lock even when you use Laravel's lock API?
What changes when a Laravel application runs under Octane?
How would you find and fix a memory leak in long-running Laravel processes?
How would you stop a singleton from retaining another request’s tenant or user context?
How would you handle configuration caching and secret rotation in production?
How would you deploy Laravel code while old queue workers are still running?
How would you implement graceful shutdown and meaningful health checks?
How would you trace a request across HTTP calls and asynchronous jobs?
How would you make logs and error reporting useful without exposing secrets?
How would you defend a Laravel URL-fetching feature against SSRF?
How would you securely process untrusted file uploads?
How would you prevent authorization bugs in a multi-role, multi-tenant API?
How would you manage the lifecycle of Sanctum personal access tokens?
How would you test concurrency and database-specific behavior reliably?
How would you test external API contracts without making the suite brittle?
How would you test expiry, delayed work, and time-sensitive behavior?
How would you investigate a production latency spike safely?
How would you capacity-plan and load-test a Laravel application?
How would you plan a Laravel major-version upgrade with a controlled rollback?
When would you use Laravel concurrency features, and what should remain sequential?
Laravel Interview Questions and Answers
These answers follow the question bank exactly and begin with direct responses candidates can adapt. Unless stated otherwise, the baseline is Laravel 13 with PHP 8.4 and the modern application skeleton. Laravel 13 requires PHP 8.3 or later within its supported range. PHP namespace use imports belong at file scope, outside class and method bodies. PHP and Blade snippets are independent excerpts; named application models, tables, policies, services, and routes must exist as described rather than being supplied by the framework.
Laravel Interview Questions and Answers for Beginners
These answers connect core Laravel features to observable application behavior and distinguish modern configuration from older skeleton conventions.
1. What is Laravel, and when would you choose it?
Laravel is a PHP web application framework that supplies the common infrastructure behind a website or API: routing, dependency injection, database access, validation, authentication integration, and testing tools. I choose it when the application benefits from an integrated set of conventions rather than assembling each capability independently.
For example, an internal recruiting application needs candidate records, protected dashboards, email notifications, and background processing. Laravel provides building blocks for those concerns, while the application still owns its hiring rules and access decisions. It can render HTML with Blade or serve a separate frontend through JSON endpoints.
The trade-off is that developers must understand the framework's conventions and operational requirements. Laravel does not automatically make inefficient queries fast or an incorrectly authorized endpoint secure. I would evaluate team experience, hosting, integrations, and workload before choosing it.
2. How does Laravel use the MVC pattern?
MVC separates the model, view, and controller responsibilities. In a Laravel application, an Eloquent model represents persisted data and its relationships, a Blade view presents information, and a controller coordinates an HTTP request and response.
For a candidate details page, the route selects a controller action, the action obtains an authorized candidate, and a view renders the permitted fields. The model should not generate HTML, and the view should not decide whether the visitor may access confidential data. An API can use the same request coordination and model layer but return JSON instead of a Blade view.
MVC is an organizational pattern, not a rule that every business operation must fit in three classes. I move reusable workflows into focused application or domain services when that makes dependencies and tests clearer, while keeping controllers readable.
3. Which PHP and Laravel versions should these examples target?
These examples target Laravel 13 with PHP 8.4. Laravel 13 supports PHP 8.3 through 8.5, so PHP 8.4 is a deliberate supported baseline rather than the framework's minimum requirement. The release line began in March 2026.
When joining an existing project, I first inspect its Composer constraints and lock file, then check the installed runtime and framework:
Bash command
Run these from an installed project's environment; the final command checks the actual PHP platform and extension requirements of installed dependencies. A compatible PHP version alone is not enough if required extensions are missing.
I also match documentation to the project's major version. Older applications may retain their original structure after an upgrade, so I would not assume their bootstrap files look exactly like a newly created Laravel 13 application.
4. What do Composer and Artisan do in a Laravel project?
Composer manages PHP dependencies and autoloading; Artisan runs Laravel's application commands. They solve different problems. I use Composer to install framework and package code, and Artisan to inspect or operate the application that uses that code.
Bash command
In an existing project with a lock file, composer install restores its locked dependency versions. The Artisan commands list available commands, generate a controller class, and display registered routes. These examples assume PHP, Composer, and the project's required configuration are available.
composer update resolves versions again and can change the lock file, so it is not interchangeable with a reproducible deployment install. I commit the application lock file and review dependency changes. Generating a controller also does not create its routes, authorization rules, or business behavior automatically.
5. Which directories and bootstrap files matter in a modern Laravel application?
In a modern Laravel application, I start with routes, app, resources, and database. Routes define entry points; app contains application classes; resources/views contains Blade templates; and database holds migrations, factories, and seeders. public is the web server's document root, not the entire repository.
A fresh Laravel 13 skeleton uses bootstrap/app.php to configure routing, middleware, and exception handling, and bootstrap/providers.php lists application service providers. Configuration values belong in config; generated logs, compiled views, and other runtime files commonly live under storage.
This differs from tutorials that require editing an application HTTP kernel or registering every provider in config/app.php. Those may describe older skeletons. Upgraded applications can preserve older organization, so I inspect the actual bootstrap path rather than moving files merely to match a tutorial.
6. What happens during a Laravel HTTP request?
A Laravel HTTP request enters through public/index.php, which loads Composer's autoloader and obtains the application from bootstrap/app.php. The HTTP kernel bootstraps framework services, including configuration and registered providers, before the request reaches the routing pipeline.
Global middleware can inspect the request first. The router then finds a matching route, runs its applicable middleware, and invokes a closure or controller action. That action produces a response, which travels back through the middleware pipeline before being sent to the client. Middleware can stop processing early, such as returning a redirect for an unauthenticated browser request.
This sequence helps me debug failures systematically. If a controller is never reached, I check route matching and middleware before changing its code. If a dependency cannot be resolved, I check container registration and bootstrapping. Command-line execution uses a related console path, not the same browser request flow.
7. What is Laravel's service container, and how does dependency injection work?
Laravel's service container constructs objects and supplies their dependencies. Dependency injection means a class receives the services it needs instead of constructing or locating them internally. This makes dependencies visible and allows tests to substitute controlled implementations.
For example, assume the application defines a CandidateSearch interface and a DatabaseCandidateSearch implementation. In a provider's register method, I can bind them:
PHP example
A controller can then request CandidateSearch in its constructor, and Laravel resolves the configured implementation. Concrete classes whose constructor dependencies are resolvable can often be injected without an explicit binding. Interfaces usually need a binding because the container cannot infer which implementation I intend.
I would not inject a large generic service merely to hide unrelated dependencies. Small interfaces are useful when they express a real boundary, such as searching candidates through a database or an external provider.
8. What is a service provider, and how do register and boot differ?
A service provider connects application services to Laravel's bootstrapping process. I use register for container bindings and boot for initialization that may depend on services being available. Laravel registers providers before invoking their boot methods, which is why resolving unrelated services too early in register can cause problems.
Application providers are listed in bootstrap/providers.php in a fresh Laravel 13 application. The make:provider Artisan command generates and registers a new provider there.
For example, an application might bind a payment interface in register, then define a custom Blade directive in boot. Request-specific business actions, such as charging a card, do not belong in either method: bootstrapping can occur for commands and other execution paths as well as HTTP requests.
The distinction is about lifecycle responsibilities, not simply putting all setup code in whichever method happens to work first.
9. What is a Laravel facade, and is it the same as a static class?
A Laravel facade exposes convenient static-looking syntax for an object managed by the service container. It is not simply a collection of ordinary static utility methods. A call such as Cache::get() is forwarded through the facade to an underlying service.
PHP example
This retrieves the cached label, or the supplied default when the key is absent. It assumes the application has a working configured cache store. Importing the facade class makes clear which Cache name is being used.
Facades integrate with Laravel's testing facilities, so static-looking syntax does not mean the dependency is impossible to replace in tests. I still weigh readability: a constructor makes required collaborators explicit, while many facade calls scattered through a large class can hide how much that class does. Both styles can be appropriate when used consistently and deliberately.
10. How should Laravel applications use .env files and configuration?
I use environment variables for deployment-specific values, configuration files to organize those values, and config() to read them in application code. A plaintext .env file must not be committed with secrets; .env.example should document required keys using safe placeholders.
For example, create config/recruiting.php:
PHP example
Application code then reads config('recruiting.support_email'). This keeps deployment configuration separate from business logic.
The distinction matters after php artisan config:cache: Laravel loads the cached configuration rather than loading the .env file for each request. Calling env() directly from a controller can therefore behave differently after deployment; only externally supplied environment values remain available that way. Configuration changes require the deployment's configuration cache to be rebuilt, and long-running processes may need a restart. I also keep APP_DEBUG disabled in production.
11. How do you define routes and distinguish web routes from API routes?
A route maps an HTTP method and path to a closure or controller action. I use routes to define the public interface of the application, not to hide a large business workflow.
PHP example
A GET request to /status-label returns Ready. In the default configuration, routes/web.php receives the web middleware group, including session handling and request-forgery protection.
API routing is optional in a fresh modern skeleton. php artisan install:api installs Sanctum and creates routes/api.php; the default API routing configuration adds an /api prefix. API routes use the api group rather than automatically receiving the browser-session stack. I check bootstrap/app.php and route:list for the actual configuration, because projects can customize both groups. Returning JSON alone does not make a route stateless or authenticated.
12. Why use named routes and route groups?
Named routes give an endpoint a stable application-facing identifier, while groups apply shared settings to related routes. I use names in links and redirects so changing a URL does not require editing every caller. A group can share middleware, a URL prefix, and a route-name prefix.
PHP example
Assuming the controller action and authentication flow exist, this registers a protected GET endpoint at /reports named reports.index. route('reports.index') generates its URL. The dot in reports. is intentional: name prefixes are concatenated directly.
The auth middleware establishes a signed-in user; it does not automatically prove that user may view every report. Resource-specific permission checks still belong in the relevant authorization flow.
13. What is implicit route model binding?
Implicit route model binding turns a route parameter into an Eloquent model automatically. The parameter name must match the type-hinted variable, and Laravel normally looks up the model by its route key, which defaults to the primary key.
PHP example
Assume Article maps to an existing articles table and these articles are intentionally public. /articles/7 loads article 7, while a missing record produces a 404 rather than calling the closure with null.
Binding reduces repetitive lookup code, but it is not authorization. For private records, I still enforce a policy or equivalent ownership check. I also avoid blindly returning a complete model when the endpoint needs only a few public fields. Binding by a slug can be configured explicitly when the URL should not use an ID.
14. What belongs in a controller, and what is a resource controller?
A controller coordinates HTTP work: it receives validated input, enforces or invokes authorization, calls application behavior, and chooses a response. I keep transport details there while moving substantial reusable workflows into focused services rather than building a single oversized controller.
A resource controller follows the standard CRUD action names: index, create, store, show, edit, update, and destroy. For an API, the HTML form actions create and edit are usually unnecessary.
PHP example
With the corresponding implemented controller, this registers conventional API-style resource routes. It does not implement validation, persistence, authentication, or policy decisions. I inspect php artisan route:list to confirm the generated paths and methods. A resource route is a naming and routing convention, not a guarantee that the endpoint is secure or fully RESTful.
15. What is middleware, and where is it configured in Laravel 13?
Middleware runs around request handling and can inspect, reject, or modify a request and its response. Authentication, request-forgery protection, and shared request context are common examples. Middleware is appropriate for cross-cutting HTTP concerns, not every business rule.
In a fresh Laravel 13 application, I customize middleware through withMiddleware in bootstrap/app.php. For an existing application-defined middleware class, an alias can be added inside that callback:
PHP example
This is the callback body and signature, not a replacement for the entire bootstrap file. Routes can then attach ->middleware('staff'). The EnsureStaff implementation must actually perform the check. Order matters when one middleware depends on an authenticated user or an initialized session, so I verify the surrounding middleware stack rather than treating aliases as independent switches.
16. How do you read and validate request input safely?
I inject Illuminate\Http\Request, read input through its methods, and validate the fields that the operation accepts. Retrieving a value is not validation: input(), query(), and all() do not establish that data is well formed or permitted.
Inside a controller action receiving Request $request, a simple example is:
PHP example
Valid input returns the accepted display name; invalid input follows Laravel's validation error flow. nullable permits a missing or null optional biography rather than requiring text. The example demonstrates validation and a response, not persistence.
I use the validated result rather than forwarding the entire payload into a model. Authorization is separate: a correctly formatted request can still attempt an action that the user is not allowed to perform.
17. How do Laravel controllers return views, JSON, and redirects?
Laravel controllers return response objects or values Laravel converts into responses. I choose the response for the client: a view for server-rendered HTML, JSON for an API, or a redirect after a browser form submission.
These are separate return alternatives inside an action:
PHP example
The first requires a reports.index Blade view; the redirect requires a route with that name and a session-enabled web request for the flash message. They are alternatives, not three statements that will all execute. The JSON response explicitly uses HTTP 202 to describe an accepted asynchronous operation; actual queue submission must already have succeeded before returning that claim.
I also choose suitable status codes for creation, missing resources, and errors rather than returning HTTP 200 for every outcome.
18. What happens when Laravel validation fails?
Laravel's normal validation flow stops the action when validation fails. For a traditional web form, it redirects back and makes errors and appropriate old input available through the session. When the request expects JSON, it returns a structured validation response with HTTP 422 instead of redirecting to an HTML form.
A Blade field can show both the previous value and its error:
Code example (language tag: blade)
This assumes a session-enabled web validation redirect and a form with the required request-forgery protection. The snippets do not store the value. I avoid repopulating secrets such as passwords and do not turn every validation exception into a generic server error. An API client should request JSON, commonly with an Accept: application/json header, and handle field errors separately from transport failures.
19. When should you use a Form Request?
I use a Form Request when validation and request-level authorization deserve a named, testable class instead of crowding the controller. Laravel resolves the type-hinted Form Request and performs its authorization and validation before executing the action.
For a protected candidate-creation endpoint, the relevant methods in App\Http\Requests\StoreCandidateRequest, extending FormRequest, could be:
PHP example
This assumes an authenticated route and a configured Candidate policy with a create ability. A denied authorization returns 403; permitted valid input becomes available through $request->validated() in the controller. I keep ownership IDs and privileged fields out of user-controlled data unless the use case explicitly permits them. A Form Request organizes these checks but does not replace database constraints or downstream business rules.
20. What is Blade, and how do layouts and components help?
Blade is Laravel's server-side template engine. It compiles templates to PHP and provides readable directives for conditions, loops, reusable components, and layouts. It is not a client-side reactive framework, although it can be used alongside one.
An anonymous component at resources/views/components/notice.blade.php can contain:
Code example (language tag: blade)
A page uses it like this:
Code example (language tag: blade)
The title becomes component data and the content between the tags becomes its slot. This avoids repeating the notice structure across pages. Layouts similarly centralize page-level structure such as navigation and content areas.
I keep templates focused on presentation. A component may format supplied data, but confidential queries, permission decisions, and state-changing operations should not be concealed inside its markup.
21. How does Blade escape output, and what does that prevent?
Blade's normal {{ ... }} output escapes ordinary string values for HTML, which helps prevent user-controlled text from becoming executable markup. Unescaped {!! ... !!} output deliberately bypasses that protection and should not be used for arbitrary user input.
Code example (language tag: blade)
If $candidateName contains <script>alert(1)</script>, the browser displays that string as text instead of running it as a script in this HTML text position. This is the behavior I want for candidate names, comments, and other plain-text fields.
Escaping is context-sensitive, not a universal security switch. I do not concatenate untrusted input into JavaScript, CSS, or arbitrary URL schemes and assume HTML escaping solves every problem. Rich text requires an appropriate sanitization policy before intentional HTML rendering. Validation and CSRF protection address different risks and do not replace output handling.
22. How do you protect HTML forms from CSRF in Laravel 13?
For a session-based HTML form, I include @csrf and keep Laravel's request-forgery middleware enabled. A forged cross-site request can carry the victim's cookies, so being logged in does not prove the user intended the operation.
Code example (language tag: blade)
This assumes a named PATCH route under the session-enabled web middleware stack. @csrf emits the token field; @method lets an HTML POST form express PATCH.
Laravel 13 uses PreventRequestForgery by default. It first accepts qualifying same-origin requests using browser fetch-metadata verification, then falls back to session-token validation when that check does not pass. I still include tokens for compatibility with that fallback. CSRF protection does not replace authentication, authorization, or XSS defenses, and removing it globally is not an appropriate fix for a failing form.
23. What is Eloquent, and how does a model map to a database table?
Eloquent is Laravel's object-relational mapper. An Eloquent model represents records from a database table and provides a query interface, attribute handling, relationships, and persistence behavior. I can work with domain-shaped PHP objects without manually writing every SQL statement.
For example, an App\Models\Candidate class extending Illuminate\Database\Eloquent\Model conventionally maps to the candidates table, uses id as its primary key, and expects created_at and updated_at timestamps. These conventions can be configured when the schema differs.
A model class does not create its table: a migration or another schema-management process must do that. Eloquent also does not eliminate the need to understand SQL. I still inspect query counts, indexes, constraints, and the amount of data being loaded. For a report involving many joins or aggregates, a query-builder expression or carefully parameterized SQL may be clearer than loading large object graphs.
24. How do you create, read, update, and delete an Eloquent model?
Eloquent supports CRUD through model queries and instance methods. I distinguish constructing an object from saving it: assigning an attribute changes the PHP object, while save() persists it.
In a disposable local example with a Candidate model and a candidates table containing id, name, and timestamps:
PHP example
This creates a row, retrieves it, updates its name, and deletes it. Other required database columns must also be supplied if the real schema has them. For a model without SoftDeletes, the final step removes the row; with that trait, deletion is normally represented by a timestamp instead.
Real HTTP endpoints also need validation and authorization before modification. I would not place this demonstration behind a GET route, because reads should not trigger these state changes.
25. What is mass assignment, and why do fillable attributes matter?
Mass assignment fills several model attributes from an array, as with create, fill, or an instance's update. The risk is accepting fields the caller should not control, such as is_admin or tenant_id, merely because they were included in a request.
Laravel 13 documents a Fillable attribute for declaring an allowlist:
PHP example
After validating those fields, Candidate::create($validated) can populate them, assuming the table exists and its constraints are satisfied. I do not pass $request->all() simply because an allowlist exists.
Mass-assignment protection is not an authorization system, and it does not prevent deliberate direct assignment such as $candidate->tenant_id = $trustedTenantId. I obtain such privileged values from trusted server-side context. Allowlisting, validation, policies, and database constraints each protect a different boundary.
26. What are Eloquent attribute casts?
Attribute casts tell Eloquent how to represent stored values when I access them as model attributes. They reduce repetitive conversion code and make the PHP representation clearer, such as a boolean flag, an array stored as JSON, or a date object.
Inside a model, I can define:
PHP example
Assuming matching columns, $candidate->is_active is represented as a boolean, preferences as an array, and a non-null reviewed_at as an immutable date-time value. Casting JSON to an array also supports serializing an assigned array back for storage.
A cast is not input validation or a schema migration. It does not create these columns, prove an array has the required keys, or authorize changing the flag. I validate external input and retain appropriate database types and constraints alongside casts.
27. What is a Laravel collection, and how is it different from a query builder?
A collection is an object for working with a set of values in PHP; a query builder describes work that will run against the database. This distinction matters because filtering after get() usually means the rows have already been retrieved.
PHP example
With the corresponding table and columns, where, orderBy, and pluck select ordered active candidate names through a database query. The subsequent map transforms those names in PHP without adding another query.
Eloquent's get() returns an Eloquent collection of models, while general collection operations also work with strings, arrays, or other values. I push filtering and aggregation into SQL when appropriate instead of loading an entire table and treating collections as a substitute for efficient querying.
28. How do you define a basic one-to-many Eloquent relationship?
A one-to-many relationship means one parent can have several children, such as an article with many comments. Eloquent relationship methods describe how their keys connect and return relationship objects that can also be queried.
Assume both models live in App\Models, and comments.article_id references articles.id:
PHP example
$article->comments accesses the related collection, loading it if necessary. $article->comments() returns the relationship query, so I can add conditions before retrieving results. The inverse $comment->article accesses its parent.
These methods do not create foreign-key constraints; the migration must define those. I also consider eager loading when displaying many articles and their comments, because repeatedly lazy loading inside a loop can produce unnecessary queries.
29. What are migrations, and how do up and down work?
Migrations are version-controlled database schema changes. up applies a change and down defines its reversal. They let a team evolve a schema through reviewed code rather than relying on undocumented manual edits.
In a generated migration extending Migration, with Schema and Blueprint imported:
PHP example
Running php artisan migrate applies pending migrations to the configured database. This example creates a tags table and enforces unique names at the database level.
A reversible schema operation is not necessarily a reversible data operation: dropping this table destroys its contents. I review the target environment and backup/recovery plan before destructive changes. Commands such as migrate:fresh drop tables and are suitable only when rebuilding the selected database is intentional.
30. How do seeders and model factories differ?
A factory describes how to generate model data, while a seeder coordinates inserting data into a database. I use factories for varied test or local-development records and seeders for a deliberate setup sequence, which may call factories or insert reference data.
PHP example
Assume Candidate uses HasFactory, a discoverable CandidateFactory supplies all required fields, and the configured development or test database has the table. make() constructs an unsaved model; create() persists five records and returns them as a collection.
A seeder's run method might invoke the second expression to prepare a demo. I do not mix fake personal records into production reference-data seeding. If a seeder is intended to be rerun, I design its inserts to avoid accidental duplicates instead of assuming every seeder is automatically idempotent.
31. How do you paginate a Laravel list?
I paginate at the query level so the database returns only the requested page instead of loading the entire result set. Laravel's paginator carries the current items and navigation information needed by an HTML view or API response.
Inside an action serving a public article list:
PHP example
The table must have the referenced fields. The view iterates over $articles and renders navigation with {{ $articles->links() }}. This retrieves up to 15 matching records for the requested page and includes total-count pagination metadata.
simplePaginate is useful when only previous/next navigation is needed because it avoids the total-count query. Ordering makes the list predictable, although concurrent changes can still affect offset-based pages. I also bound any client-selected page size rather than allowing an arbitrarily large response.
32. What is the difference between authentication and authorization?
Authentication establishes who the requester is; authorization decides whether that requester may perform a particular action. A successful login does not grant access to every record. I treat these as separate checks.
Laravel's auth middleware can require a signed-in user. Gates and policies express permissions, with policies commonly grouping abilities around a model. Inside an authenticated action with an already resolved candidate:
PHP example
Assuming the application's candidate policy is configured, this allows execution to continue only when the view ability is permitted; denial normally produces HTTP 403. The controller still chooses which fields to return.
I enforce permissions on the server even when the interface hides a button. A user can send a request without using that interface. Similarly, route model binding proves a record exists, not that the current user owns it or may access it.
33. How should passwords be stored and checked in Laravel?
Passwords should be stored as one-way password hashes, not plaintext or reversibly encrypted strings. Laravel's Hash facade supports password-hashing algorithms and verification without manually managing salts.
PHP example
This illustrative example produces a hash and then a successful verification; the literal password is not a recommendation for real credentials. Real registration code accepts validated user input, persists only the hash, and never logs the plaintext. A normal login flow can rely on Laravel's authentication facilities to perform verification.
I do not hash the submitted password and compare two freshly generated hash strings, because salting means equivalent passwords need not produce identical hashes. Hash::needsRehash() helps determine when an existing hash should be upgraded after successful verification. Login throttling and transport security remain necessary alongside correct storage.
34. How do sessions and flash data work?
A session stores state across requests associated with a client. Laravel supports several session drivers, so session data is not necessarily stored directly in the browser; commonly, the browser carries a session identifier while the data lives on the server.
Inside a session-enabled controller action:
PHP example
Assume $request is the injected request and the named route exists. The preference remains available until changed, removed, or expired, while the flash message is intended for the next request. The target view can read session('status') and display it with escaped Blade output.
I use flash data for confirmation and validation feedback rather than permanent records. Authentication flows also need session lifecycle hygiene, including regenerating the session after login and invalidating it on logout. A session is not an alternative to checking authorization on each protected operation.
35. How should a beginner log useful information without exposing secrets?
I log events that explain application behavior and failures, with enough structured context to investigate them but without exposing secrets or unnecessary personal data. Laravel's logging channels control destinations and severity handling.
PHP example
This assumes the values come from a completed import and the configured log channel is available. The stable message describes the event, and the context provides identifiers and counts that can be searched without embedding candidate records in the message.
I avoid logging passwords, access tokens, session cookies, or entire request bodies by default. Severity should reflect the event: expected validation failures are not necessarily critical incidents. Log access, retention, and redaction matter too. Logging an exception is useful only if the application also returns an appropriate response and someone can act on the signal.
36. What is the difference between a unit test and a feature test?
A unit test checks a small piece of logic in isolation; a feature test checks behavior across a larger application boundary, often an HTTP endpoint. Laravel's default unit tests do not boot the full application, while feature tests can exercise routing, middleware, controllers, and responses.
With the public GET /status-label route returning Ready, a PHPUnit feature test at tests/Feature/StatusLabelTest.php could be:
PHP example
Run it with php artisan test --filter=StatusLabelTest in a configured test environment. It checks the response status and visible text without launching a real browser. A pure formatting or calculation class is usually a better candidate for an isolated unit test. I choose the smallest boundary that verifies the actual requirement, and use separate test infrastructure whenever a test writes data.
37. What do APP_KEY and encryption do, and why is key:generate not a routine production fix?
APP_KEY supplies Laravel's application encryption key. Laravel uses encryption for capabilities such as encrypted cookies and values explicitly protected through its encryption service. Password hashing is a different mechanism and does not use APP_KEY to make passwords decryptable.
PHP example
With a valid application key, the second statement recovers the original string and verifies the encrypted payload's integrity. This demonstrates encryption mechanics; it is not a reason to log either value or embed real secrets in source code.
php artisan key:generate is appropriate when initializing an application, not as a routine response to an unrelated production error. Replacing a key can invalidate cookies and prevent decryption of existing data unless a planned previous-key configuration and migration strategy preserves access. I manage key rotation deliberately and keep keys out of public repositories.
38. How do Laravel views receive data from a controller?
A controller passes view data as an array of named values. Blade receives those names as variables, making the template's inputs explicit and avoiding hidden data retrieval in the presentation layer.
PHP example
Create resources/views/reports/summary.blade.php with:
Code example (language tag: blade)
The dot-separated view name resolves to the nested template path, and the response renders the supplied heading and count. This example uses a fixed count to illustrate data passing; a real controller would obtain an authorized aggregate from the application's data layer.
I pass only the data the page needs and keep the keys clear. Shared view composers can be useful for repeated cross-page data, but I do not introduce them merely to obscure where a simple page gets its information.
39. How do you generate URLs without hard-coding paths?
I generate application URLs from named routes when possible instead of embedding paths throughout templates and controllers. A route name describes the destination, while its URI can change independently.
Assume a route named articles.show has the parameter {article} and $article is an appropriate public or authorized model:
Code example (language tag: blade)
Laravel uses the model's route key to fill the route parameter and generates the URL. If the route moves from /articles/{article} to another path while keeping the same name, this template does not need the path updated manually.
I still validate externally supplied redirect destinations and do not treat URL generation as an access-control check. For links with integrity or expiry requirements, signed URLs are a separate feature. Environment and proxy configuration also matter when generating the correct scheme and host, especially outside a normal browser request.
40. How are application errors handled, and why should debug mode be disabled in production?
Laravel's exception handling separates reporting an error from rendering a response. Reporting records information useful for diagnosis; rendering determines what the client receives. In a fresh Laravel 13 skeleton, application-level customization is configured through withExceptions in bootstrap/app.php.
I let expected failures use meaningful responses, such as 404 for a missing resource and 422 for invalid input, rather than catching every exception and returning success. Unexpected failures should be reported with appropriate context while the client receives a safe error message.
Production must use APP_DEBUG=false because detailed diagnostic pages can reveal configuration, paths, and other sensitive information. That setting does not repair the underlying bug or eliminate the need for logging and monitoring. I reproduce the failure in a controlled environment, inspect the recorded exception, fix the cause, and add a regression test rather than suppressing the symptom with a broad empty catch block.
Laravel Interview Questions and Answers for Intermediate
These answers explain practical implementation details, the setup behind examples, and the limitations that matter when requirements become more demanding.
41. How do you choose between bind, singleton, and scoped container bindings?
I choose a binding by the lifetime of the object, not simply by whether constructing it is expensive. bind normally creates a new instance for each resolution, singleton reuses one instance within the application container, and scoped reuses one instance within a request or queue-job lifecycle before Laravel clears that scope.
PHP example
The formatter can hold isolated working state, the country catalog can share immutable reference data, and the audit context can be shared by collaborators handling the same operation. A singleton is not a distributed singleton across servers or processes. I avoid putting the authenticated user, mutable request data, or a scoped dependency inside a long-lived singleton. Choosing scoped also does not make an arbitrary static property safe: the state must actually belong to the scoped object. Tests should verify both expected reuse and lifecycle isolation.
42. When would you use contextual binding instead of a single interface binding?
I use contextual binding when consumers share an interface but legitimately require different implementations. A global interface binding supplies the default; a contextual binding expresses a particular consumer's requirement without placing environment switches or container lookups inside business logic.
PHP example
When the container constructs EmailCheckout, its ReceiptSender constructor argument receives the email adapter. Constructing SmsCheckout supplies the SMS adapter instead. The classes remain independently testable by passing a fake sender directly. This is useful when the distinction is a stable dependency of the consumer. If the choice changes for each order according to a customer's preference, I would normally inject an explicit routing service or factory and let it choose at runtime. Contextual binding is dependency configuration, not a replacement for dynamic business decisions.
43. How do middleware order and configuration affect a request?
Middleware order determines which prerequisites exist when a check runs and which code executes while the response returns. Code before $next($request) runs on the inbound path; code after it runs while unwinding the pipeline. For example, session-backed authentication needs the session infrastructure, and a policy check referencing a route model needs binding to have supplied that model.
In a modern Laravel application, I configure middleware aliases, groups, and global additions through withMiddleware in bootstrap/app.php, rather than assuming an application HTTP kernel file exists. I use the existing priority list and targeted priority adjustments when route declarations cannot guarantee the required relative order.
I diagnose surprising behavior with the actual route middleware list and a feature test, not just the order of aliases in source code. I also preserve the standard web protections, including Laravel 13's PreventRequestForgery; moving or removing security middleware to make an endpoint work usually treats the symptom rather than the integration problem.
44. How do scoped route model bindings protect nested resource routes?
Scoped bindings require a nested child model to belong to its parent rather than merely exist somewhere in the database. This prevents a URL such as /projects/10/tasks/99 from silently loading task 99 from another project. It is relationship validation, not proof that the authenticated user may access the project.
PHP example
This assumes Project defines a tasks() relationship and TaskPolicy::view checks the user's access. A child outside the selected parent is not resolved and produces a not-found response. An existing, correctly nested task can still be forbidden by the policy. I test both cases separately. Custom route keys such as slugs also need appropriate uniqueness rules within their parent scope.
45. How do you validate nested input without accepting unexpected keys?
I validate both the shape of nested data and the rules for each element. Wildcards validate repeated values, while an array key allowlist rejects unexpected properties that could otherwise travel with the validated array. I also limit array size so a valid request cannot request unbounded work.
PHP example
A line containing an extra unit_price key fails validation, and quantities outside the range fail at their specific indexed paths. I calculate prices on the server rather than trusting client totals. The exists check establishes existence, not permission or availability: a tenant-aware application must constrain product access separately and recheck inventory during the write. Finally, I build persistence data from $data, explicitly selecting model attributes, rather than passing the entire original request to create or update.
46. How do you validate uniqueness safely when updating a record?
I ignore the trusted model being updated, authorize that update, and retain a database unique constraint. Validation gives a useful error before persistence, but it cannot prevent two simultaneous requests from passing the same uniqueness check.
PHP example
This allows a user to keep their current address while rejecting an address already held by another row. I never take the ignored identifier from an arbitrary request field. If uniqueness is tenant-specific, both the validation query and the database composite unique index must use the same tenant boundary. I normalize values consistently with the application's email policy and database collation, and translate a genuine unique-constraint race into an appropriate conflict or validation response rather than exposing raw SQL errors.
47. How should API resources control fields and relationship loading?
I use an API resource as an explicit response contract: it selects public fields and transforms them independently of the database schema. The query layer decides which relationships to load; the resource conditionally includes those already loaded, avoiding accidental queries during serialization.
PHP example
With Post::with('author')->withCount('comments')->findOrFail($id), the response contains the author and count without hydrating every comment. Without those loads, the conditional fields are omitted rather than triggering extra database work. UserResource must itself expose only appropriate user data. I authorize access before constructing the resource and avoid returning sensitive fields merely because they exist on the model. Model $hidden settings are useful defense in depth, but an explicitly named resource field still requires careful review. Pagination should preserve its links and metadata instead of flattening the result into an undocumented array.
48. How do you diagnose and fix an Eloquent N+1 query problem?
An N+1 problem occurs when a collection query is followed by a separate related query for each record. I identify the repeated SQL and its call site, then load only the relationships and columns the response actually needs.
PHP example
For this ordinary belongsTo relationship, the collection and its authors normally need two queries rather than up to twenty-one. The foreign key on the posts and primary key on authors are essential for matching the results. I can enable Model::preventLazyLoading during development to reveal unexpected relationship access. Eager loading is not automatically faster if it retrieves thousands of unused children; counts, existence checks, constraints, or pagination may be better. I also inspect inverse relationship access inside child loops because loading children does not universally guarantee every parent reference is already hydrated.
49. When should you use withCount, whereHas, and withWhereHas?
I use withCount when I need a related aggregate, whereHas when related records should determine which parents qualify, and withWhereHas when I need both a relationship condition and eager loading with that same condition. These operations answer different questions and are not interchangeable.
PHP example
Assuming Post::comments() is defined, this retrieves only posts with approved comments and loads their approved comments. comments_count still counts all comments because that aggregate was not constrained. If the product asks for an approved-comment count, I add the same condition to the count or use a clearly named aliased count. A plain whereHas filters posts but does not preload comments. A plain constrained with loads matching children but does not remove parents with no matching children. Recognizing that distinction prevents both incorrect results and hidden N+1 queries.
50. How do local scopes differ from global scopes?
A local scope is an explicitly requested, reusable query condition; a global scope modifies every normal Eloquent query for a model unless removed. I use local scopes for business filters such as published articles and global scopes sparingly for consistent model behavior such as soft deletion.
PHP example
Calling Post::published()->latest()->get() applies the filter without duplicating it across controllers. The attribute form is supported by Laravel 13, and the attributed method is protected. I group orWhere clauses carefully so an alternative condition does not accidentally escape another required filter. Global scopes can be deliberately bypassed and do not apply to raw table queries, so I do not treat them as the only authorization mechanism. A tenant scope additionally needs an explicit tenant source for HTTP requests, commands, and queued work; an implicit dependency on whichever user happens to be authenticated is fragile.
51. When should you use an accessor, a mutator, or a cast?
I use an accessor to transform a value when reading it, a mutator to transform a value when assigning it, and a cast for a consistent type conversion such as a boolean, date, enum, or JSON array. Standard casts reduce repetitive conversion code; custom accessors are useful when the transformation has application meaning.
PHP example
After hydration, $product->options is an array rather than a JSON string, and the timestamp can be handled as an immutable date object. Assigning an array to options prepares it for storage when the model is saved. These conversions do not replace validation, database constraints, or field-level authorization. I also avoid accessors that secretly execute relationship queries during serialization. Query-builder updates do not instantiate models, so I do not assume that they run custom model mutators or produce model lifecycle events.
52. How do polymorphic relationships work, and why use a morph map?
A polymorphic relationship lets one related table reference more than one model type using a type discriminator and an identifier. For example, a comment can belong to a post or a video through commentable_type and commentable_id; the inverse relationship uses morphTo.
PHP example
A morph map keeps stored values stable when PHP namespaces change and makes supported types explicit. If rows already contain full class names, I migrate those existing values before enforcing aliases. A polymorphic identifier cannot normally have one conventional foreign key referencing several parent tables, so deletion and integrity rules need deliberate design. I never accept a raw class name from a request to decide which model to load. Eager loading a morphTo relationship can issue separate queries for each encountered type; it is not necessarily one related query for the entire result.
53. How do you update a many-to-many relationship and its pivot data?
I choose the pivot operation according to whether the request adds membership, changes metadata, or replaces the entire selected set. attach adds a relationship, detach removes it, sync makes the stored set match the supplied IDs, and syncWithoutDetaching preserves memberships omitted from the request.
PHP example
This assumes a roles() many-to-many relationship and an expires_at pivot column. The first call adds or updates the selected membership without removing other roles; the second changes its metadata. Reading that extra field through $role->pivot requires the relationship to include it with withPivot. I validate and authorize every assignable role, not merely the user being edited. A unique index on the two foreign-key columns prevents duplicate pairs. When several membership changes form one business operation, I use a transaction and define whether concurrent edits should merge or replace state.
54. When should you choose the query builder instead of Eloquent?
I prefer Eloquent when model relationships, casts, lifecycle behavior, and domain-oriented code improve clarity. I prefer the query builder for reporting, large set-based changes, or queries where creating model objects adds little value. Both can use bound parameters, but neither makes arbitrary raw SQL or user-selected identifiers automatically safe.
PHP example
The result contains ordinary objects with customer IDs and aggregate totals, not Order models. Consequently, model accessors, relationship helpers, and global scopes are not applied. If the data is tenant-scoped, I must include the tenant condition explicitly. The raw expression here is fixed application SQL; variable values should use bindings, and selectable sort columns should come from an allowlist. I also remember that a bulk Eloquent update can skip per-model events, so choosing Eloquent syntax alone does not guarantee observers will execute.
55. How do indexes and query plans guide Laravel query optimization?
I optimize the actual SQL and data-access pattern, not the appearance of the Eloquent chain. I capture the query and timing, inspect its execution plan on the target database, and check whether filters, joins, and ordering can use suitable indexes. A slow query may be caused by too many rows or round trips rather than a missing index alone.
PHP example
For a query filtering by customer and sorting by creation time with an ID tie-breaker, this composite index is a candidate worth measuring. Its usefulness depends on the database, cardinality, selected columns, and exact sort conditions; it is not a universal guarantee. I compare the plan and latency before and after the change using representative data. Indexes consume storage and make writes more expensive, so adding one for every column is counterproductive. I also check eager loading, result limits, unnecessary count queries, and functions around indexed columns before concluding that indexing alone is the solution.
56. How do chunkById, lazyById, and cursor differ for large datasets?
I use chunkById for batch callbacks, lazyById for a streaming-looking interface backed by ID-based batches, and cursor when iterating one query without eager-loaded relationships. ID-based batching is particularly helpful when updating a column used in the filter, because offset-based chunks can skip rows as the matching set shrinks.
PHP example
The traversal advances using the last ID rather than a changing row offset. I do not modify that traversal key, and I group my own orWhere conditions because batching adds its own conditions. A cursor reduces hydrated model memory but PDO may still buffer raw results, so it is not a promise of constant total memory. The export example also needs a resumable or deduplicated writer if reruns must not produce duplicate output after a partial failure.
57. How do you choose between offset and cursor pagination?
I use offset pagination when users need numbered pages or a total result count, and cursor pagination for efficient next/previous traversal through large ordered datasets. paginate runs a count query; simplePaginate avoids that count while still using offsets. Cursor pagination seeks past the last ordered values instead of scanning an ever-growing offset.
PHP example
Here the ID breaks timestamp ties, producing a unique ordering; both columns must be selected and non-null for this design. I add an appropriate index and keep filters consistent between requests. The authenticated customer's scope remains in every query—an encoded cursor is not an authorization token. Cursor pagination supports next and previous links, not arbitrary numbered page jumps. It reduces common offset anomalies during inserts and deletes but does not create a historical snapshot: changing a row's ordering values while browsing can still affect what the user sees.
58. How do foreign keys and unique indexes complement validation?
Validation improves request feedback, while database constraints enforce invariants regardless of which application path writes the data. An exists validation rule can become outdated before insertion, and a uniqueness check can pass simultaneously in two requests. A foreign key or unique index remains the final database-level guard.
PHP example
This assumes the teams and users tables already exist. It prevents duplicate membership pairs and references to missing parents. Cascading deletion is an explicit business choice; financial or audit data may require restricting deletion instead. These constraints do not prove that the current user may join a team, so policies still enforce permission. When adding constraints to an existing table, I inspect and repair duplicate or orphaned data first. I handle expected constraint violations deliberately and avoid swallowing unrelated database exceptions as if they were ordinary validation failures.
59. How should a Laravel database transaction handle exceptions and retries?
I use DB::transaction to keep related writes on the same database connection atomic. Successful completion commits; an exception escaping the callback rolls the transaction back and is rethrown. Catching an exception inside the callback and returning normally can unintentionally commit earlier writes.
PHP example
This assumes the relationships exist and the audit model allows its fixed message field. The second argument permits up to three transaction attempts for supported concurrency failures such as deadlocks, not an unconditional retry of every exception. I keep remote calls and irreversible side effects outside the callback because the entire callback can run again. A transaction on one connection does not roll back another database, a file upload, or an external payment. I authorize and validate inputs before entering, then recheck state-sensitive conditions within the transaction when necessary.
60. When is pessimistic locking appropriate in a Laravel transaction?
I use pessimistic locking when a decision and its subsequent write must be protected against another transaction changing the same row. lockForUpdate belongs inside a transaction and requires a database that implements the expected row-locking behavior.
PHP example
This assumes the item was authorized and quantity validated as a positive integer. Competing updates must wait or fail according to the database's lock rules; a later locking reader checks the current available quantity rather than a previously loaded model. I keep the transaction short and avoid HTTP calls while holding locks. When several rows are involved, consistent lock ordering reduces deadlock risk. A conditional atomic update can be simpler for one counter. SQLite's locking behavior is different from production MySQL or PostgreSQL, so a passing SQLite test does not prove equivalent contention behavior.
61. What is the difference between an authentication guard and a user provider?
A guard determines how a request is authenticated; a user provider determines how the corresponding user record is retrieved and credentials are checked against storage. For example, a session guard can use an Eloquent provider backed by the User model. The same provider can be used by more than one guard.
PHP example
The name admin identifies a configured authentication mechanism; it does not automatically grant an administrator role. The route should use the matching guard, such as auth:admin, and authorization must separately enforce what that identity can do. I keep this distinction clear when debugging a user who is logged in on one part of an application but appears unauthenticated elsewhere. I inspect the selected guard, its provider, and session or token transport before changing policies. For ordinary role-based access, separate guards are often unnecessary: one guard plus policies or permissions is simpler than maintaining parallel login systems.
62. How do you enforce record-level authorization using policies?
I put resource-specific permission rules in a policy and invoke them on the server before reading sensitive data or changing state. Authentication supplies the user; the policy decides whether that user may perform the action on the particular record.
PHP example
With the policy discovered by convention or explicitly registered, a non-owner is denied before the update. This assumes compatible ID casts; real systems may also permit selected team members. I use the Gate facade explicitly rather than assuming a custom base controller includes authorization helper traits. Hiding an edit button with Blade's authorization directives improves the interface but is not enforcement, since clients can call the endpoint directly. I test the owner, another authenticated user, and an unauthenticated request. Policies should express the business boundary consistently across controllers, commands, and other entry points.
63. How does Sanctum authentication differ for first-party SPAs and API tokens?
For a first-party SPA, I use Sanctum's stateful session-cookie authentication rather than issuing a personal access token to store in the browser. The SPA and API must meet Sanctum's same top-level-domain requirements, with stateful domains, credentialed requests, cookie settings, and request-forgery protection configured consistently. The client initializes CSRF protection before the login request and sends the expected XSRF header.
For mobile clients or appropriate external integrations, Sanctum can authenticate a bearer API token. Tokens may have abilities and should have a deliberate expiry and revocation strategy; they are not an OAuth authorization-server implementation. Routes commonly use auth:sanctum, which supports the relevant session or token flow.
I still apply policies to the authenticated user and resource. A token ability narrows what a token may request; it does not establish ownership. Sanctum's tokenCan behavior for first-party authenticated requests is intentionally permissive, so an ability check alone cannot replace record-level authorization. I also avoid treating a CORS configuration error as a reason to disable CSRF protection.
64. How do you design a user-aware API rate limiter?
I key an authenticated API limit by a trusted user or account identifier and use an IP-based fallback only when no identity exists. This prevents every user from sharing one global allowance while recognizing that many anonymous clients can share an IP address.
PHP example
I attach throttle:reports to the intended routes and ensure authentication has established the intended identity before a user-specific limit is evaluated. In a multi-server deployment, the limiter must use a shared appropriate cache store. The limit should reflect endpoint cost; a report export may deserve a smaller allowance than a lightweight read. I test the normal response and the exhausted-limit response, including retry guidance. Rate limiting is not authorization and does not replace upload limits, query bounds, or controls against expensive individual requests.
65. What data should a queued job contain, and what does model serialization imply?
A queued job should contain the smallest stable data needed to identify the work, while runtime services are injected into handle. I normally pass identifiers, immutable input values, or Eloquent models only when their serialization semantics are appropriate. I do not serialize the request, an open database connection, or a service client.
When a job uses Laravel's model-serialization support, an Eloquent model is generally represented by its identifier and re-retrieved when the job runs. That means the worker observes current persisted state, not a guaranteed snapshot of the attributes at dispatch time. The row might have changed or been deleted.
For example, an invoice-delivery job can carry an invoice ID and then verify that the invoice is still deliverable. If the exact rendered document version matters, I pass a versioned document reference rather than expecting the original model values to survive. I avoid carrying unnecessary loaded relationships and explicitly define missing-model handling, tenant context, and authorization-sensitive assumptions. The job's dependencies belong in its handler signature, where the container can resolve and tests can replace them.
66. How do you configure job attempts, backoff, and timeouts?
I configure attempts, retry delay, and execution limits separately. Attempts bound how many executions are allowed, backoff spaces retries after failures, and a timeout bounds one execution. A transient network failure may justify a retry; invalid business input usually should fail without repeatedly consuming worker capacity.
PHP example
For these settings, I configure the connection's retry_after comfortably above 45 seconds, or the equivalent visibility timeout for SQS, and set outbound client timeouts below the job limit. A timeout requires the appropriate worker environment, including PCNTL support for Laravel's worker timeout handling. Middleware releases can consume attempts too, so a low attempt limit may fail a job that has not meaningfully executed. I record terminal failures and make the handler safe to run again. These settings reduce accidental overlap but do not provide exactly-once execution or make a repeated external charge safe.
67. Why dispatch a job after a database transaction commits?
I dispatch after commit when a job depends on database changes made by the current transaction. Otherwise a fast worker can run before the new row is visible, or perform work for a transaction that eventually rolls back.
PHP example
This assumes the job accepts an invoice ID and the invoice update has already been authorized. Laravel delays the dispatch until the relevant open transactions commit; if the transaction rolls back, the deferred job is discarded. A queue connection can also enable after_commit as a broader default. I test the rollback path as well as successful dispatch and avoid assuming the synchronous test driver reproduces every real worker timing issue. After-commit dispatch solves visibility and rollback ordering, not atomic delivery to a separate broker: the database can commit and a subsequent enqueue can still fail. Critical delivery guarantees require an additional durable design.
68. How do events and queued listeners separate application responsibilities?
An event states that something happened; its listeners perform independent reactions. I might dispatch an OrderPlaced event after recording an order, then let separate listeners update analytics and send a confirmation. This keeps the order workflow from knowing every secondary consumer.
A normal listener executes synchronously, so it can affect request latency and propagate failures. A listener implementing ShouldQueue moves its work to the queue and requires a configured connection and running worker. If it reads data changed within a transaction, I use after-commit behavior, such as ShouldQueueAfterCommit, rather than allowing it to race the commit.
I keep mandatory business decisions explicit: if stock allocation must succeed before an order is accepted, it should not be hidden in an optional asynchronous listener. I also avoid depending on independent queued listeners completing in a particular order. Each listener needs its own failure handling and repeat-safe behavior. Tests should distinguish whether the event was dispatched from whether the listener actually performed its intended effect; an event fake proves the former, not the latter.
69. How should a scheduled Laravel task be configured and operated?
I define the schedule in application code and ensure an external scheduler actually invokes Laravel. In the modern skeleton, routes/console.php is a standard place for schedule definitions; adding a definition alone does not start a background service.
PHP example
This assumes the command exists and an appropriate shared cache store supports the locks used across servers. A production scheduler commonly runs php artisan schedule:run every minute; I inspect schedule:list to verify the expected next run. withoutOverlapping prevents concurrent scheduled executions, while onOneServer coordinates one scheduler instance for a due run. Neither proves the business work completed exactly once. If the command only dispatches a queued job, its scheduler lock does not automatically cover that job's entire execution. I define timezone expectations, monitor failures and missed runs, and make rerunning the underlying command safe. Daylight-saving changes deserve attention for timezone-based wall-clock schedules.
70. How do you cache a query result and invalidate it correctly?
I cache data that can tolerate a defined freshness window and invalidate it when its source changes. The key must include every dimension that changes the result, such as tenant, locale, or visibility; otherwise a cache can return incorrect or unauthorized data even if the original query is correct.
PHP example
This example deliberately caches the same public list for all visitors for up to five minutes. After a successful category change, the write path can call Cache::forget('public-categories:v1'), preferably after its database transaction commits. A TTL provides a fallback freshness bound when an invalidation is missed. I avoid caching permission decisions or sensitive model fields without a clear access-aware design. remember does not itself guarantee that only one request computes a missing value; expensive recomputation may need a lock or another stampede-control strategy. I measure hit rate and query reduction before expanding the cache.
71. What is the difference between cache data, a cache lock, and the session?
Cached data is a reusable value, a cache lock coordinates temporary ownership of work, and a session stores state associated with a particular client session. They may share infrastructure such as Redis, but their purposes and lifecycle expectations differ.
PHP example
This assumes an application CatalogRefresher service and a configured lock-capable cache store. The callback runs only if the lock is obtained, and the callback form releases it afterward. If acquisition fails, get returns false rather than running the refresh. The lock's expiration is a safety bound, not proof the task finished; work exceeding 30 seconds can overlap a later owner. In a multi-process system, all contenders must use the same shared lock backend and key. I do not use the session for cross-worker coordination, and I do not store authoritative financial state only in an evictable cache. For browser workflows, session state should remain minimal and server-side business records should remain durable.
72. How do you store and deliver private files through Laravel disks?
I store private files on a non-public disk or private object-storage location, keep a trusted storage path in the database, and authorize each download. A disk abstracts the storage backend; it does not automatically decide which application users may access a document.
PHP example
This assumes local is configured to a private root and the path was created by trusted application code, not accepted as an unrestricted request path. For suitable drivers I can instead issue a short-lived temporary URL after authorization, recognizing that anyone holding that URL may access it until it expires. The public disk and its storage symlink are intended for intentionally public assets, not confidential uploads. I also distinguish visibility from encryption, define retention and deletion behavior, and handle missing files without leaking internal paths. A database transaction does not automatically roll back filesystem writes, so failed workflows need cleanup.
73. How do you validate and store a user-uploaded image safely?
I validate an upload's content type, size, and dimensions, generate its storage name on the server, and store it outside executable public paths unless it is intentionally public. The browser's filename and declared MIME type are untrusted inputs.
PHP example
This authenticated upload handler stores a generated-name file on the configured local disk; it must separately associate the resulting path with the authorized user. Laravel's image rule rejects SVG by default, which avoids treating active SVG content as an ordinary safe bitmap. Validation is not a malware scanner or a guarantee that every decoder is safe, so high-risk workflows may quarantine or re-encode accepted content. I align PHP and web-server body limits with the application rule, handle storage failure, and remove orphaned files when later database work fails. I never build a destination path directly from a client-supplied filename.
74. How should Laravel's HTTP client handle failures and timeouts?
I set explicit connection and overall timeouts, distinguish transport failures from HTTP error responses, and deliberately decide whether to throw or translate each failure. Laravel's HTTP client does not automatically throw for every 4xx or 5xx response, so ignoring the response status can silently turn a failed integration into apparent success.
PHP example
This assumes a trusted configured HTTPS catalog base URL. The call raises a request exception for an HTTP error, while connection failures have their own exception path. I validate the returned structure because a successful status is not proof that the payload meets my contract. Retries should be bounded and limited to appropriate transient failures; replaying a non-idempotent POST can duplicate an external action. I also avoid logging credentials or full sensitive payloads, and I test timeouts, malformed responses, rate limits, and upstream server errors rather than testing only a successful JSON body.
75. How do you isolate Laravel test environments and parallel test databases?
I give tests an explicitly isolated environment before allowing any database-reset trait or destructive setup to run. I configure test-only database credentials and service endpoints through phpunit.xml, .env.testing, or controlled CI variables, and clear stale configuration caches so the effective configuration matches those settings. Production credentials should never be available to the test job.
Bash command
This parallel command requires the brianium/paratest development dependency. Parallel tests use separate processes, and Laravel can create token-suffixed test databases for its supported database-testing workflow. That does not automatically isolate every external resource: shared Redis keys, storage paths, search indexes, and third-party sandboxes also need process-specific namespaces or fakes. I use ParallelTesting::token() where a process-specific resource name is needed and register cleanup hooks for disposable resources. I keep tests independent of execution order and fixed global IDs. Finally, I run important SQL behavior against the production database engine as well as any faster local alternative; separate databases prevent interference, but they do not erase engine differences.
76. How do factories and RefreshDatabase support reliable database tests?
I use factories to describe the minimum meaningful test data and RefreshDatabase to prevent one database test from contaminating another. Factory states and relationships make intent visible, while explicitly setting fields that control the assertion avoids relying on random values.
PHP example
This assumes factories, migrations, an authenticated show route, and an ownership policy exist. The test exercises their integration rather than mocking the database. RefreshDatabase prepares the schema when needed and normally uses transactions for isolation; it does not mean every table is recreated before every test. I also assert response content or database changes relevant to the behavior, and add a non-owner case. Real background workers use separate connections and may not see uncommitted test data, so tests involving actual asynchronous execution need a deliberately different isolation strategy.
77. How do you test authentication, authorization, and validation failures?
I test unauthenticated access, authenticated-but-forbidden access, and invalid input as separate behaviors. For a JSON endpoint these commonly produce 401, 403, and 422 respectively, although a policy may deliberately conceal a resource with 404. I also verify that rejected requests did not mutate data.
PHP example
This assumes the route authorizes an owner-only update before saving and the project factory creates a different owner. For validation, I authenticate an authorized owner, submit invalid input, and use assertJsonValidationErrors for the exact fields. Otherwise an authentication failure could mask a broken validation rule. I keep middleware enabled for these feature tests. Laravel's HTTP testing conveniences do not simulate every browser behavior, including the ordinary CSRF flow, so separate integration or browser coverage is appropriate for cookie and request-forgery protection.
78. How do queue and event fakes help test behavior without hiding bugs?
I use queue and event fakes to assert that the application requested the correct side effect without actually performing it. I then test the job or listener separately. A dispatch assertion cannot prove that the handler sends the message, writes the file, or handles a failure correctly.
PHP example
This assumes the endpoint dispatches a job exposing those two properties. Faking only the relevant job keeps unrelated behavior visible. The corresponding handler test should use controlled dependencies and assert the produced result, including retries or failure paths where relevant. Similarly, a broad Event::fake prevents listeners from running and can disable model events required by factories. I create necessary fixtures first or fake only selected events. For chains and batches, I use the bus-specific assertions rather than assuming a queue-push assertion verifies the orchestration.
79. How do you test file uploads and outbound HTTP requests?
I fake storage and the HTTP client at their integration boundaries, then assert the exact observable behavior. This keeps tests deterministic and prevents ordinary tests from uploading to real buckets or calling live providers.
PHP example
This assumes an endpoint that stores the upload on local and returns its generated relative path in a JSON response. Fake image creation also needs the relevant image-generation support in the test environment. I add oversized and invalid-type cases and assert no unwanted file remains after failure. For outbound calls, I use Http::fake with a narrow URL pattern, enable Http::preventStrayRequests, and verify method, payload, and headers with assertSent. These fakes cover Laravel's clients; they do not intercept arbitrary SDK transports. A small separately controlled integration suite can verify real storage permissions and upstream contracts.
80. When should application logic use a service class, an observer, or an event listener?
I put a multi-step use case in an explicit application service, use an observer for behavior tied to a model lifecycle event, and use an event listener for an independent reaction to a meaningful event. The choice is about visibility and guarantees, not a rule that every operation must have all three layers.
For example, PlaceOrder can calculate trusted totals, reserve stock, and persist an order inside its transaction. A model observer may normalize a model-specific value or maintain closely related metadata. An OrderPlaced listener can schedule a confirmation message after the core operation succeeds.
I avoid hiding essential invariants in observers if bulk updates or raw database writes can bypass the relevant model events. I also avoid making an asynchronous listener responsible for a condition that must hold before returning success. Services should have explicit inputs and dependencies so HTTP controllers, commands, and jobs can reuse the same workflow without constructing a fake request. Tests then cover the service's decisions, the observer's lifecycle behavior, and the listener's independent effect at their respective boundaries.
Laravel Interview Questions and Answers for Experienced
These answers emphasize failure modes and verifiable guarantees rather than assuming framework conveniences eliminate distributed-system or security problems.
81. How would you structure a large Laravel application without prematurely creating microservices?
I would usually start with a modular monolith: one deployable application with explicit boundaries around capabilities such as billing, fulfillment, and identity. Each module should own its business rules and expose a small application-level interface, rather than allowing every controller to manipulate its tables.
Laravel's container can connect a contract to an implementation. For example, after defining the application's PaymentGateway contract and StripeGateway adapter, a service provider can register:
PHP example
An application service requesting PaymentGateway then receives that adapter. I would keep HTTP validation, authorization, and response formatting outside the payment rules, and isolate provider-specific data at the boundary.
I would not create a repository interface for every Eloquent model automatically. Abstractions should protect a meaningful seam or invariant. I would extract a service only when independent deployment, ownership, scaling, or fault isolation justifies the extra network failures and operational burden. Architecture tests and code review should enforce these boundaries; directories alone do not.
82. How would you prevent cross-tenant data leakage in a Laravel SaaS application?
I would make tenant isolation an invariant across every entry point, not just an Eloquent scope. The tenant must come from an authenticated, authorized context; a submitted tenant_id is not proof of access. Database queries, cache keys, object paths, exports, broadcasts, and jobs must all preserve that boundary.
For example, after middleware verifies membership and supplies $tenant, an invoice lookup can deliberately constrain the tenant before authorization:
PHP example
An identifier belonging to another tenant is not returned by this lookup. The policy still checks the user's permission within the tenant. A global scope can reduce mistakes, but raw SQL, unscoped queries, and background commands need equivalent controls.
I would use tenant-aware uniqueness constraints and verify related records belong to the same tenant. Jobs carry an explicit tenant identifier, establish validated context, and clear it afterward. My negative tests attempt cross-tenant reads, writes, downloads, and queued operations, including consecutive requests through the same long-running worker.
83. How would you make an inventory reservation safe under concurrent requests?
I would move the reservation condition into an atomic database operation rather than reading stock and later saving a decremented model. Two requests can otherwise observe the same quantity and both sell it.
Assume an inventory table with a unique (tenant_id, sku) key, integer available quantity, and a positive, validated $quantity. This query reserves only when sufficient stock remains:
PHP example
The affected-row count distinguishes success from a rejected reservation. If an order or reservation record must be created too, I would place those writes and the conditional decrement in the same transaction. A unique reservation key prevents a retried request from decrementing twice.
For rules spanning several rows, I would consider row locks acquired in a consistent order or optimistic version checks. I would test simultaneous connections against the production database engine. A database constraint remains a valuable last line of defense; an application-level if is not concurrency control.
84. How would you design an idempotent payment endpoint when the provider can time out?
I would treat a provider timeout as an unknown outcome, not evidence that the payment failed. The provider may have charged the customer before the connection broke, so blindly issuing a new payment can charge twice.
My endpoint accepts a caller-supplied idempotency key scoped to the authenticated tenant and operation. A database uniqueness constraint protects that key, while a request fingerprint rejects reuse with a different amount, currency, or order. I persist the payment attempt and a stable provider idempotency key before making the remote request, without keeping a database transaction open across the network call.
The attempt moves through explicit states such as pending, succeeded, failed, and reconciliation required. Retries reuse the same provider key and parameters; duplicate client requests return the recorded outcome or a clear pending response. Provider-specific retention and retry semantics must be respected rather than assumed.
Signed webhooks and a reconciliation process resolve ambiguous outcomes. I deduplicate webhook event identifiers and verify the payment's tenant, amount, and currency before fulfilling the order. This design aims for one business effect through durable state and idempotency, not a fictional exactly-once network delivery guarantee.
85. What problem does a transactional outbox solve, and how would you implement one?
A transactional outbox closes the failure gap between committing business data and publishing a message. Dispatching after commit avoids a worker seeing uncommitted data, but a process can still die after the commit and before publication.
I would persist the business change and an outbox row on the same database connection in one transaction. Assume orders and outbox_events exist, the latter has a unique UUID id, and the order has already been authorized:
PHP example
Both writes commit or neither does. Production code must also guard the order's state transition and preserve tenant metadata.
A relay claims pending rows, publishes them, and records success. If it crashes after publishing but before marking success, it may publish again. Consumers therefore deduplicate the event ID and apply their local change transactionally. I monitor oldest unpublished age, retries, and retention, and explicitly preserve per-aggregate ordering where required.
86. How would you coordinate a multi-service workflow when a single database transaction is impossible?
I would use a durable workflow with local transactions and explicit recovery steps, rather than pretend one Laravel transaction can atomically control several services. A saga is appropriate when each participant owns its data and temporary intermediate states are acceptable.
For example, an order workflow could reserve stock, authorize payment, and request shipment. I would persist the workflow identifier, current state, step attempts, and resulting external references. Every command needs an idempotency key, and every reply needs correlation and duplicate handling. Timeouts should trigger reconciliation or a safe retry, not an automatic assumption that the previous step did nothing.
If shipment cannot be arranged, compensation might release stock and void the authorization. Compensation is a new business operation, not a database rollback: a dispatched parcel may require a return, and a captured payment may require a refund. Compensation can also fail, so it needs retries, monitoring, and a manual-resolution state.
I would choose an orchestrator when a central view of progress and recovery is valuable. I would keep a workflow in one database transaction instead when the operations genuinely share ownership and need immediate atomicity; distributing them creates complexity without necessarily delivering value.
87. Where do Laravel database transactions stop protecting application consistency?
A Laravel transaction protects participating database operations on its connection; it does not roll back HTTP requests, object storage writes, emails, or work performed through another independent database connection. Its isolation guarantees also come from the database engine and configured isolation level.
For example, if code charges a card inside DB::transaction() and a later insert fails, rolling back SQL does not refund the card. I would record durable intent first and coordinate the external side effect through an idempotent workflow. Likewise, deleting a file should not be assumed reversible because a surrounding SQL transaction exists.
Other traps include catching an exception inside the transaction closure and returning normally, which can allow a commit, and executing database statements that cause implicit commits. Some engines implicitly commit around DDL, so a migration is not automatically an all-or-nothing unit.
I keep transactions short, use one clearly identified connection for atomic writes, and avoid slow external calls while holding locks. For data-dependent rules, I choose suitable constraints, conditional updates, or locking rather than assuming the transaction alone prevents races. Finally, I test rollback paths and ambiguous external outcomes separately.
88. How would you diagnose and reduce deadlocks in a busy Laravel application?
I would investigate the database's deadlock report to identify the statements, indexes, and lock acquisition order involved. A deadlock is not simply a slow query: transactions are waiting on each other in a cycle, so the database aborts a participant to make progress.
The first fixes are usually shorter transactions, consistent lock ordering, and indexes that avoid locking more data than necessary. For a transfer between two existing account rows, all code paths should lock them in the same order. Within an already authorized transfer operation:
PHP example
The final argument permits bounded transaction retries. The omitted transfer rules must also validate ownership, currency, positive amounts, and both rows' existence.
I treat retries as a safety net, not the primary optimization. The closure may execute again, so it must not send an irreversible external request on each attempt. I track deadlock frequency and retry exhaustion, then reproduce the competing paths with separate database connections.
89. How would you perform a zero-downtime database schema change?
I would use an expand–migrate–contract rollout so old and new application versions can run against the same schema. A destructive rename bundled with a rolling deployment is risky because an old worker may still reference the previous column.
For example, replacing an order's legacy status representation starts with an additive migration. Assuming the existing orders table, place the imports at file scope and the schema call inside the migration's up() method:
PHP example
I then deploy compatible code, backfill in resumable batches, handle concurrent writes, verify completeness, and switch reads. Only after old code and queued payloads are retired would I enforce final constraints and remove the legacy field.
Additive does not automatically mean nonblocking. I inspect the database version, table size, lock behavior, replica lag, and DDL implementation. PostgreSQL concurrent index creation, for instance, has different transactional restrictions from ordinary index creation.
The rollout needs progress metrics, lock timeouts, and an abort procedure. I prefer a compatible application rollback or forward repair over a down() migration that might delete newly written production data.
90. What consistency problems arise with read replicas, and how would you address them?
Read replicas can return stale data, so I would classify reads by their consistency requirements instead of routing every SELECT to a replica. A reporting dashboard may tolerate delay; checking a just-created order or an authorization change may not.
Laravel's sticky connection option keeps subsequent reads on the writer after a write during the current request. It does not by itself establish a cross-request guarantee. Laravel 13 also provides session-based PreventStaleDatabaseReads middleware for a configurable post-write window, but a time window is still not proof that every replica has caught up.
For a critical lookup, I can explicitly use the writer:
PHP example
This example assumes configured read/write connections and a validated tenant context. It avoids replica lag for this lookup, not unrelated cache staleness.
I would monitor replication lag, define behavior when replicas fail, and test redirects and background jobs separately. Correctness-critical decisions should use authoritative state or an explicit replication-consistency mechanism, rather than arbitrary sleeps intended to hide the race.
91. How would you investigate a slow endpoint before adding a cache?
I would first break the endpoint's latency into database time, external calls, application CPU, serialization, and queueing at the web tier. A cache can hide a symptom while leaving a bad query, oversized response, or saturated dependency unresolved.
I would compare representative fast and slow requests, including tenant size and request parameters. Useful evidence includes query count, repeated query shapes, rows examined, response size, and external-call duration. Pulse or an application profiler can help locate the expensive segment; sensitive query bindings should not be copied indiscriminately into logs.
For a suspected PostgreSQL query, I might inspect a plan in a safe environment:
SQL example
ANALYZE executes the statement, so I assess its cost before using it on production. I compare estimated and actual rows and look for expensive scans, sorts, and I/O.
Only then would I choose an index, narrower projection, eager loading, pagination, or caching. I validate the change with realistic data and tail-latency measurements, not just one warm local request.
92. How would you make a queued job safe when it is delivered more than once?
I would design the job around an idempotent business operation, because a worker can complete its side effect and crash before acknowledging the message. A retry may therefore represent already-completed work, not a fresh instruction.
For a job that applies loyalty credit, I would use a stable business event ID and a database uniqueness constraint for that credit. The deduplication record and balance change must commit together. A marker written before a separate balance transaction could suppress necessary work after a crash; a marker written afterward could allow duplicate credit.
For a remote side effect, I would pass a stable idempotency key supported by the provider and persist its external reference. If the provider offers no deduplication mechanism, I would define reconciliation and an explicit ambiguous-outcome path rather than claim guaranteed single execution.
Job inputs should identify the operation and tenant, while execution rechecks current state. I test delivery twice, concurrent delivery, and crashes at each persistence boundary. Queue uniqueness and overlap locks can reduce redundant work, but neither replaces a durable business invariant. The intended result is a correct final state despite repeated delivery, not proof that handle() ran only once.
93. How should retry, timeout, and backoff settings work together?
I would set separate budgets for the external request, the job, and the queue's reservation or visibility window. The external call should time out first; the worker timeout should be shorter than the reservation window, with enough margin for termination. Otherwise another worker may receive a job while the first is still running.
For an existing queued job whose legitimate execution fits within 45 seconds, these class members illustrate a bounded policy:
PHP example
With Redis, I might start with retry_after set to 90 seconds and a provider HTTP timeout well below 45 seconds, then validate the numbers against measured behavior. SQS uses its visibility timeout instead of that Laravel connection setting. Worker timeout support also depends on the required process-control extension.
I retry transient network failures and throttling, not invalid credentials or permanently malformed payloads without intervention. Jitter or controlled release delays help prevent synchronized retries. I monitor retry volume and total operation age, because repeated attempts can amplify an outage even when each individual setting looks reasonable.
94. How would you handle poison jobs and safely replay failed work?
I would separate a poison job from a transient failure. A poison job repeatedly fails because of its payload or application logic; simply adding workers or unlimited retries increases cost and can block useful work.
The job should have bounded attempts, actionable error classification, and a failed-work record with a safe correlation identifier. I would capture enough metadata to reproduce the problem without storing access tokens or unrestricted customer documents. Alerts should group a widespread provider outage differently from one malformed import row.
Before replaying, I determine whether an earlier attempt produced a partial or external side effect. Then I fix the root cause, validate the payload's schema version and tenant, and check that the handler is idempotent. I retry a small, identified set before widening the replay, with downstream rate limits and progress monitoring.
A failed() callback is useful for notification or bookkeeping, but it should not be the only durable proof of work state: process termination and infrastructure failures may interrupt cleanup. I would also retain a reconciliation path for operations stuck in processing. Deleting failed records without accounting for the underlying business operation is not a recovery strategy.
95. How do unique jobs differ from overlap prevention and business-level deduplication?
I use unique jobs to suppress duplicate dispatches while their uniqueness lock applies, overlap middleware to limit simultaneous execution, and database or provider invariants to prevent duplicate business effects. These solve different problems and have different failure windows.
For example, two job classes that rebuild the same tenant's search index can share an overlap lock. With the import at file scope and $tenantId defined on each job, add this class method:
PHP example
shared() makes the key coordinate across job classes. This example assumes a shared lock-capable cache and a job runtime comfortably shorter than the lock expiry. Jobs released because of contention also need an appropriate attempt budget.
I choose uniqueness keys around the intended operation, not just the PHP class. Overly broad keys can discard legitimate updates, while overly narrow keys provide little protection. Expired locks, worker crashes, and manual replay still happen, so a completed payment or ledger entry needs durable deduplication. In particular, neither ShouldBeUnique nor WithoutOverlapping establishes exactly-once execution.
96. How would you preserve business ordering while scaling queue workers?
I would define the smallest business scope that requires ordering, such as one order or one account. Global ordering usually destroys useful parallelism, whereas independent aggregates can often be processed concurrently.
A normal multi-worker queue does not guarantee that completion follows dispatch order: one job can be slow or retried while a later job finishes. Chaining helps express a known sequence, but it does not order unrelated producers' messages for the same aggregate.
For a stream of account events, I would include an aggregate identifier and monotonically increasing version. The consumer stores its last applied version and updates it atomically with the business state. A duplicate version is ignored; a gap triggers a bounded wait, replay, or reconciliation policy instead of silently applying changes in the wrong order.
An SQS FIFO message group can serialize delivery for one aggregate while other groups progress. I still need to manage producer ordering, visibility timeouts, poison-message handling, and idempotent effects. Moving an event to a dead-letter queue may allow later events to proceed, so that decision must respect the business invariant. I would monitor hot groups and avoid assigning every tenant to a single unnecessary bottleneck.
97. How would you configure and operate Horizon without overloading downstream systems?
I would use Horizon's visibility into Redis queues to control service levels, not simply maximize worker count. More workers can increase database contention, exhaust connections, or overwhelm a rate-limited provider.
For an existing Horizon installation, this production supervisor entry illustrates a bounded worker pool in config/horizon.php:
PHP example
These numbers are examples, not universal defaults. I would set job-level timeouts below the supervisor timeout and the Redis retry_after above it, with adequate margin. I would confirm the effective defaults merged into the supervisor configuration.
Separate supervisors help isolate urgent transactional work from slow imports. I would monitor queue age, throughput, execution percentiles, failures, and downstream saturation, then tune capacity against those signals. Rate limits still need their own enforcement; automatic balancing is not a provider quota manager.
Finally, I restrict dashboard access, retain enough failure history for diagnosis, and run Horizon under a process manager. Deployment and termination procedures must be rehearsed so the monitoring interface is not mistaken for the recovery mechanism.
98. How would you run scheduled work safely on multiple application servers?
I would distinguish selecting one scheduler instance from preventing consecutive executions of the same task from overlapping. Those are separate failure modes when several application servers run the scheduler.
For an installed application command that performs its work synchronously, a schedule in routes/console.php could be:
PHP example
onOneServer() coordinates election through the shared cache. withoutOverlapping(10) sets an overlap-lock expiry in minutes. Every server must use the same supported central cache, and the expiry must suit the task's real runtime.
If the command merely dispatches a job and exits, its scheduler lock does not cover the job's later execution. The job needs its own overlap or idempotency controls. Crashes, expired locks, and manual runs likewise mean the underlying operation should remain safe to repeat.
I would record last successful completion, duration, and reconciliation coverage rather than alert only when cron stops. I also consider timezone and daylight-saving behavior for business schedules; a UTC schedule and an explicit business-period identifier are often easier to make repeatable.
99. How would you prevent a cache stampede after a popular entry expires?
I would prevent many concurrent misses from rebuilding the same expensive value at once. A TTL alone does not do that: after expiry, hundreds of requests can all query the database before any one finishes repopulating the cache.
With a shared Redis cache and a tenant-scoped key, this example performs a second cache check after acquiring the rebuild lock:
PHP example
The count can legitimately be zero, so the check is specifically for null. The lock lifetime assumes rebuilding finishes comfortably within 15 seconds. The caller must handle lock-acquisition timeout with a bounded fallback, not launch an uncontrolled duplicate rebuild.
Where stale data is acceptable, stale-while-revalidate can reduce waiting; Laravel offers Cache::flexible for that pattern. I also stagger expirations and warm genuinely hot entries. Cold misses, downstream outages, and lock expiry still need explicit policies.
100. How would you design cache invalidation for tenant-specific mutable data?
I would start by defining how stale the data may be and which mutations affect it. Cache invalidation becomes manageable when each key has an owner, a dependency set, and an explicit consistency policy.
For mutable product data, one option is a revisioned key. Assume $product was loaded from authoritative state, belongs to the authorized tenant, and its integer revision changes atomically whenever cached fields change:
PHP example
A newer revision uses a different key, while old entries expire naturally. This does not eliminate the need to read an authoritative revision or a consistently versioned snapshot; a stale replica can still direct a caller to old data.
For delete-on-write invalidation, I would trigger it only after commit and examine the race where an older concurrent reader repopulates the deleted key. Stronger requirements may call for version checks or durable invalidation events. Tenant ID, locale, permissions, and query parameters belong in keys when they affect the result. I never cache authorization-sensitive output under a broadly shared key.
101. What can go wrong with a distributed lock even when you use Laravel's lock API?
A distributed lock is a lease with failure assumptions, not an unconditional guarantee that only one actor can ever modify a resource. The dangerous case is a worker that pauses longer than the lease: another worker acquires the expired lock, then the first worker resumes and continues writing.
I would choose a shared lock backend, a precise resource key, bounded acquisition time, and a lease appropriate to the operation. Owner-aware release matters because an old worker must not delete a new owner's lock. Force-releasing a lock without establishing ownership can create concurrent execution.
For correctness-critical writes, I would also use a database invariant, optimistic version check, or fencing mechanism accepted by the protected resource. A fencing token is monotonically increasing, and the destination rejects stale tokens. Merely generating a token in Laravel is insufficient if the downstream system never checks it.
I would test expiry during execution, worker termination, network partitions, and backend failover. Cache failover to a different independent store can undermine coordination if workers no longer agree on the same lock state. For short local database changes, a transaction and row-level locking may be simpler and stronger than adding a separate distributed lease.
102. What changes when a Laravel application runs under Octane?
Under Octane, the application is booted once per worker and reused for multiple requests, so I have to reason about process lifetime as well as request lifetime. Code that was accidentally safe because PHP discarded everything after each request can retain stale data or leak information.
I would audit singleton services, static properties, global variables, custom listeners, and packages that capture a request or authenticated user during initialization. Request-specific values should be passed explicitly or resolved within the correct lifecycle. I would also test tenant A followed by tenant B on the same worker, not merely isolated requests that each boot a fresh application.
Octane can reduce repeated bootstrap cost, but it does not automatically fix expensive SQL, increase database capacity, or make every blocking operation concurrent. I would benchmark representative workloads and the chosen server rather than assume all supported servers expose identical features.
Deployment must reload long-running workers so they pick up code and configuration changes. I would monitor memory growth, worker restarts, error rates, and tail latency, with sensible request limits as protection. Recycling workers limits damage from leaks; it does not establish that leaked state or cross-request data retention has been corrected.
103. How would you find and fix a memory leak in long-running Laravel processes?
I would look for memory that remains reachable across completed requests or jobs. An unbounded static array, singleton cache, event-listener accumulation, or retained model graph can grow for the lifetime of a worker even when each individual operation succeeds.
I would reproduce the workload repeatedly in one process and compare memory after equivalent iterations, not just the peak during one large import. A lightweight diagnostic inside an existing job can record:
PHP example
These PHP metrics describe allocator usage; I also inspect process RSS because extensions and allocator behavior can differ. A rising peak alone does not prove a leak, whereas a steadily rising post-job baseline deserves investigation.
I would remove retained references, bound intentional caches, release external resources, and process large datasets incrementally. Garbage collection can reclaim eligible cycles, but it cannot discard an array that a live singleton still references. I would verify the fix over a sustained workload. Worker recycling and memory limits remain useful containment, but frequent restarts should not conceal a continuing leak.
104. How would you stop a singleton from retaining another request’s tenant or user context?
I would remove the captive request-specific dependency rather than assume a scoped dependency stays safe when a singleton retains it. If a singleton captures the first resolved tenant context, resetting the container's scoped instances cannot rewrite that singleton's existing property.
For tenant-bound services, I would scope the entire dependent object graph. Assuming existing TenantContext and BillingService classes, where the latter accepts the context, put these imports at file scope and the bindings in a provider's register() method:
PHP example
Validated middleware or job setup must populate the context before use; missing tenant context should fail closed. Laravel creates fresh scoped instances for a new request or job lifecycle.
If a service genuinely must be a singleton, I prefer making it stateless and passing tenant or user data into each operation. A carefully designed resolver can obtain current context at call time, but must not capture the original request or stale container. Consecutive cross-tenant worker tests verify the lifetime boundary.
105. How would you handle configuration caching and secret rotation in production?
I would treat cached configuration as a deployment artifact containing resolved values, including secrets, rather than a live view of .env. Application code should read configuration keys; environment access belongs in configuration files.
For example, this complete config/billing.php file reads a deployment-provided secret:
PHP example
The payment adapter then reads config('billing.provider_secret'). After config:cache, changing .env alone does not rebuild cached values or update services already held by long-running workers. I would regenerate configuration in the correct environment and reload those processes through the deployment procedure.
For rotation, I first establish the provider's overlap policy: deploy support for the new credential, verify successful use without logging it, then retire the old credential. Cached configuration files, build logs, backups, and diagnostic endpoints all require appropriate access controls.
Rotating APP_KEY is a separate operation because it affects encrypted application data and cookies. Laravel supports previous encryption keys for decryption during a controlled transition, but I would inventory affected data and plan retirement carefully. I would never generate a fresh application key as a routine deployment step.
106. How would you deploy Laravel code while old queue workers are still running?
I would deploy for a period when old and new web processes, workers, schemas, and queued payloads coexist. Restarting workers is necessary to load new code, but it does not make incompatible messages or schema changes safe.
I would first make schema changes backward-compatible and ensure the new consumer understands existing payloads. If a new payload cannot be read by old workers, I would version the message or introduce a controlled producer/consumer cutover. Removing or renaming a queued job class before its pending jobs are drained can break deserialization.
For standard workers, queue:restart requests a graceful restart after the current job. Horizon has its own horizon:terminate deployment command. A process manager must start replacement processes, and restart signaling must reach the workers' configured cache. I would verify the running release identifiers afterward rather than assume issuing a command completed the rollout.
The shutdown grace period must exceed the supported job duration, with idempotency protecting forced termination. I would watch queue age, retries, failed jobs, and business completion rates during a canary rollout. A rollback is only credible if the older code can still read the current database and messages; reverting the application directory alone may not restore compatibility.
107. How would you implement graceful shutdown and meaningful health checks?
I would make shutdown stop accepting new work while allowing bounded in-flight work to finish. For HTTP servers, that means removing the instance from load balancing before termination. For queue workers, it means stopping acquisition, allowing the current job to complete, and preserving recoverability if the grace period expires.
I would align container termination grace periods, process-manager stop settings, job timeouts, and queue visibility windows. Long operations should checkpoint or split into smaller idempotent units. A process receiving a signal is not proof that all its effects have committed.
Health checks need separate meanings. Liveness asks whether the process should be restarted; readiness asks whether it should receive traffic. Laravel's /up health endpoint confirms application boot by default, and its health-diagnosis event can add checks. I would not interpret a default successful response as proof that the database, Redis, and payment provider are healthy.
Dependency checks should be fast and proportionate. Making liveness fail whenever a shared database briefly stalls can cause every instance to restart and worsen the incident. I would test draining with real in-flight requests and jobs, and measure whether the deployment produces duplicate effects, dropped responses, or a prolonged recovery surge.
108. How would you trace a request across HTTP calls and asynchronous jobs?
I would attach a safe correlation identity to the business operation and propagate tracing context through each boundary. A request ID can connect logs, while trace and span identifiers show causality and where time was spent; they are related but not interchangeable.
For basic Laravel correlation, a request middleware can create an identifier before invoking the next handler. With these imports at file scope, the following belongs in its handle() method:
PHP example
Laravel Context can carry context into queued jobs. Full distributed tracing still requires instrumentation and propagation of the tracing system's headers or message metadata. I would preserve a business-operation ID across retries while identifying each processing attempt separately.
External identifiers must be validated or replaced rather than trusted blindly. I exclude tokens and personal data from trace baggage, control high-cardinality metric labels, and ensure long-running workers do not leak context between jobs. Finally, I verify traces across an actual asynchronous boundary, not only within one HTTP request.
109. How would you make logs and error reporting useful without exposing secrets?
I would log structured operational facts using an allowlist rather than dump entire requests, models, or provider responses. Useful fields include an internal operation ID, tenant identifier where appropriate, release version, retry attempt, elapsed time, and a controlled failure category.
For example, a payment failure log can identify the order and provider error class without including card details, authorization headers, cookies, raw tokens, or a complete request body. Redaction must happen before data reaches logs and error-reporting vendors, including exception breadcrumbs and HTTP instrumentation. Hashing an email address is not automatically sufficient anonymization.
I distinguish expected business rejections from unexpected failures so alerts remain actionable. The client receives a stable, sanitized error response and correlation ID, while protected reporting retains the technical detail needed for diagnosis. Production debug output should remain disabled.
I would test redaction using recognizable dummy secrets and inspect every configured log channel and reporting integration. Access controls, retention limits, encryption, and auditability matter after collection as well. Finally, I avoid swallowing errors just to reduce noise: a failed payment or background operation must remain visible in durable business state even if its exception is classified as non-alerting.
110. How would you defend a Laravel URL-fetching feature against SSRF?
I would avoid accepting arbitrary URLs when the product can instead accept a provider identifier and construct a known destination. Server-side request forgery becomes dangerous because the application may reach private networks, cloud metadata services, or credentials unavailable to the caller.
If arbitrary remote retrieval is genuinely required, I would isolate it behind restricted network egress and validate schemes, destinations, ports, and redirects. URL syntax validation alone is not a security boundary. DNS can resolve to private addresses, IPv6 and alternate address forms complicate filtering, and a validated hostname can resolve differently at connection time.
For a fixed, application-owned integration, these HTTP-client options illustrate only request limits, not a complete SSRF defense:
PHP example
Here $trustedEndpoint must come from trusted server configuration, not a submitted URL. For user-selected destinations, I would use a hardened fetch service or egress proxy that enforces destination policy at connection time, cap response size, and avoid forwarding credentials. Redirects require renewed validation, and security tests should cover metadata addresses, loopback, private ranges, and DNS rebinding.
111. How would you securely process untrusted file uploads?
I would treat uploads as untrusted content even after validation. The pipeline should authorize the uploader, limit size and type, generate storage names, quarantine the file, inspect it, and expose it only through an authorized delivery path.
Assume a configured private disk named private and a validated tenant context. In a controller action, these imports belong at file scope:
PHP example
This stores the upload under an application-generated filename; it does not certify that the PDF is harmless. I would persist its tenant, checksum, size, and processing state, then scan or sanitize it using an isolated, resource-limited worker before marking it available.
I would not trust a browser-supplied MIME type or original filename, allow executable uploads under the public web root, or let users choose arbitrary storage paths. Archives and document parsers need protection against decompression bombs and excessive resource use. Downloads require fresh authorization, suitable content headers, and short-lived access where appropriate. Failed or abandoned uploads also need a retention and cleanup policy.
112. How would you prevent authorization bugs in a multi-role, multi-tenant API?
I would authorize the action against the actual resource and tenant membership, not just check that the caller is logged in or has an admin label. A user can be an administrator in one tenant and an ordinary member in another.
My policy would combine tenant membership, the relevant tenant-scoped permission, resource ownership where required, and business state. Controllers, APIs, downloads, exports, and bulk actions must invoke the same rule. Hiding a button in the frontend or accepting only validated fields does not enforce authorization.
I would constrain resource retrieval to the authorized tenant and avoid trusting submitted ownership fields. Bulk operations deserve special attention: authorizing the first record does not authorize every ID in the request. Query-builder updates also bypass model-level code, so the permitted scope must be enforced explicitly.
For queued actions, I decide whether authorization is a durable grant made at submission or must be revalidated at execution. That decision matters when access is revoked before the job runs. I would test cross-tenant access, privilege changes, guessed identifiers, nested resources, and malicious bulk payloads. Privileged bypass hooks require a narrowly defined purpose and audit trail; a broad global override can silently defeat otherwise correct policies.
113. How would you manage the lifecycle of Sanctum personal access tokens?
I would give Sanctum personal access tokens a deliberate issuance, expiry, revocation, and rotation policy rather than treat them as permanent passwords. Each token should have only the abilities its integration needs, and sensitive issuance should require appropriate user verification.
Assuming Sanctum is installed and the user model uses HasApiTokens, this creates a seven-day read token:
PHP example
I return that plaintext value only through the authorized creation response and never log it. Sanctum stores a hash; clients must store the secret securely. Revocation deletes the appropriate token record, and expired-token pruning is cleanup rather than the mechanism that makes an expired token invalid.
Token abilities are only one authorization layer. Policies must still verify tenant access and resource permissions. Sanctum's first-party session authentication has different semantics, including tokenCan() behavior, so I would not use an ability check alone as a universal permission test.
I would support named-device or integration revocation, record safe usage metadata, and define incident procedures for leaked tokens. Rotation should overlap only as long as necessary and should not assume Sanctum automatically provides an OAuth refresh-token workflow.
114. How would you test concurrency and database-specific behavior reliably?
I would use real, separate database connections against the same engine and relevant configuration as production for concurrency tests. SQLite or a mocked repository can be useful elsewhere, but neither proves PostgreSQL or MySQL locking, isolation, collation, or constraint behavior.
For a stock-reservation test, I would seed one available item, synchronize two independent callers at the contention point, release them together, and assert that exactly one reservation succeeds, stock never becomes negative, and no partial order survives. I would repeat with duplicate idempotency keys and intentional transaction failures.
The test harness must allow genuinely concurrent commits. A surrounding test transaction can hide fixtures from another connection or defer after-commit behavior, so I would use a dedicated integration database and an explicit cleanup strategy. Separate processes need synchronization barriers or signals, not arbitrary sleeps that merely make the test flaky.
I also test the real queue backend for reservation timeout and redelivery behavior, and Redis for shared-lock semantics. Framework fakes verify that work was requested; they do not verify broker delivery or crash recovery. These slower tests belong in a focused integration suite, with deterministic assertions about business state rather than assumptions about execution order.
115. How would you test external API contracts without making the suite brittle?
I would separate fast tests of our adapter's decisions from contract checks against the provider's actual interface. Fakes can prove retry, parsing, and error-mapping behavior, but they cannot prove that the provider still accepts our request or returns the fixture we invented.
For example, inside a Laravel test method, with the Http facade imported at file scope, this checks a safe GET retry path:
PHP example
The expected outcome is two requests and a parsed value of 12. Production retry delays should respect the provider's throttling policy; zero delay here keeps the unit test fast.
At the application boundary, I would also assert authentication headers, serialized fields, error classification, and idempotency keys without exposing real secrets. A separate sandbox or schema-based contract suite verifies representative success and failure responses. Fixtures should be sanitized and versioned, and destructive or billable live operations should never run implicitly in ordinary tests.
116. How would you test expiry, delayed work, and time-sensitive behavior?
I would control application time explicitly and test the boundary conditions, rather than use real sleeps. Expiry bugs often come from disagreements about whether the boundary is inclusive, which timezone owns the rule, or which clock a component actually uses.
In a Laravel test method, this minimal example demonstrates the rule “expired at or after the expiry instant”:
PHP example
In the real application test, I would exercise the actual eligibility or token-validation path at those instants, including just before and after the boundary. I also cover daylight-saving transitions for local business schedules while storing instants consistently.
Laravel's test clock does not advance a real Redis server, database server clock, or remote provider. Delayed queue delivery and backend TTL behavior therefore need separate integration tests or injectable clock boundaries. I verify both the requested delay and the eventual business result, and always restore time so one test cannot silently influence another.
117. How would you investigate a production latency spike safely?
I would first establish the incident's scope and protect users, then narrow the cause with evidence. I would compare the affected routes, tenants, regions, and release versions, and distinguish increased request volume from slower service time.
My first signals are request rate, error rate, latency percentiles, queue age, CPU and memory pressure, database connection usage, lock waits, cache hit rate, and external dependency latency. A deployment, migration, traffic shift, cache eviction, or provider slowdown near the onset is a hypothesis to test, not automatically the root cause.
I would use sampled traces and existing metrics before enabling expensive global query logging. Broad cache flushes, unlimited retries, or rapidly adding workers can worsen overload. A safe mitigation might roll back a compatible release, disable a nonessential expensive feature, reduce concurrency, or shed low-priority work while preserving critical operations.
Every mitigation needs a measurable success condition and an owner. I preserve a timeline and useful evidence, verify recovery in business outcomes as well as HTTP status, and watch the backlog drain without re-saturating dependencies. Afterward, I turn the confirmed cause into a regression test, capacity adjustment, or operational guardrail rather than merely documenting that a restart helped.
118. How would you capacity-plan and load-test a Laravel application?
I would capacity-plan from workload characteristics and service-level targets, not from a universal requests-per-second claim about Laravel. The limiting resource may be database connections, CPU, Redis, network bandwidth, a provider quota, or an especially hot tenant.
For a queue receiving 80 jobs per second with mean execution time of 0.25 seconds, the average work in flight is roughly 20 jobs before allowing for bursts, retries, and headroom. That calculation is a starting estimate, not proof that 20 workers will meet the queue-age target. Tail durations and downstream contention can change the result substantially.
My load tests would use production-like data distributions, realistic request mixes, think times, authentication, and payload sizes. I would test cold caches, sustained load, sudden bursts, dependency slowdown, and recovery after overload. The load generator itself must be able to sustain the intended arrival rate rather than silently slowing down as the application does.
I would measure saturation points and graceful degradation, then set worker and connection budgets across the whole fleet. Autoscaling needs ceilings and backpressure so scaling the web tier does not overwhelm a fixed-size database. I would retain headroom and re-test after major changes in data volume or query behavior.
119. How would you plan a Laravel major-version upgrade with a controlled rollback?
I would treat a major Laravel upgrade as a compatibility project with a reproducible build, focused regression coverage, and a rollout plan. For this article's Laravel 13 and PHP 8.4 baseline, the framework's documented PHP minimum is 8.3; all installed packages and extensions still need their own compatibility checks.
I would inventory dependencies, read the relevant upgrade guide, identify customizations of framework internals, and resolve package constraints in a branch. Useful Composer checks at the appropriate stages include:
Bash command
The first diagnoses blockers in the current dependency graph. After installing the resolved lockfile on the target runtime, the second checks actual PHP and extension requirements. These commands do not replace application tests or guarantee behavioral compatibility.
I would test authentication, serialization, validation, queues, scheduled tasks, database behavior, and external integrations, including old queued payloads. The release should separate risky schema changes from the framework upgrade where possible.
I would canary the new build, compare errors and latency, and preserve the previous deployable artifact. Rollback requires backward-compatible data and message formats; restoring a lockfile cannot undo an irreversible business or schema change. Unrelated refactoring should wait until the upgrade is stable.
120. When would you use Laravel concurrency features, and what should remain sequential?
I would use concurrency for independent work when the expected latency reduction outweighs process startup, serialization, and downstream load. I would keep dependent state transitions sequential, or coordinate them through explicit transactions and durable workflows.
For example, with the process driver available and an already authorized scalar $tenantId, two independent dashboard counts can run concurrently:
PHP example
The results correspond to the supplied tasks, but they are not a single consistent database snapshot. Each child process performs its own work; I would not expect it to share the parent's active transaction, in-memory request state, or open resource handles.
I would pass small serializable inputs, bound task duration and fan-out, and define behavior when only some operations succeed. For outbound HTTP calls, HTTP-client pooling may be a better fit; for durable retryable work, queues are usually preferable. Laravel's sync driver is helpful in tests but cannot demonstrate actual parallel behavior. I would benchmark with realistic contention, because faster individual requests can still reduce system-wide capacity if they multiply expensive queries.
Laravel Documentation and Further Reading
Laravel 13 with PHP 8.4 is the supported baseline; Laravel 13 supports PHP 8.3–8.5 at review time. Older application skeletons and version-sensitive features are identified where relevant. PHP 8.4 is actively supported at this baseline. Examples are scoped implementation excerpts, not a complete application. Check the installed framework, first-party package, database, and runtime versions before applying them.
These 15 curated primary references cover the framework baseline, implementation, security, testing, and operations. They are a focused reading list rather than every page consulted. Source review and static artifact checks do not establish runtime execution or production readiness.
Similar Posts
