An LLM call is just IO

One method, response back. Laravel 13 makes calling a model feel like calling a mailer, and the demo level is now twenty minutes of work. Which is exactly when architecture starts to matter, because everybody will ship the demo. An LLM call is IO. Slow IO, expensive IO, IO that sometimes fails and sometimes lies. We know how to handle IO like that. We forget, because the response text looks smart and the API looks native to the framework. ...

March 11, 2026 · 2 min · Murat Useinov

Laravel 12 is a boring release

Laravel 12 came out on Monday. Tuesday evening I upgraded one mid-size project. The whole diff is composer constraints and the lock file. Tests green, deployed before midnight. People in chats are disappointed. Where are the big features. A major that mostly refreshes dependencies and starter kits, is it even a major. I remember 4.2 to 5.0. New folder structure, new config system, half of the packages dead on arrival. We planned it as a separate project, because it was one. Skip a major, and the next one cost double. ...

February 26, 2025 · 2 min · Murat Useinov

The fastest request never reaches PHP

One header, and the FPM load graph fell off a cliff. Cache-Control: public, s-maxage=60 A public catalog page on one project renders the same HTML for every anonymous visitor. Same queries, same JSON from the search service, same template, thousands of times per hour. We profiled it, we tuned it, and only then asked the obvious question: why is PHP involved in the second request at all. HTTP had the answer before my career started. public says a shared cache may store the response. s-maxage gives the CDN or reverse proxy its own lifetime, separate from browser max-age. With Varnish in front, the request path splits in two. MISS: full stack, FPM, database, sixty milliseconds. HIT: the proxy answers from memory, the PHP process never starts, the database never hears about it. Nothing in the application got faster. There was simply less application running. ...

December 3, 2024 · 2 min · Murat Useinov

WebSockets change your operational model

Every client reconnects at once. That is what a deploy means now on one project of mine, since Reverb made WebSockets a first-party Laravel feature this spring and order updates go to the browser instead of polling. The code was the easy part. A classic PHP app is stateless between requests. An FPM worker takes a request, answers, forgets. Capacity planning is requests per second. A WebSocket server is the opposite animal: thousands of open connections that mostly do nothing, but each one holds memory and a file descriptor, and each one is state that dies with the process. Your quiet realtime feature has a thundering herd built in. Plan for reconnect storms, raise descriptor limits, and make the client reconnect with jitter, never on a fixed timer. ...

July 3, 2024 · 2 min · Murat Useinov

Laravel 11 removes files, not decisions

The diff of a Laravel 11 upgrade on one mid-size API this week is mostly deletions. Almost empty app/, configuration in one fluent bootstrap file, the slim skeleton I wrote about in January now real. It feels great. Now look at what survived untouched. The use case class that wraps an order state change in a transaction. The listener that must fire only after commit, because it queues an email about a row that must exist. The cache invalidation that follows every write to a heavily read table. The decision that validation lives in a request class and business rules one layer deeper. None of this is skeleton. None of it got smaller. ...

March 25, 2024 · 2 min · Murat Useinov

The shrinking Laravel skeleton

No app/Http/Kernel.php. That is the first thing you notice in the Laravel 11 preview, and the first thing every comment thread asks: where did my middleware go. The skeleton is the loudest change this quarter, louder than any feature. An app/ directory that is almost empty, no folder of middleware stubs, a pile of service providers collapsed into one, config files trimmed. The middleware did not go anywhere. It moved into the framework. The mechanism exists exactly as before, only the file does not, and bootstrap/app.php gets a small fluent API to reconfigure the stack when you actually need to. ...

January 6, 2024 · 2 min · Murat Useinov

Laravel 10 is a quiet major

Process::run('gzip -k dump.sql'). Out of the whole Laravel 10 release this week, that is the line I will actually use. The rest is quiet. Skeleton and framework code got native type declarations instead of docblocks, PHP 8.1 is the floor, and there is the Process facade. That is roughly the whole story. Some people are disappointed. I am not. A major release that is mostly maintenance means the framework is an adult. ...

February 16, 2023 · 2 min · Murat Useinov

Before Laravel 10: raise the baseline first

composer why-not php 8.1. One command, and it tells you who holds you back. Run it before making any plans. Laravel 10 comes next month and requires PHP 8.1. One project I help with is on Laravel 9 and PHP 8.0. The temptation is one heroic branch: new PHP, new framework, new package versions. When that branch breaks in production, you will not know which of the three changes broke it. ...

January 7, 2023 · 2 min · Murat Useinov

Laravel 9 and the boring major

One evening. That was the Laravel 9 upgrade on a mid-size project last week. Most of the diff was composer.json and a few config files. Laravel 9 is out this month. Symfony 6 components under the hood, PHP 8.0 minimum, Flysystem 3. And from now on, one major per year. The upgrade guide is short, and the “high impact” section is shorter. This is a policy: small majors, often. I want to defend this policy, because I remember the other kind. A project that sat on an old major for three years, because “we will upgrade when there is time”. There is never time. Then the version goes EOL, a security fix forces the jump, and you pay three years of drift in one horrible month. Packages you depend on drop support one by one, and suddenly it is not a framework upgrade anymore. It is archaeology. ...

February 25, 2022 · 2 min · Murat Useinov

Octane and state that outlives the request

One customer sees another customer’s cart. No crash, no error in the log. That is the bug Laravel Octane brings to an old codebase, and the benchmarks are the least interesting part of the story. Octane is out in beta, Swoole and RoadRunner became first-class overnight. In FPM every request gets a fresh application. Boot, handle, die. The model forgives everything. Under Octane the framework boots once and workers reuse it. Bootstrap cost drops to near zero, hence the pretty numbers. But every singleton is now shared between requests, and between users. ...

May 5, 2021 · 2 min · Murat Useinov