When we tell people Birkly is written in plain PHP — no framework, no Composer dependencies, no build step — the reaction is usually a polite pause. In a world of edge runtimes and typed template pipelines, plain PHP sounds like a step backwards. We think it is the most forward-looking decision in the whole project, and this post is the long version of why.
The argument from where it runs
Start with the least interesting property, because it turns out to matter most. PHP runs on effectively every web server on earth. Not "can be made to run" — runs, today, on the €3 shared host your client already pays for, on the university server nobody has SSH access to, on the box in the corner of a small manufacturer's office that is not allowed to talk to the internet.
A build step quietly excludes all of those. The moment deployment requires a toolchain, you have made a decision about your users' infrastructure that you do not get to unmake later. Every CMS that requires a build has, in practice, chosen its audience: people with a CI pipeline. That is a perfectly reasonable audience. It is just not the one we wanted.
Birkly deploys by copying files. That is the entire procedure. There is no artifact to produce, no lockfile to reconcile, no Node version to pin. If you can put a file on a server, you can run Birkly.
The argument from what you owe
Every dependency is a promise someone else has to keep. Most of them keep it. The ones that do not are the ones you remember.
A modest CMS built on a modern framework might sit on top of several hundred packages once the tree is fully resolved. Each is a maintainer who might lose interest, an account that might get compromised, a breaking change that might land in a minor version. You did not choose most of them. You cannot name most of them. But when one has a CVE, it is your Saturday.
A dependency you do not have is a vulnerability you do not patch, an upgrade you do not schedule, and a 2 a.m. page you do not get.
The honest counter-argument is that writing it yourself means writing bugs yourself, and you do not get the benefit of a thousand people having already found them. That is true, and it is a real cost. We accepted it because the surface area of a CMS is knowable. Image handling, routing, templating, auth, storage — it is a finite list, and none of the items on it are research problems. What we gave up in battle-testing we got back in the ability to read every line that runs.
The argument from backwards compatibility
PHP has been backwards-compatible to a fault for two decades, and it has taken a great deal of mockery for it. That mockery is the reason code written for PHP 5.6 mostly still runs today, and it is the reason we are comfortable telling people that a Birkly site built in 2026 will still work in 2036.
This is not a claim most stacks can make. It is not that the alternatives are badly maintained — it is that they move, deliberately and often for good reasons, and moving with them is work that someone has to do. For a CMS whose whole promise is "you can run this yourself, forever, without us," the ability to stand still is a feature we are not willing to trade.
The part nobody predicted: agents read boring code best
We chose plain PHP for the reasons above, all of which are about humans. The AI benefit was an accident we noticed six months in.
Ask an agent to extend a framework-based codebase and it has to reason about conventions it cannot see. Where does this route come from? What does this annotation do at runtime? Which of the four possible service containers resolved this dependency? The answers exist, but they are distributed across the framework's own source and its documentation, and an agent working from your repository alone will guess. Sometimes it guesses well. Sometimes it invents a method that has the right name and does not exist.
Plain PHP has nowhere to hide. Control flow is what the file says it is. A function does what its body does. An agent asked to add a field type opens the file that handles field types and sees the whole story, the same way you would.
This is what we mean when we say Birkly is built for humans and AI equally, and it is worth being precise about it, because the phrase is doing a lot of work in other people's marketing. It does not mean we added a chat sidebar. It means the architecture is small enough that any reader — a junior developer on their first day, an agent connected over MCP — can hold the relevant part of it in working memory at once. Simplicity is the AI feature.
What we gave up, honestly
This should not read as a costless decision, because it was not one.
No hot module reload. You refresh the page. It is fine, and it is also worse than what you are used to.
No type-checked templates. A typo in a field name fails at render time, not at build time. We mitigate this with clear error output, but a compiler would catch it earlier and we do not have one.
No package ecosystem. There is no composer require for the thing you need. If Birkly does not do it, someone writes it — as a plugin, in the same plain PHP. This is the cost that compounds, and it is why the Hub exists: the plugins we maintain are the ecosystem, and we would rather have twelve we can vouch for than four thousand we cannot.
Fewer developers who are excited about it. "Plain PHP, no framework" is not a recruiting pitch in 2026. We know. We would rather have the developers for whom it is one.
Where this leaves us
None of this is novel. It is the boring-technology argument, made by people who have been burned enough times to find boring appealing. What is new is that the same constraint now pays a second dividend we did not plan for, in a form of readership that did not meaningfully exist when the argument was first made.
Birkly will be free and open source at release, which means you will be able to check whether any of this is true rather than taking our word for it. If the philosophy resonates, join the waitlist — alpha invites go out to the list first.