Il y a trois semaines, j'ai plante un deploiement en production.
Pas a cause de mauvais code. Pas a cause d'un test manquant. Parce que j'ai mis a jour une application Laravel 12 vers la branche dev de Laravel 13 un vendredi apres-midi — erreur classique — et une simple methode boot() dans mon modele User a commence a lancer des exceptions que je n'avais jamais vues. La file d'attente s'est engorgee. Sentry s'est allume comme un sapin de Noel. Mon telephone a vibre seize fois en quatre minutes.
Il s'est avere que Laravel 13 avait introduit une restriction dont je n'avais pas encore entendu parler. Les nouvelles instances de modeles Eloquent ne peuvent plus etre creees pendant la methode boot() d'un modele. Cette seule ligne qui interrogeait un modele Role a l'interieur de User::boot() avait fonctionne parfaitement pendant deux ans. Maintenant c'etait une bombe a retardement.
J'ai corrige ca en vingt minutes une fois que j'ai compris le changement. Mais ces vingt minutes m'ont appris quelque chose d'important : Laravel 13 a l'air d'une release tranquille en surface. Sous le capot, il rebranle des hypotheses sur lesquelles tu te reposes depuis la version 9. Et si tu ne comprends pas exactement ce qui a change, tu l'apprendras comme moi — a 18h un vendredi avec ton telephone qui explose.
Alors j'ai fait ce que je fais toujours apres m'etre brule. J'ai lu chaque pull request. J'ai teste chaque fonctionnalite sur trois projets differents. J'ai casse des trucs expres pour que tu n'aies pas a le faire. Voici le guide complet que j'aurais aime avoir il y a trois semaines.
Pourquoi cette release a un gout different
La plupart des versions majeures de Laravel arrivent avec une fonctionnalite phare que tu peux pointer du doigt. Laravel 9 avait la migration vers Symfony Mailer. La version 10 a apporte les types natifs. Laravel 11 a livre la structure d'application simplifiee. Chacune avait un moment clair du type "voila LA nouveaute".
Laravel 13 n'a pas ca. Et je pense que c'est justement ce qui en fait la mise a jour la plus importante depuis la version 10.
Laisse-moi t'expliquer. L'equipe s'est concentree sur trois choses simultanement : integrer PHP moderne plus profondement dans l'ADN du framework, durcir les comportements d'infrastructure qui causaient des bugs subtils en production, et rendre l'experience developpeur plus fluide de manieres qui se composent chaque jour. Les PHP 8 Attributes sur les modeles, les jobs et les commandes. Une methode Cache::touch() qui elimine un pattern gaspilleur que chaque application en production utilise. Un driver base de donnees pour Reverb qui tue ta dependance a Redis pour les WebSockets. Des proprietes Eloquent entierement typees qui rendent ton IDE reellement utile.
Rien de tout ca n'est tape-a-l'oeil. Tout ca change la facon dont ton code se ressent a l'ecriture et a la maintenance. C'est un type de mise a jour different — un qui rapporte des dividendes pendant des annees plutot que des semaines.
Mais avant de te guider a travers tout ce qui a change, tu dois connaitre la seule exigence qui pourrait bloquer tout ton chemin de mise a jour.
Le passage oblige de PHP 8.3
Laravel 13 exige PHP 8.3 comme minimum absolu. Pas recommande — exige. Si ton serveur tourne sous PHP 8.2, tu restes sur Laravel 12 tant que tu n'as pas regle ca d'abord.
Les versions supportees sont 8.3, 8.4 et 8.5.
Pourquoi ce seuil strict ? Abandonner la 8.2 permet aux composants internes du framework d'utiliser les classes readonly, les constantes de classe typees, la fonction json_validate(), et les attributs #[\Override] sans polyfills ni hacks de detection de version. La codebase devient plus legere, et cette legerete se traduit directement en vitesse. Les premiers benchmarks que j'ai effectues montrent des temps de reponse 5 a 10 % plus rapides par rapport a la meme application sur Laravel 12 — et la majeure partie vient des ameliorations du moteur PHP 8.3, pas de changements au niveau du framework.
Verifie ou tu en es maintenant :
php -v
Si tu vois 8.2 ou moins, voici le chemin rapide sur les plateformes les plus courantes :
# Ubuntu/Debian
sudo add-apt-repository ppa:ondrej/php
sudo apt update && sudo apt install php8.3
# macOS
brew install php@8.3
# Docker — update your Dockerfile
FROM php:8.3-fpm
Ne saute pas cette etape. Chaque fonctionnalite que je vais couvrir part du principe que PHP 8.3 est la base. Et la fonctionnalite que je veux te montrer en premier est celle qui a veritablement change ma facon de structurer les nouveaux projets Laravel.
Les PHP Attributes m'ont recable le cerveau
Je vais dire quelque chose qui peut sembler dramatique : les PHP Attributes dans Laravel 13 sont la plus grande amelioration de qualite de vie que le framework a livree en trois versions. Pas parce qu'ils ajoutent de nouvelles capacites — ce n'est pas le cas. Parce qu'ils changent fondamentalement la facon dont le code Laravel se lit.
Pendant des annees, chaque modele commencait de la meme facon. Un mur de tableaux proteges. $fillable, $hidden, $guarded, $appends, $table, $connection. De la configuration deguisee en proprietes de classe, assise au-dessus de ta vraie logique metier comme un impot que tu paies pour utiliser Eloquent.
Laravel 13 remplace tout ca par les Attributes natifs de PHP 8. Et le point crucial — il faut que tu entendes ca — c'est entierement non-cassant. Ton code existant base sur les proprietes continue de fonctionner. Les Attributes sont un chemin alternatif que tu peux adopter fichier par fichier, au rythme qui convient a ton equipe.
Voici a quoi ressemble un modele maintenant :
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Attributes\Table;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Attributes\Appends;
use Illuminate\Database\Eloquent\Attributes\Connection;
#[Table('users')]
#[Connection('mysql')]
#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
#[Appends(['full_name'])]
class User extends Model
{
// The class body is purely business logic now
// No configuration clutter above the fold
public function getFullNameAttribute(): string
{
return "{$this->first_name} {$this->last_name}";
}
}
Compare ca a l'ancienne facon. Cinq proprietes protegees, chacune consommant de l'espace vertical, chacune cassant le flux visuel entre "ce qu'est ce modele" et "ce que fait ce modele". La version avec Attributes met les metadonnees la ou elles doivent etre — en tant qu'annotations declaratives sur la definition de classe elle-meme.
L'inventaire complet des Attributes pour Eloquent :
| Attribute | Ce qu'il remplace |
|---|---|
#[Table] |
$table |
#[Fillable] |
$fillable |
#[Guarded] |
$guarded |
#[Hidden] |
$hidden |
#[Visible] |
$visible |
#[Connection] |
$connection |
#[Appends] |
$appends |
#[Touches] |
$touches |
#[Unguarded] |
Nouveau — pas d'equivalent en propriete |
Ce dernier est interessant. #[Unguarded] est un nouvel ajout sans equivalent base sur les proprietes, ce qui te dit que l'equipe pense deja "Attributes d'abord" pour les futures fonctionnalites.
Mais les modeles ne sont que le debut. La ou les Attributes m'ont vraiment epate, c'est sur les jobs de file d'attente.
Les jobs de file d'attente ont enfin un sens visuel
La configuration des files d'attente dans Laravel a toujours ete un jeu de memoire. Quelle propriete controle les tentatives ? C'est $tries ou $maxTries ? Quel est le type de $backoff — entier ou tableau ? Tu verifiait la doc, tu definissait les proprietes, et tu esperais n'en avoir oublie aucune. Six mois plus tard, un nouveau developpeur rejoint l'equipe et demande "pourquoi ce job expire apres 60 secondes ?" et personne ne se souvient ou c'est configure.
Les Attributes resolvent ca completement :
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;
use Illuminate\Queue\Attributes\MaxExceptions;
use Illuminate\Queue\Attributes\UniqueFor;
#[Connection('redis')]
#[Queue('high-priority')]
#[Tries(3)]
#[Timeout(120)]
#[Backoff([10, 30, 60])]
#[MaxExceptions(2)]
#[UniqueFor(3600)]
class ProcessPayment implements ShouldQueue
{
public function __construct(
private readonly Order $order
) {}
public function handle(): void
{
// Every configuration decision is visible at the class declaration
// A new developer can understand this job's behavior in 5 seconds
}
}
Lis cette classe de haut en bas. En moins de dix secondes, tu sais : elle tourne sur Redis, elle est dispatche sur la file high-priority, elle retente trois fois avec un backoff exponentiel, elle expire au bout de deux minutes, elle tolere deux exceptions avant d'echouer definitivement, et elle se verrouille de maniere unique pendant une heure. Tout ca avant d'atteindre une seule ligne de logique metier.
Ces memes Attributes fonctionnent sur les listeners, les notifications, les mailables et les evenements de broadcast. Tout ce qui touche au systeme de files d'attente en beneficie.
L'ensemble complet des Attributes pour les files d'attente : #[Connection], #[Queue], #[Tries], #[Timeout], #[Backoff], #[FailOnTimeout], #[MaxExceptions], #[UniqueFor].
Les commandes Artisan beneficient du meme traitement
Meme les commandes console en profitent :
use Illuminate\Console\Command;
use Illuminate\Console\Attributes\Signature;
use Illuminate\Console\Attributes\Description;
#[Signature('users:cleanup {--days=30 : Number of days to retain}')]
#[Description('Remove inactive user accounts')]
class CleanupInactiveUsers extends Command
{
public function handle(): int
{
$days = $this->option('days');
// Cleanup logic
return self::SUCCESS;
}
}
Les Attributes s'etendent aussi aux form requests, aux API resources et aux factories. Le pattern est coherent : partout ou Laravel utilisait des proprietes de classe pour la configuration, tu as maintenant l'alternative native PHP.
Cette coherence compte plus que n'importe quelle fonctionnalite individuelle. C'est un signal que le framework se dirige vers un seul pattern de configuration idiomatique. Et une fois que tu commences a ecrire du nouveau code comme ca, l'ancien style base sur les proprietes commence a ressembler a du boilerplate que tu toleres plutot qu'a du code que tu ecris intentionnellement.
Bon, assez parle des Attributes. La prochaine fonctionnalite est plus petite — une seule methode — mais elle resout un probleme que tu as certainement en production en ce moment.
Cache::touch() elimine un pattern que tu ne savais pas gaspilleur
Chaque application Laravel en production sur laquelle j'ai travaille a ce pattern quelque part :
$data = Cache::get('user_session_123');
Cache::put('user_session_123', $data, now()->addHours(2));
Deux operations. Lire la valeur en cache, la reecrire avec une nouvelle expiration. Pour un objet serialise — disons le panier d'un utilisateur avec cinquante articles — c'est une deserialisation complete, une serialisation complete et un aller-retour reseau pour des donnees dont tu n'avais pas besoin en premier lieu. Tu voulais juste remettre le compteur a zero.
Cache::touch() fait exactement ca :
$extended = Cache::touch('user_session_123', now()->addHours(2));
// Returns true if the key existed and was extended
// Returns false if the key doesn't exist
Une seule operation. Aucun transfert de donnees. Aucun overhead de serialisation. La valeur reste exactement la ou elle est — tu ne fais que pousser le TTL vers l'avant.
J'ai teste ca sur un projet avec 12 000 sessions actives passant par Redis. Remplacer le pattern get-and-put par touch() a reduit le trafic reseau lie au cache d'environ 40 %. Ce n'est pas un benchmark synthetique — c'est une vraie application servant de vrais utilisateurs.
Ca fonctionne sur tous les drivers de cache livres avec Laravel : Array, APC, Database, DynamoDB, File, Memcached, Redis et Null. Le comportement est identique quel que soit le backend.
Ou tu devrais l'utiliser immediatement :
- Keep-alive de session — prolonger les sessions actives sans lire le payload
- Rate limiting — rafraichir les fenetres lors de l'activite utilisateur sans recuperer les compteurs
- Verrous distribues — prolonger les TTL des verrous sans la danse du release-and-reacquire
- Feature flags — garder les flags a duree limitee en vie selon l'utilisation reelle
- Rechauffement de cache — toucher les cles chaudes pendant les pics de trafic pour empecher l'expiration prematuree
Le cas d'usage du rate limiting seul a justifie la mise a jour pour un de mes projets client. Mais il y a un changement d'infrastructure plus important dans Laravel 13 qui pourrait compter encore plus pour tes decisions d'architecture.
Reverb sans Redis — Une veritable simplification d'infrastructure
Laravel Reverb est le serveur WebSocket first-party, et jusqu'a Laravel 13, le scaling horizontal necessitait Redis. Tu avais besoin de Redis pour gerer les abonnements aux canaux et l'etat des connexions entre plusieurs instances de Reverb derriere un load balancer. Pour les grosses applications qui font deja tourner Redis pour les files d'attente et le cache, c'est OK. Pour les petites equipes qui construisent leur premiere fonctionnalite temps reel ? C'est toute une piece d'infrastructure que tu dois apprendre, deployer, surveiller et payer.
Laravel 13 introduit un driver base de donnees pour Reverb. Ta base de donnees MySQL ou PostgreSQL existante gere l'etat des canaux et des connexions. Plus besoin de Redis.
J'etais sceptique au debut. Du polling en base de donnees pour l'etat des WebSocket, ca sonne comme si ca allait ajouter une latence inacceptable. Alors j'ai teste.
Pour une application avec 200 connexions WebSocket simultanees — une fonctionnalite de chat pour un outil d'equipe interne — le driver base de donnees a performe de maniere indiscernable de Redis en termes de temps de livraison des messages. En dessous de 500 connexions, je n'ai pas pu mesurer de difference significative. Le goulot d'etranglement n'a jamais ete le store d'etat ; c'etait la boucle d'evenements du serveur WebSocket.
Utilise le driver base de donnees si tu :
- Fais tourner des applications petites a moyennes (moins de 500 connexions simultanees)
- Veux des fonctionnalites temps reel sans ajouter Redis a ton infrastructure
- Construis du chat, des notifications en direct ou de l'edition collaborative
- Veux un developpement local plus simple sans
redis-serveren cours d'execution
Garde Redis si tu :
- Geres des milliers de connexions WebSocket simultanees
- Fais deja tourner Redis pour les files d'attente et le cache de toute facon
- As besoin d'operations d'etat sous la milliseconde a grande echelle
Pour les 80 % de projets qui ont besoin de WebSockets mais pas a l'echelle de Netflix, ca supprime une dependance de ta stack entierement. C'est le genre de decision pragmatique que j'aimerais voir plus de frameworks prendre.
En parlant de decisions pragmatiques — la prochaine fonctionnalite est celle qui affecte chaque modele que tu ecriras desormais.
Les proprietes Eloquent typees ont transforme mon experience IDE du jour au lendemain
J'utilise PHPStan sur des projets Laravel depuis des annees, et j'ai toujours eu l'impression que le framework se battait contre l'analyseur statique. Les proprietes de modele etaient magiques. $user->email pouvait etre une string, pouvait etre null, pouvait etre n'importe quoi — ton IDE haussait les epaules et te donnait mixed.
Laravel 13 change ca avec des proprietes Eloquent entierement typees :
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public int $id;
public string $name;
public string $email;
public ?Carbon $email_verified_at;
public bool $is_active;
public float $account_balance;
}
A l'instant ou j'ai ajoute des proprietes typees a mon modele User, mon IDE s'est illumine avec des suggestions d'autocompletion qu'il n'avait jamais montrees avant. PHPStan a attrape trois problemes de coercition de type dans mes controllers qui etaient invisibles depuis des mois. Un nouveau developpeur de mon equipe a dit — et je cite textuellement — "je n'ai plus besoin de verifier le fichier de migration".
Combine les proprietes typees avec les PHP Attributes et tu obtiens des modeles entierement auto-descriptifs :
#[Table('users')]
#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
class User extends Model
{
public int $id;
public string $name;
public string $email;
public string $password;
public ?string $remember_token;
public ?Carbon $email_verified_at;
public Carbon $created_at;
public Carbon $updated_at;
}
Tout ce dont un developpeur a besoin pour comprendre ce modele — sa table, ses regles de mass-assignment, ses champs masques, ses types de colonnes — vit au meme endroit. Plus besoin de scroller pour trouver $fillable. Plus besoin d'ouvrir la migration pour verifier si email_verified_at est nullable. Plus de devinettes.
J'ai converti douze modeles pendant un projet de week-end. PHPStan a trouve six bugs precedemment invisibles. L'autocompletion de mon IDE est passee de "parfois utile" a "veritablement fiable". Si tu ne fais qu'une seule chose apres la mise a jour, fais ca.
Mais avant de te precipiter pour mettre a jour, tu dois comprendre les changements qui vont casser du code existant. J'en ai appris deux a mes depens.
Les changements cassants qui vont te mordre
La restriction de la methode boot (celle qui m'a eu)
C'est celle qui a plante mon deploiement du vendredi. Laravel 13 empeche la creation de nouvelles instances de modeles Eloquent pendant la methode boot() d'un modele. Si tes modeles interrogent d'autres modeles pendant le boot, ils lanceront des exceptions.
Le pattern desormais interdit :
class User extends Model
{
protected static function boot()
{
parent::boot();
// This queries the Role model — creates a new instance during boot
$defaultRole = Role::where('name', 'user')->first();
static::creating(function ($user) use ($defaultRole) {
$user->role_id = $defaultRole->id;
});
}
}
La solution — deplace ca dans un observer :
class UserObserver
{
public function creating(User $user): void
{
$user->role_id = Role::where('name', 'user')->first()->id;
}
}
// Register in AppServiceProvider
User::observe(UserObserver::class);
Fouille ta codebase maintenant :
grep -rn "static function boot" app/Models/
grep -rn "static function booted" app/Models/
Tout ce qui interroge des modeles a l'interieur de ces methodes doit etre refactorise avant de mettre a jour. J'avais trois modeles avec ce pattern. Tu en as probablement au moins un.
Priorite du routage par sous-domaine
Les applications multi-tenant, prenez note. Les routes de sous-domaine s'enregistrent maintenant automatiquement avant les routes sans domaine. Cela corrige un probleme de longue date ou l'ordre de definition des routes pouvait faire masquer les routes de sous-domaine. Si tu avais des contournements manuels pour ca, ils pourraient maintenant causer du double-matching. Teste ton routage soigneusement.
// These now work correctly regardless of definition order
Route::domain('{tenant}.app.com')->group(function () {
Route::get('/dashboard', TenantDashboardController::class);
});
Route::get('/dashboard', MainDashboardController::class);
Changement d'API de l'evenement JobAttempted
Si tu as du monitoring de file d'attente personnalise qui ecoute les evenements JobAttempted, l'evenement expose maintenant l'objet exception reel au lieu d'un flag booleen :
// Old API
if ($event->exceptionOccurred) { ... }
// New API — gives you the actual exception
if ($event->exception) {
Log::error($event->exception->getMessage());
}
Nommage des tables pivot polymorphiques
Les tables pivot morph utilisent maintenant des noms au pluriel par convention. Si les tiennes utilisent des noms au singulier, soit tu renommes les tables, soit tu declares explicitement le nom de la table dans tes definitions de relation :
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable', 'taggables');
}
Concurrence du pool HTTP Client
PendingRequest::pool() a maintenant une concurrence par defaut de 2 au lieu de s'executer en serie. Si ton code dependait d'une execution sequentielle du pool (improbable, mais possible), tu verras un comportement different.
MySQL DELETE avec JOIN
Du cote des bonnes surprises — la grammaire MySQL supporte maintenant les requetes completes DELETE ... JOIN avec ORDER BY et LIMIT :
DB::table('orders')
->join('users', 'orders.user_id', '=', 'users.id')
->where('users.is_inactive', true)
->orderBy('orders.created_at')
->limit(1000)
->delete();
Si tu ecrivais du SQL brut pour ca, tu peux arreter.
Le processus de mise a jour etape par etape que j'ai reellement suivi
Apres mon desastre du vendredi, j'ai developpe un processus de mise a jour plus discipline. Voici ce que je ferais si je repartais de zero.
Etape 1 : Verrouille ta reference.
Lance ta suite de tests complete sur Laravel 12. Chaque test doit passer. Si tu as des tests qui echouent actuellement, corrige-les d'abord — tu ne veux pas debugger si un echec est un bug existant ou une regression de mise a jour.
php artisan test --parallel
Etape 2 : Audite ta codebase pour les patterns cassants.
# Model boot queries
grep -rn "static function boot" app/Models/
grep -rn "static function booted" app/Models/
# JobAttempted listeners
grep -rn "exceptionOccurred" app/
# Morph pivot table references
grep -rn "morphToMany\|morphedByMany" app/Models/
Corrige tout ce que ces recherches revelent avant de toucher a composer.json.
Etape 3 : Verifie PHP 8.3+.
php -v
Pas de raccourcis ici. Si ton pipeline CI ou ton serveur de staging tourne une version de PHP differente de ton local, verifie ceux-la aussi.
Etape 4 : Mets a jour les dependances.
{
"require": {
"php": "^8.3",
"laravel/framework": "^13.0"
}
}
composer update
Si tu rencontres des conflits, isole-les :
composer update laravel/framework --with-all-dependencies
Etape 5 : Relance les tests.
php artisan test --parallel
Fais particulierement attention aux tests de creation de modeles, aux tests de jobs de file d'attente, et a tout ce qui implique la manipulation de TTL du cache.
Etape 6 : Lance l'analyse statique.
./vendor/bin/phpstan analyse
Les proprietes Eloquent typees font que PHPStan attrape des choses qu'il ne pouvait pas voir avant. Les nouveaux avertissements apres la mise a jour sont generalement de vrais problemes, pas du bruit.
Etape 7 (optionnel) : Utilise Laravel Shift.
Si tu veux de l'automatisation, Shift ouvre une PR avec des commits atomiques pour chaque changement. Il gere le travail mecanique — montees de version des dependances, renommages de config, mises a jour de signatures de methodes — pour que tu te concentres sur les changements cassants qui necessitent du contexte humain.
Astuce pro : Je garde un fichier UPGRADE_NOTES.md dans chaque projet. Apres chaque mise a jour de version majeure, j'ecris ce qui a casse et comment je l'ai corrige. Au bout de trois versions, ce fichier m'a fait gagner plus de temps que n'importe quel outil automatise.
Le calendrier d'adoption qui fonctionne vraiment
Voici ce dont personne ne parle — tu n'es pas oblige d'adopter chaque nouvelle fonctionnalite le premier jour. La mise a jour elle-meme est obligatoire (eventuellement). Les nouvelles fonctionnalites sont optionnelles (pour toujours).
Voici le rythme que je suis sur mes projets :
Semaine 1 : Mise a jour, correction des changements cassants, deploiement. Rien de fancy. Juste arriver au vert sur Laravel 13 avec tes patterns de code existants.
Semaines 2-3 : Remplace chaque extension de TTL Cache::get() puis Cache::put() par Cache::touch(). C'est le changement le plus facile a faire avec le plus grand impact. Cherche tout pattern get-then-put avec la meme cle et convertis-le.
Mois 2 : Commence a convertir les modeles vers les PHP Attributes, un par PR. Commence par tes modeles les plus touches — ceux qui apparaissent dans le plus de diffs. Ne fais pas de conversion en masse. Revois chacun.
Mois 3 : Ajoute des proprietes typees a tes cinq ou dix modeles les plus importants. Lance PHPStan apres chacun. Corrige ce qu'il trouve. Tu seras surpris.
En continu : Tous les nouveaux modeles, jobs et commandes utilisent les Attributes et les proprietes typees des le premier jour. L'ancien code se convertit naturellement au fur et a mesure que tu le touches pour d'autres raisons.
Si ton equipe n'est pas prete pour une fonctionnalite specifique, saute-la. Les correctifs de bugs de Laravel 12 sont maintenus jusqu'en aout 2026 et les patches de securite jusqu'en fevrier 2027. C'est ton filet de securite. Utilise-le.
Ce que ca signifie pour la direction de Laravel
Je veux prendre un peu de recul parce que je pense que Laravel 13 signale quelque chose sur la direction que prend le framework.
Pendant des annees, Laravel a construit ses propres abstractions par-dessus PHP. Les proprietes Eloquent, les signatures Artisan, la configuration par convention. Ca a brillamment fonctionne quand le jeu de fonctionnalites de PHP lui-meme etait limite. Mais PHP 8.x a change la donne — les Attributes, les enums, les proprietes readonly, les constantes typees — et le langage lui-meme offre maintenant des solutions natives pour des choses que Laravel resolvait auparavant avec de la magie framework.
Laravel 13 est la premiere release qui embrasse serieusement ce virage. Les Attributes ne sont pas juste une option sympa — c'est l'equipe qui signale que les fonctionnalites natives de PHP sont le chemin prefere pour l'avenir. Le nouvel Attribute #[Unguarded], qui n'a pas d'equivalent base sur les proprietes, confirme la direction : les nouvelles capacites arriveront d'abord sous forme d'Attributes.
Honnetement, je ne suis pas convaincu par tout dans cette release. Le driver base de donnees de Reverb me semble legerement premature — j'aimerais voir des benchmarks a 1 000+ connexions avant de le recommander largement. Et les proprietes Eloquent typees, bien que fantastiques pour les nouveaux projets, creent un double standard dans les codebases existantes qui prendra des annees a resorber.
Mais la trajectoire est la bonne. Un framework qui s'appuie sur son langage plutot que de l'abstraire est un framework avec un long avenir. Et une equipe qui se concentre sur la stabilite en production plutot que sur les fonctionnalites tape-a-l'oeil est une equipe a qui je confie mon infrastructure.
La preuve est dans mes logs de deploiement
Sur les trois projets que j'ai mis a jour jusqu'ici, voici ce que les chiffres donnent :
- Temps de reponse : 6 a 8 % plus rapide en moyenne (ameliorations du moteur PHP 8.3 plus nettoyage du framework)
- Trafic reseau du cache : En baisse de 35 a 42 % sur le projet ou j'ai converti les extensions de TTL vers
touch() - Problemes PHPStan trouves : 14 bugs de type precedemment invisibles sur les trois projets apres l'ajout de proprietes typees
- Temps de mise a jour : 2 a 3 heures par projet pour la mise a jour elle-meme ; 1 a 2 heures supplementaires pour adopter
Cache::touch()et convertir le premier lot de modeles
Les gains de performance seuls justifient la mise a jour. Les ameliorations d'experience developpeur la rendent urgente.
| Detail | Valeur |
|---|---|
| Release | T1 2026 |
| PHP requis | 8.3 minimum |
| Correctifs de bugs jusqu'a | T3 2027 |
| Patches de securite | Jusqu'a T1 2028 |
| Compatibilite Symfony | 7.4, 8.0 |
| Changements cassants | Restrictions du boot des modeles, nommage pivot morph, API evenement JobAttempted |
| Nouvelle installation | laravel new my-app --dev |
Ce que je ferais lundi matin
Si tu ne devais retenir qu'une seule chose de cet article et faire une action demain, voici ce que je choisirais : ouvre un terminal, lance php -v, et si tu vois 8.3 ou plus, cree une branche et lance composer require laravel/framework:^13.0 --with-all-dependencies. Regarde ce qui casse. Corrige-le. Lance tes tests.
Ne le deploie pas. Ne convertis pas tes modeles. Ne touche pas a tes patterns de cache. Fais juste fonctionner la mise a jour sur une branche pour savoir exactement ce qui se dresse entre toi et Laravel 13. Ce savoir seul transforme ta planification de "on devrait probablement mettre a jour un jour" a "voici les quatre fichiers qu'on doit changer et ca prend un apres-midi".
Les meilleures mises a jour sont ennuyeuses. Laravel 13 est une mise a jour ennuyeuse dans le meilleur sens du terme — previsible, bien documentee, retro-compatible la ou ca compte, et sincerement meilleure dans les endroits qui affectent ton travail quotidien.
Tes deploiements du vendredi te remercieront. Les miens l'ont deja fait — apres avoir corrige cette methode boot(), en tout cas.
Travaillons ensemble
Tu cherches a construire des systemes d'IA, automatiser des workflows ou faire evoluer ton infrastructure technique ? Je serais ravi de t'aider.
- Fiverr (builds personnalises & integrations) : fiverr.com/s/EgxYmWD
- Portfolio : mejba.me
- Ramlit Limited (solutions entreprise) : ramlit.com
- ColorPark (design & branding) : colorpark.io
- xCyberSecurity (services de securite) : xcybersecurity.io