Expertise
PHP developer — since 2008
I have been writing every flavour of PHP since 2008; Laravel, Symfony or CodeIgniter makes little difference. Years of it have made this the ecosystem I am most at home in.
- Since
- 2008 Since
- years
- 18 years
- posts
- 88 posts
- projects
- 5 projects
Me in the PHP ecosystem
One question sits at the centre of the work I do as a PHP developer: where will the business rule live? The framework, the directory layout and the test surface all come after that answer. Build a project in the reverse order and the rule scatters across the application — which is exactly where maintenance cost begins.
What follows is what I do at application level and what I base those decisions on. Microservice boundaries, event-driven design itself and database internals I write about, with their costs, on sade.dev.
What I build in PHP
None of them is a brochure site — I use PHP where the business rules live.
-
Invoicing and payment flows
Systems that touch the flow of money and carry the business rule inside the application.
-
Asynchronous background work
Queues, consumers and idempotent consumption; the structures that take the synchronous wait out from in front of the user.
-
Framework setups
Laravel, Symfony and CodeIgniter; on work I start from scratch and in codebases I inherit alike. All three sit on the same language, and at application level the work is done not by the framework but by where you put the business rule.
-
Framework-free libraries
MIT-licensed packages that stand on their own, from a router to a PSR-11 container. Once a library is inside someone else's project, the signature is the one thing you cannot take back.
How I decide on a framework
I do not pick a framework when I take on new work, and what sets the order is not my taste.
- 01 Synchronous or asynchronous
- That one decision fixes the queue, the error contract and what happens when the same request arrives twice; it is the most expensive one to change later, and it comes before the framework.
- 02 The tools the team already knows
- If nobody on the team knows Symfony, writing that product in Symfony costs more than it returns.
- 03 The time given to the MVP, and the product's own needs
- The delivery date and the product's own requirements decide how much of what a framework brings ready-made I will actually use.
- 04 Performance, last
- The weakest argument in that decision. When I measured it, the gap between frameworks closes as soon as the request starts doing real work.
The order I follow in an inherited codebase
Not every piece of work starts from scratch, and in inherited code the first job is not rewriting.
- 01 Understand the behaviour first
- Where each rule sits, which side effect belongs to which request, how far the surface protected by tests reaches. An improvement made before those are clear risks breaking a working system invisibly.
- 02 Then widen the test surface
- Put the protection in place before touching the change; every correction after that costs less.
- 03 Version and dependency cleanup last
- An ordinary part of the work, held to the same measure: the cost of a change has to stay smaller than the relief it brings.
The three surfaces I set tests on
Tests are not a quality badge for me but the tool that lowers the cost of change, so they are set up as a surface from the start.
-
Where the business rule is exercised directly
The rule itself, tested without ever reaching the outside world. A rule with no test is a rule that can change quietly on the first busy day.
-
Where the outer boundary is imitated
The payment provider, the queue, a third-party service; the far side of the boundary is imitated so the test can run on its own.
-
Where the flow runs end to end
The path a request enters and a result leaves by. Only here does the case show up where every part is right on its own and the combination is wrong.
I do not cover architecture here
Microservice boundaries, event-driven design itself, database internals and cache strategy are not subjects I take on in their own right here; the place I write them is sade.dev.
Recent writing in this area
Fast from scratch, slow in a live system: the real cost of brownfield in the age of agents
Work an agent finishes in days takes a week in a system running for years: there the cost isn't producing code, it's finding and proving the rules.
Same message, different outcome: determinism in event-driven architecture
Why does the same message produce different outcomes in an event-driven system? The hidden inputs that break determinism — and four moves to win it back.
Measurements in this area
The fixed cost of installing a PHP framework: disk size tells you nothing
How many megabytes on disk, how many files per request and how many milliseconds on the first request do seven PHP frameworks cost — and which of those numbers actually predicts throughput under load?
Finding
Disk size predicts nothing: Yii2 has the largest vendor tree at 34.1 MB and loads only 62 files per request, among the fewest in the field. Files per request predicts nothing either: CodeIgniter loads 96 files and serves 6,431 req/s, Symfony loads 224 and serves 13,067. The one number that genuinely separates them is the first request served with a cold opcache: 2.2 ms for Phalcon, 72.2 ms for Laravel — thirty-three times. That is the compile bill the first visitor pays after every deploy, and it is measured in tens of milliseconds, not kilobytes.
measured 18 days ago
opcache preload cuts the deploy bill by up to fourteen times — but five of seven frameworks do not hand it to you
With `opcache.preload` on, how long is the first request seven PHP frameworks serve after a deploy, what does the gain cost, and who can actually have it?
Finding
Preload shortens the cold first request by between 3.5× and 14.2×: Symfony drops from 35.58 ms to 2.50 ms, down to Phalcon's bare figure. But only two of the seven candidates — Symfony and CodeIgniter — publish a preload file of their own; for the other five the gain sits on the table waiting for the user to write one. Writing one is not as easy as it looks: a preload generated blindly from the classmap never brings Symfony up at all, and on CodeIgniter it does worse (5.29 ms) than the hand-picked official file (3.13 ms). And the cost does not vanish: Laravel's classmap preload takes the 62 ms it saves each visitor and writes it back as 2,340 ms of php-fpm start-up.
measured 16 days ago
Seven PHP frameworks under identical load: the gap narrows as soon as the request does real work
On the same hardware, the same PHP build and the same seven routes, how many requests a second do Laravel, Symfony, CodeIgniter, Yii2, Phalcon, Laminas and Slim serve, and at what latency?
Finding
On an empty route the fastest is 4.4× the slowest (Slim 25,975, Laravel 5,966 req/s). As soon as the request does real work the gap closes: 3.7× for a single row from the database, 3.5× for twenty rows. Phalcon is third on an empty route and fifth once a query is involved — being a C extension buys nothing while the process waits on MySQL. And the expensive decision is not the framework: Laravel's own default `web` middleware group takes the same response from 5,858 to 2,176 req/s, so one default costs more than most of the distance between the frameworks.
measured 18 days ago
What I have built in this area
academia.sh: can theory and a real field problem live in the same lesson?
A free, publicly open, text-first learning platform that holds textbook theory and problems actually met in the industry inside a single lesson flow.
What it does today
Live as a free learning platform with no signup wall: the field–curriculum–course–unit–lesson hierarchy, full-text search, progress tracking and verifiable certificates all work. Anyone who wants to read the computer-science curriculum can use it today.
Qurandia: matching two datasets whose numbers disagree, by fingerprinting the text
An open, free resource for reading, understanding and learning the Qur'an, gathering Qur'an- and hadith-centred research under one roof.
What it does today
Live as a free, open resource for reading and searching the Qur'an and hadith side by side; letter-level fingerprint matching gives 91–99% coverage across the hadith collections. Readers and researchers who want to go straight to the source can use it today.
Personal and business finance on one data model
A finance core that keeps multi-account income and expense tracking in a single model; it became Parantaj, running on web, iOS and Android.
What it does today
One account/transaction model carries multi-account management, budgeting, goals and reporting on a single core; the web, iOS and Android clients are all live. Anyone who wants personal and business finances tracked in one place can use it.
What shipped in this area
Parantaj
OngoingOwner
A personal and corporate financial management platform. It offers income-expense tracking, budget planning, detailed reporting, and multi-account management.
BabelQueue
OngoingFounder & Developer
BabelQueue is a language-agnostic message queue standard that lets services written in different languages share the same queue without getting trapped by serialization lock-in. Instead of language-specific formats like PHP's serialize(), it defines a strict JSON envelope — frozen at schema_version 1 — that every language can read natively. It runs on Redis and RabbitMQ with no sidecar or broker plugin, adding under 2% overhead.
InitPHP
OngoingFounder & Developer
I published a minimalist MVC framework (Framework3) alongside 30+ modular PHP libraries (Router, Database, Cache, Mailer, Socket, ...) under a single ecosystem; all packages are available on Composer/Packagist under the MIT license. The codebase complies with PHP-FIG standards and is backed by type safety, PHPUnit tests, extensive documentation, and examples.
Questions in this area
14 questions answered on this axis.
Should I bind a Money value object to Eloquent with a custom cast or with accessors/mutators?
Integer cents in the DB, an immutable `Money` on the model: put the mapping in one `CastsAttributes` class and keep currency as a second column there.
Should I use chunkById instead of chunk when the same job also updates the rows it iterates?
Use `chunkById()` when there is real per-row work and never touch the cursor column; if you only update a column, switch to one set-based `UPDATE`.
How do I cleanly implement "withX" copy methods on readonly value objects?
A readonly withX returns a new instance: `new self` with named arguments is portable, and `clone($this, [...])` fits PHP 8.5 when validation is simple.
Technologies I pair it with
-
Laravel
The framework I reach for first where business rules and money move.
-
Symfony
My pick on projects assembled component by component, with sharp boundaries.
-
CodeIgniter
The lightweight framework I meet both on greenfield work and in codebases I inherit.
-
Composer
Dependency management; where I stopped downloading library zips in 2014.
-
PHPUnit
The base of the testing side; still what I meet in older codebases.
-
Pest
The testing surface I reach for on new projects.
-
Redis
Cache, session and queue store.
-
MySQL
Primary relational store.
-
RabbitMQ
Where billing left the synchronous path.
Frequently asked
7 questions
-
How long have you worked with PHP?
Since 2008 — 18 years. 88 posts of that stretch are recorded on this site.
-
Which PHP frameworks do you use?
My depth is in Laravel: my first Laravel project ran on Laravel 4, and the archive carries separate migration notes from 5 through 12. But Laravel is not my only framework — I have written Symfony and CodeIgniter both on work I started from scratch and in codebases I inherited.
-
Do you write PHP without a framework?
Yes. I have written packages that stand on their own — from a router to a PSR-11 container — without leaning on a framework; all of them MIT-licensed on Packagist. Writing PHP without a framework is also the shortest way to learn what the framework has been doing for you.
-
How do you pick a PHP framework for a project?
Three parameters decide it: the time given to the MVP, the tools the developers on the team already know and the product's own requirements. Performance is usually the weakest argument in that decision — when I measured it, the gap between frameworks closes as soon as the request does real work.
-
How do you start a new PHP project?
Not by picking a framework. First I decide whether the work runs synchronously or asynchronously. That single decision fixes the queue, the error contract and the idempotency requirement together, which makes it the most expensive one to change later.
-
Do you do architecture work as well?
Yes, but not on this page. This surface stays at application level; the architectural decisions themselves — with their costs — go on sade.dev.
-
How many projects used PHP?
5 of the projects recorded on this site touch the PHP side; most of what I delivered during the freelance years never made it into the portfolio.
Let us work together
If you have work in this ecosystem, tell me what you are building and we will talk about how to build it.