Order status is an enum now

One row in orders had status 'canceled', with one l. Nobody knows how long it sat there. The enum found it on the first day. PHP 8.1 enums are six weeks old, enough time in one project to say something practical. The first candidate was obvious: order status. For years it was a class with string constants, Order::STATUS_PAID, Order::STATUS_SHIPPED. Constants look safe, but nothing stops a function from receiving 'payed'. The parameter type was string, and string accepts everything. ...

January 8, 2022 · 2 min · Murat Useinov

Symfony 6.0 is boring, take the compliment

The deprecation counter in the profiler, three digits. That is where one project stands this week, and that number is the whole Symfony 6 migration. Symfony shipped 5.4 and 6.0 together at the end of November, same code, same day. 4.4 and 5.0 worked exactly like this two years ago, so the story is rehearsed. 6.0 is 5.4 with the deprecated code deleted. You never migrate to a major. You migrate to the last minor, and the major is a formality. ...

December 9, 2021 · 2 min · Murat Useinov

PHP 8.1: enums, readonly and a word about Fibers

'payed'. One letter, half a day of debugging, a few years ago. The string walked straight through OrderStatus::isValid(), because someone had added it to the constants list “for compatibility”. Every project I touched has that class: a bag of string constants, an isValid() helper, and a prayer. The type system never knew these strings were special. PHP 8.1 came out yesterday. Point release on paper, and it brings the feature I wanted since forever. Enums. ...

November 26, 2021 · 2 min · Murat Useinov

Generics we already have

A template calls getEmail() on an Order at three in the morning. The method returned array, the docblock said “array of User”, and eight months later someone put an Order in it. Everything was fine until it was not. PHP still has no generics and will not get them soon. Meanwhile PHPStan and Psalm shipped them anyway, in docblocks. Checked at analysis, not at runtime. For everyday backend work that turns out to be most of the value. ...

October 9, 2021 · 2 min · Murat Useinov

PostgreSQL 14 and the price of a connection

A few hundred connections, mostly idle, and the whole server gets slower. That was Postgres for as long as I remember it: a process per connection, and past some count the snapshot bookkeeping starts to eat everything. PostgreSQL 14 is released today. I had the RC on a test box for a week, so a few notes from a backend seat. The headline for me is that this internal work got a serious rewrite. On our connection-heavy profile, many workers, short queries, lots of idle time, the difference is visible without squinting. Idle connections finally cost close to what people always assumed they cost. ...

September 30, 2021 · 2 min · Murat Useinov

Redis Streams is a log, not a better queue

“Should we move the queue to Streams? They are the proper way now.” A colleague, this week, about our Redis list queue. Wrong question. Streams are not a newer version of the same thing. A list queue is a hand-off. LPUSH on one side, BRPOP on the other, and once a consumer takes the message, it is gone. Crash after the pop and the message died with you, unless you built the pending-list dance yourself. No history, no second reader. For “send this email eventually” it is honestly enough. ...

August 20, 2021 · 2 min · Murat Useinov

Nobody closes your database connection anymore

“MySQL server has gone away”, first thing in the morning, on a worker that had no traffic all night. That was week one after we moved a project from FPM to RoadRunner. For all these years FPM managed our database connections. Not with clever code. By dying. Process ends, connection closes, transaction rolls back, session variables reset. Nobody thought about connection lifecycle, because there was none. Move the same code to RoadRunner or Swoole workers and the topic is back. A connection lives for hours now. Two things go wrong, and both showed up within a week. ...

July 20, 2021 · 2 min · Murat Useinov

Attributes in Symfony 5.3

#[Route('/orders/{id}')] right above the method, and nothing in config/routes. Symfony 5.3 came out last week, and with PHP 8 attributes configuration finally found its place. Routes, autowiring hints, some validation, native syntax next to the code: #[Route('/orders/{id}', methods: ['GET'])] public function show(int $id): Response { // ... } I spent years defending YAML routing. The argument was separation: code is code, wiring is wiring, one file shows the whole URL map. The argument was never wrong. It lost to practice. In every real project the first thing you do with a route is jump to the controller, and the first thing you do with a controller is wonder which route hits it. Two files, one mental join, forever. Attributes remove the join. Rename a method, the metadata moves with it. Delete the class, no orphaned YAML block stays behind to confuse the next person. ...

June 4, 2021 · 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

Symfony Runtime: front controller as a callable

public/index.php, unchanged since I learned Symfony. Create the request from globals, run the kernel, send the response, terminate. Symfony 5.3 arrives next month, and the Runtime component rewrites this file. It looks like a small refactoring. It is a statement about where PHP is going. Baked into those few lines is one big assumption: one process, one request, then we die. FPM made the assumption true for fifteen years, so nobody saw it as an assumption. ...

April 16, 2021 · 2 min · Murat Useinov