2023 in review: everything changed at once, in April
One project I started in April 2023 introduced four things to my stack simultaneously: a new bundler in Vite, a server-driven UI layer in Livewire, Pest as a test runner, and a CI pipeline. That is either reckless or efficient, and having lived with it for eight months, it was efficient. But only because it was a new project rather than a migration.
Later in the year a major CMS release landed and two long-running projects moved onto it. And in September I stood up at a conference and gave a lightning talk about GitHub Copilot, which is the first time writing code with AI has been worth a talk rather than a shrug.
Vite, the third bundler in nine years
Third one in nine years. The difference this time is that there is barely a config:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({ input: ['resources/css/app.css', 'resources/js/app.js'], refresh: true }),
],
});
What sold it was not the production build, it was the dev server. Native modules mean a change is on screen essentially instantly, no matter how big the project is. Coming from a bundler that rebuilt the world on every save, the first hour is genuinely startling.
Cost to know about: the dev server serves modules that the production build then bundles differently. Twice this year something worked in development and broke in the build. Both times it was an import that only resolved because of the dev server's more forgiving resolution. Run a production build locally before you promise anything.
Livewire
The other adoption. Instead of an API endpoint plus a client-side component plus a serialiser, the component lives on the server, renders HTML, and updates over the wire.
For CRUD-shaped work, which is most of what I build, it removes a whole layer. No endpoint to write, no duplicate validation, no state that can disagree with the database. The cost is a network round trip per interaction, so it is a bad fit for anything that must feel instant, and I keep a Alpine for those bits.
The rule I settled on: server-driven for anything that touches the database, client-side for anything that only touches the DOM.
Pest
Same underlying engine, different surface. The tests read like sentences, and that matters specifically when you open a project after two years. Migrating the existing suites was a find-and-replace plus a day of tidying, and I have not written a class-based test since.
Upgrading the CMS to a major version
Three projects, none dramatic, all tripping over the same things. Short version: the CMS release is mostly the previous one with a higher PHP floor and deprecated API removed. The strategy that made it boring:
Do the deprecation pass on the last old release first. Turn deprecation notices on while you still have a working site, click through the admin and the front end, fix everything it shouts about. Every notice you fix before the upgrade is a fatal you do not debug after it.
Most of the work is PHP, not the CMS. Passing null to strlen(), trim() or str_replace()
is deprecated in PHP 8.1 and it is everywhere in helper methods that assume a field is set. Static
analysis at the lowest level found more of these in ten seconds than an hour of clicking.
The module inventory is the whole risk. One command tells you which dependency is holding you back:
composer why-not vendor/recipe 5.0
Every time, it was a small third-party module with a single maintainer. Two had a compatible release. One did not, and I forked it: the change needed was a version constraint and two return types. Find that out before you quote the job, not halfway through it.
A lightning talk about GitHub Copilot
In September I gave a ten-minute lightning talk at the conference about GitHub Copilot. I opened by asking the room who was already using it. Four hands. Then who had tried it and stopped: another five. So most of the room had not touched it.
The other speakers had run long, so I skipped the talking and spent the whole slot on a live demo in PhpStorm.
What it was good at. I made an empty Vehicle.php data object. It suggested the namespace. I
gave it a class name, waited a couple of seconds, and it filled in the database fields and a
relationship to a dealer I had not written yet. Then I made a controller, and it proposed both
actions, index and show, before I had typed either. Repetitive structure that I have written a few
million times, produced instantly.
The part I did not expect was the ORM. The organiser asked from the floor for a filter, so I wrote one for cheap vehicles, and it got the framework's query syntax right. That syntax is exactly what normal code completion is worst at, because it is not standard PHP. The trick I have settled on is to write a comment saying what I intend, then start the variable, and let it finish.
What it was not good at. Templates. I opened a .ss file and got nothing useful, because as
far as Copilot is concerned that is plain HTML with no framework in it. Editing in the middle of an
existing line is also hit and miss.
The honest part. The organiser volunteered from the floor that the countdown on the conference website had been written by Copilot, and that it turned out to be off by one, spotted two weeks before the event. His summary of the whole technology beat anything on my slides: you get new errors, but faster.
My own description on the day was that it is an employee you value who is not the brightest. I would stand by that. Use it for the parts where you already know the answer and typing is the only obstacle.
At the end I asked who would try it after the talk. Fourteen hands, up from four.
Joining a board
The non-technical change: I joined the board of the community organisation around the CMS I have built on since 2012. After eleven years of taking from an open source project, being on the side that keeps it running is a different relationship with the same software.
It also changes how you read an upgrade guide. When you know roughly how many people wrote it and on what budget, "the module inventory is the whole risk" stops being a complaint and starts being a description of a maintenance economy you are now part of.
And a pipeline that runs the tests
Embarrassingly late. Push, tests run, and a red result blocks the merge. The value is not the automation, it is that the tests now run when I am tired, which is exactly when I skip them.
In short
Vite, Livewire, Pest and CI all landed in one April project. Started using GitHub Copilot and gave a conference lightning talk about it. A major CMS upgrade across three sites, and a seat on a board.