The AI SDK is the headline. Queue routing will save your production Tuesdays.

Laravel 13 shipped on March 17, 2026. If you have been following Laravel news, you’ve seen the announcements about the first-party AI SDK, passkey authentication and JSON:API resources. The write-ups are enthusiastic. Most of them are also, predictably, about the shiny things.
I’ve been running Laravel in production since version 5. My team is small, my production system is not small and my tolerance for framework-induced incidents is zero. So when a major release drops, I don’t read the marketing summary. I read the release notes, I look at the source diff and I ask one question: which of these changes reduces my operational surface area?
Here’s my actual read on Laravel 13, from that lens.
About That AI SDK
Let’s get this out of the way first, because it’s the feature every newsletter is leading with.
Laravel 13 ships a first-party AI SDK. It provides a unified API for text generation, tool-calling agents, embeddings, audio transcription and image generation. It also includes vector search capabilities through PostgreSQL and pgvector, which makes building semantic search a first-class experience in the framework.
This is impressive engineering. I’m not being dismissive.
The honest advice for most production teams right now is: don’t migrate your existing AI integrations to it yet.
Here’s the problem. If your team is already calling OpenAI or Anthropic APIs, you probably have an abstraction layer in place, whether that’s a dedicated service class, a wrapper around openai-php/client or something you've built internally. That layer already works. Ripping it out to adopt a brand-new framework abstraction adds migration risk with an uncertain payoff.
The Laravel AI SDK is new. The underlying spec will change. Edge cases will surface in the community over the next six to twelve months. Adopting it now for existing integrations means coupling your business logic to an API that may look different in Laravel 13.3.
For greenfield features? Use it. It’s the right default. For anything already running in production, wait for the community to stress-test it first.
One more thing worth noting: the vector search integration builds on pgvector, which means you need PostgreSQL. If your application is running on MySQL, that dependency is real and non-trivial. Don’t let a blog post convince you the upgrade is simple because the Laravel API is clean.
Queue::route() Is the Feature Operations Actually Needed
This one has gotten almost no attention. That’s a mistake.
Before Laravel 13, if you wanted to configure which queue connection or queue name a job used, you had two practical options. You could set $connection and $queue properties directly on the job class:
class ProcessPodcast implements ShouldQueue
{
public $connection = 'redis';
public $queue = 'podcasts';
public function handle(): void
{
// ...
}
}Or you could specify the queue at every dispatch site:
ProcessPodcast::dispatch($podcast)->onConnection('redis')->onQueue('podcasts');Both approaches have the same problem. Your queue topology is scattered. When you want to move all notification jobs to a dedicated high-throughput worker, you have to hunt through job classes and dispatch calls. When a new developer joins and asks which jobs go where, the answer is “check each file individually.”
Laravel 13 adds Queue::route(), which lets you define queue routing for any job class from a single location in your service provider:
use Illuminate\Support\Facades\Queue;
public function boot(): void
{
Queue::route(ProcessPodcast::class, connection: 'redis', queue: 'podcasts');
Queue::route(SendNotification::class, connection: 'sqs', queue: 'notifications');
Queue::route(GenerateReport::class, connection: 'redis', queue: 'heavy');
}Now your entire queue topology lives in one place. When you decide to move GenerateReport to a dedicated worker, you change one line in AppServiceProvider. You don't touch the job class, you don't update dispatch sites and you don't risk a merge conflict with the developer who was editing that job class last week.
This sounds like a minor convenience. At a company with twenty job classes and three queue workers, it’s not minor. It’s the difference between a topology change that takes ten minutes and one that takes an afternoon of grep-and-replace.
PHP Attributes: The Change That Helps New Developers Most
PHP Attributes have been in the language since PHP 8.0. Laravel never made them a first-class citizen for framework configuration. Until now.
Laravel 13 introduces 36 new PHP attribute classes spanning jobs, models, Artisan commands and more. For queue jobs specifically, you can now write:
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\Queue;
use Illuminate\Queue\Attributes\Tries;
use Illuminate\Queue\Attributes\Timeout;
use Illuminate\Queue\Attributes\Backoff;
#[Connection('redis')]
#[Queue('long_running_tasks')]
#[Tries(5)]
#[Timeout(120)]
#[Backoff([10, 30, 60])]
class ProcessVideoTranscode implements ShouldQueue
{
public function handle(): void
{
// Heavy lifting here
}
}Compare that to the pre-Laravel 13 equivalent:
use Illuminate\Contracts\Queue\ShouldQueue;
class ProcessVideoTranscode implements ShouldQueue
{
public $connection = 'redis';
public $queue = 'long_running_tasks';
public $tries = 5;
public $timeout = 120;
public $backoff = [10, 30, 60];
public function handle(): void
{
// Heavy lifting here
}
}The functional difference is minimal. Both approaches work. Both are supported in Laravel 13.
The practical difference shows up when someone who’s never seen your codebase opens the file. Attributes sit at the top of the class declaration where they’re impossible to miss. Class properties sit below the class signature and are easy to skip over in a busy file with injected dependencies.
For a senior developer, this is a mild ergonomic improvement. For a new hire reviewing twenty job classes in their first week, it matters. Configuration that’s visually distinct from logic is easier to audit and harder to accidentally override.
One thing to know before you adopt attributes wholesale: some PHP static analysis tools and IDE inspectors handle them better than others. If your toolchain relies heavily on PHPStan or Psalm, verify that your rules pick up attribute-based configuration before you migrate an entire codebase.
The old property-based approach isn’t going anywhere. This is an option, not a mandate.
JSON:API Resources: A Standard Finally Shipped Right
The JSON:API specification has been around since 2015. It defines a structured format for API responses: how to represent resources, how to include related data, how to handle sparse fieldsets and how to communicate errors. If you’ve ever built an API for a mobile app that needed predictable relationship traversal, you’ve probably either implemented this spec manually or reached for a package like timacdonald/json-api.
Laravel 13 ships a first-party implementation. To generate a JSON:API resource:
php artisan make:resource PostResource --json-apiThe generated class extends Illuminate\Http\Resources\JsonApi\JsonApiResource and uses a declarative property syntax:
class PostResource extends JsonApiResource
{
public $attributes = ['title', 'slug', 'body', 'published_at'];
public $relationships = ['author', 'tags'];
}The framework handles the full JSON:API response structure for you, including relationship inclusion, sparse fieldsets (where consumers can request only specific fields), the data / relationships / links nesting and the Content-Type: application/vnd.api+json header.
Who actually needs this? Teams with external API consumers. Mobile clients that need to traverse relationships without writing custom parsing logic. Public APIs where you want consumers to rely on a predictable, documented spec rather than your internal data structure decisions.
If you’re building an internal API consumed only by your own frontend, JSON:API may be more structure than the problem demands. A well-typed standard API resource will do fine. But if your API is consumed by third parties, JSON:API compliance gives them a contract they can rely on, and this implementation means you don’t have to maintain a third-party package to get there.
No Breaking Changes Has a Specific Meaning
Laravel 13 launches with zero breaking changes from Laravel 12. That’s worth pausing on.
For most typical Laravel applications, upgrading is straightforward:
composer require laravel/framework:"^13.0"
php artisan migrateIf that description sounds too clean, it’s because it mostly is. The Laravel team’s commitment to backward compatibility has improved significantly over the past several versions. The upgrade path from 12 to 13 is as close to frictionless as a major version bump gets.
But “zero breaking changes” has a specific meaning. It means the framework’s public API didn’t change in backward-incompatible ways. It does not mean every package in your vendor directory is ready.
Before upgrading a production application, run through this list:
Check your most critical packages against their respective changelogs and GitHub issues for PHP 8.3 compatibility notes. Packages that hook into internal Laravel APIs (custom queue drivers, custom cache drivers, framework extensions) are the most likely to surface issues. Test in a staging environment for at least one full deployment cycle before promoting to production.
The upgrade is easy. The due diligence is still your job.
The Real Story of This Release
Here’s the observation that gets lost in feature announcements: Laravel’s actual competitive advantage isn’t any single feature. It’s the upgrade cadence.
The framework ships a major version roughly once a year. Each one has zero breaking changes. Each one adds a small set of genuinely useful capabilities. The cost of staying current has dropped to nearly nothing for well-structured applications.
Compare that to the ecosystem in 2018 or 2019, when major version upgrades routinely meant a multi-week migration branch, a regression risk assessment and a scheduled maintenance window. That’s not what Laravel upgrades look like anymore.
Laravel 13’s standout features are PHP Attributes for job configuration, centralized queue routing via Queue::route(), first-party JSON:API resources and the AI SDK. If your team is building new AI-native features, the SDK is worth using from the start. If your application relies heavily on queues with complex worker topology, Queue::route() is worth ten minutes of your time this week.
Upgrade when your package dependencies are ready. Don’t upgrade into a production incident because a minor version of a critical package is still pinned to Laravel 12.
The boring advice is still: test staging, watch your logs for 48 hours and ship during a low-traffic window. Laravel 13 doesn’t change that.
If you’re still running Laravel 11 in production and skipped 12 entirely, now is a good time to run the upgrade path through both. The cumulative effort is still far less than any alternative. And PHP 8.3 is worth the move regardless of the framework version.
The upgrade is straightforward. The features that matter to you depend on what your production system actually does. Read the release notes with your own production logs in mind, not someone else’s benchmark.


