It's easy to hop on trends. There's always a new framework trending on X, a "you should be using this by now" post making the rounds, a stack everyone suddenly has an opinion on. It's much harder to sit with something unglamorous, ship it to production, and let the results speak instead of the tooling.
FujiCommerce — the multi-seller commerce platform I've written about on this blog — was not built with whatever was popping at the time. It was built with raw, rugged PHP. Not Laravel. Not Symfony. Vanilla PHP 8.2, talking directly to a MySQL database, which we later migrated to PostgreSQL once the concurrency guarantees we needed — row-level locking, strict transactional isolation — outgrew what the original setup could comfortably give us.
The Question I Kept Getting Asked
At the time, PHP was simply my go-to. No internal debate, no framework bake-off, no long design doc justifying the choice. I reached for it the way you reach for a tool you already know works.
What I didn't expect was how often I'd have to defend that decision after the fact. Other engineers, recruiters, people skimming the stack in a project write-up — they all asked some version of the same question, usually with a raised eyebrow: "Why PHP?"
I got asked that question so many times that I stopped giving the long answer and started giving the honest one: "Why not?"
It wasn't a deflection. It was the actual answer. In that context, the language genuinely didn't matter. What mattered was whether the system held up under concurrent writes, whether the data stayed correct when two sellers hit the same inventory row at once, whether checkout stayed consistent when a payment failed halfway through. PHP did the job. It shipped real transactions, for real sellers, under real concurrent load, and the database never once corrupted a seller's inventory or double-charged a customer.
The Language Was Never the Hard Part
I was more worried about architecture than I was about which runtime it ran on. The actual engineering problems on FujiCommerce had nothing to do with PHP's syntax:
- How do hundreds of independent sellers write to shared inventory and order tables without corrupting each other's data?
- How do you guarantee a stock decrement is atomic under concurrent checkout attempts, without serializing every write and killing throughput?
- How do you keep payment state and inventory state consistent when a request fails halfway through — a dropped connection, a timeout, a retried click?
None of those questions care what language you're asking them in. They get solved — or not solved — at the level of schema design, transaction boundaries, and locking strategy. A framework doesn't answer them for you, and neither does a language. That work happens whether you're writing PHP, Node.js, Java, or anything else.
A small, unglamorous slice of what that looked like in practice — no framework, no router magic, just PHP doing exactly what it needs to do and nothing more:
<?php
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
// No framework router. A handful of routes, matched directly,
// because the whole app never needed more than that.
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
match (true) {
$path === '/checkout' && $_SERVER['REQUEST_METHOD'] === 'POST'
=> require __DIR__ . '/checkout.php',
$path === '/orders'
=> require __DIR__ . '/orders.php',
default
=> http_response_code(404),
};
That's it. No annotations, no service container resolving a graph of dependencies to handle a POST request. Just PHP, a database connection, and enough discipline around transactions and row locks to make the hard guarantees hold.
What Actually Matters
I've since done just as much production work in Node.js and TypeScript, which is where most of my recent builds live. That doesn't change the thesis — it reinforces it. The concurrency problems I solved in PHP on FujiCommerce are the same shape as the ones I solve in TypeScript today: lock the right row, bound the right transaction, make failure modes explicit. The syntax changes. The engineering discipline doesn't.
The language you pick is a detail. The architecture you design around it is the job. Chase the trend if it genuinely fits the problem — but don't mistake the trend for the solution.
So if you're hiring and the stack on a past project isn't the one currently trending — ask what the system actually had to guarantee, and whether it did. That's the question that tells you whether an engineer can build scalable backend architecture, not whether they picked the fashionable language to do it in.
If you're building — or hiring for — a system where correctness under concurrency actually matters, regardless of the language on the job description, get in touch.