Reading PHP 8.6 RFCs as constraints

Accepted, declined, discussion drama. That is how most posts about PHP 8.6 RFCs read, and the vote cycle is in full swing this month. I read RFCs for a different reason. Not “what do I get”, but “what will my current code look like next year”. One RFC is a feature. Ten RFCs are a direction. When several push the same way, toward stricter types, explicit declarations, deprecating some old freedom, the direction tells you which of your habits are becoming legacy while you still type them. Syntax sugar is the least interesting part. Deprecations are the most interesting, because every deprecation is a migration with a countdown attached. ...

August 17, 2026 · 2 min · Murat Useinov

Dependency upgrades without a big bang

A composer.lock that had not changed in three years. A colleague showed it to me last week, not as a confession, as a normal fact about the project. Nobody decided this. Every single upgrade looked small and risky, so everybody postponed it. Three years of postponed small risks equals one migration project with a budget and a name. The alternative is dull and it works. Upgrades as normal operations, like backups. ...

February 27, 2026 · 2 min · Murat Useinov

PHP 8.5 one month later

Last week of December, low traffic, good test suite. That is when we moved one project to PHP 8.5. Not brave, convenient. A month later I can sort the release into two piles. Pile one is the demo features. Every release has them. They look great in a tweet-sized example, then you search the codebase and find two places to use them. Fine. Not why you upgrade. Pile two is the boring changes. Less boilerplate in places you touch every day. Types that say what a function really returns. Small things that make PHPStan complain earlier instead of production complaining later. This pile is why you upgrade, and you only see it after some weeks, never in the release notes. ...

January 19, 2026 · 2 min · Murat Useinov

From Kohana to PHP 8.5

A Kohana ORM, an N+1 on a catalog page, and a blog post about it. That was November 2014. It is December 2025, the code around me runs PHP 8.5, and I went back and read that first post. The surface changed completely. A 2014 controller was a fat method: it read the request, validated by hand, called the ORM, formatted the response, sometimes sent an email on the way out. Types lived in docblocks and in hope. Deploy was files over FTP and a prayer. Today the same feature is a typed handler with constructor injection, validation at the boundary, a use case object, a message on a queue for the email, a trace id through the whole thing. The engine checks what a comment used to promise. I do not miss the old way. ...

December 11, 2025 · 2 min · Murat Useinov

The November upgrade train

PHP 8.5 came out yesterday. Symfony 7.4 LTS and 8.0 land at the end of the month, same as every year. November is upgrade month now. First the cargo, then the timetable. From 8.5 I care about three things. The pipe operator makes transformation chains read left to right: $slug = $title |> trim(...) |> strtolower(...) |> (fn ($s) => preg_replace('/\s+/', '-', $s)); The new URI extension puts standards-correct URL parsing in core. parse_url had opinions instead of a specification, I will not miss it. And #[\NoDiscard] lets a method declare that ignoring its return value is a bug. Made for result-style APIs, where a dropped return is a swallowed error. ...

November 21, 2025 · 2 min · Murat Useinov

Coding agents meet legacy PHP

A strange-looking condition in a 2015 module, “fixed” by an agent. All tests green, static analysis clean, and the condition was a business rule with history. My review caught it. Barely. That came out of an experiment I ran this month: a coding agent on one legacy PHP module, since agents can do multi-file changes now and every old codebase suddenly looks like a target. The setup matters more than the model. I did not ask “modernize this”. I gave a narrow migration goal on one bounded module: replace a deprecated API, add proper namespaces, keep behavior. The same shape of task I would give to Rector. The difference is that the agent also handles the irregular cases Rector cannot express, the places where people wrote creative code in 2015. ...

October 10, 2025 · 2 min · Murat Useinov

vendor/autoload.php is not free

46 milliseconds. That is how long the bootstrap of one large application took before the framework even started, in a trace I pulled this week. A good share of it was autoloading. require vendor/autoload.php looks like a constant of nature. With plain PSR-4 rules it is a loop: every class load walks the prefixes and asks the filesystem whether a file exists. A few thousand classes on a cold request, and the loop becomes a number you can see in a flame graph. ...

August 20, 2025 · 2 min · Murat Useinov

Symfony 7.3 and the code I get to delete

Symfony 7.3 was released this week. I opened the release notes with a text file next to them. The file is a list of things to delete. Every project older than three years carries a layer of custom helpers that were reasonable when written. The framework moves, the helpers stay. Nobody reviews them, because they work. Two examples from one project. A base console command class with sugar for arguments and options, written around 2021. Symfony 7.3 has invokable commands, arguments come as parameters with attributes: ...

May 30, 2025 · 2 min · Murat Useinov

Native lazy objects, the ORM view

Open the Doctrine proxy folder of any project older than a year. A generated subclass for every entity, getters overridden, one file per class in the cache dir. It worked for fifteen years, and the seams always showed: final classes, public typed properties, instanceof surprises, a folder of code you pretend not to see. PHP 8.4 lazy objects were designed with exactly this in mind. The native way needs no generated class: ...

April 9, 2025 · 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