2024 in review: I stood up and talked about ddev
One new build this year. The rest was maintenance: dependency updates, a PHP version bump across three platforms, and one long-running project finally moving off a bundler I had been meaning to replace since 2023.
And I went back to the conference to give a second lightning talk, this time about ddev.
A lightning talk about ddev
A lightning talk, and I opened it with a disclaimer: I am not a systems engineer and not a nerd in that direction, so this is ddev from a developer's point of view.
Then I asked who in the room was already using it. One, two, and the moderator counting as a half.
Two people. I had genuinely expected more, and I had one bit of evidence prepared: the previous
speaker's demo had localhost in the URL bar, so I knew he was not using it either.
The pitch, in three commands. ddev config to set up the project, ddev start to run it, and
your site is on https://projectname.ddev.site with a working signed certificate. All of it lives
in one config.yaml that you commit, so every developer on the project gets the same setup rather
than the same instructions.
The framework integration is the part that lands with this audience, because your own commands come out of the box:
ddev sake dev/build # Silverstripe
ddev artisan migrate # Laravel
ddev wp core # for when a client insists on WordPress
The live demo. Empty directory, ddev config, answer three questions: project name, docroot
public, type Silverstripe. Then ddev start, then a Composer install of the installer, which
promptly threw an error I had never seen before in front of a room of people. Recovered, containers
rebooted, and the point survived: the generated .env has database, user and password all set to
db, because you never need to invent credentials on a machine that is yours.
The features I actually wanted to show, and these are the ones people had not seen:
- Snapshots.
ddev snapshot, then break something deliberately, thenddev snapshot restoreand pick the point you want back. The real use is adev/buildthat added a column you did not want. ddev import-dbandddev export-db, which is how a production dump gets onto your machine in one line.- Mailpit, included. No mail leaves your local environment, ever. I generated the test-mail task for that demo with GitHub Copilot, which I had given a lightning talk about the year before.
- Solr as a one-line add-on. I asked who had configured Solr on a local machine before, and then who had done it in under thirty seconds. That got the reaction the rest of the talk was building towards.
ddev status, and a command that opens your database in whichever GUI you prefer.
What the room actually asked afterwards. Not what I expected. The first question was to put the config file on screen and show how customisable it is, so we looked at the first fifteen lines together: PHP version, web server, database engine and version, even the Node version, all switchable by editing one line and restarting.
That question is the better talk, in hindsight. The demo sells convenience, but what people wanted to know was whether they could still control it.
Container queries replaced most of my breakpoints
In 2014 I recommended splitting your stylesheet into res_768, res_978 and res_1200. That
advice aged badly for one reason: a media query asks about the viewport, but a component does not
care about the viewport. It cares about the space it was dropped into.
Two lines set it up. Declare a containment context on the parent, then query it:
.card-slot {
container-type: inline-size;
container-name: card;
}
.card {
display: grid;
gap: 1rem;
}
@container card (min-width: 30rem) {
.card {
grid-template-columns: 12rem 1fr;
align-items: start;
}
}
The same .card is a stacked block in a narrow sidebar and a two-column layout in a wide main
area, on the same page, at the same viewport width, with no variant class and no JavaScript.
The unit is the part I underestimated. cqi is 1% of the container's inline size, so type can
scale to its container rather than to the window:
.card__title {
font-size: clamp(1.1rem, 4cqi, 1.6rem);
}
Drop that card in a 300px slot and the title is small. Drop it in a 900px hero and it is large. No breakpoints at all.
Two mistakes I made, both twice:
You cannot query the element you are styling. The container has to be an ancestor, so the pattern is always a slot plus the component inside it.
container-type: inline-size applies containment on the block axis. An element sized by its
content is fine, but anything relying on a percentage height from a child will collapse.
What is left for media queries: how many columns the overall page has, whether the navigation is a bar or a menu, print styles. Three or four for a whole site instead of one per component. Plus the ones that were never about width, which keep earning their place:
@media (prefers-reduced-motion: reduce) { ... }
@media (prefers-color-scheme: dark) { ... }
The unglamorous half
A PHP version bump across three platforms, which is always the same shape: the application logic is fine, the edges are not. A date format in one report, a library that had not been touched in four years, an extension that is no longer bundled.
I have stopped treating these as interruptions and started quoting them as scheduled work at a fixed point in the year. Clients accept a planned boiler service far more easily than an emergency.
In short
Second year speaking at the conference, this time on ddev. Container queries replaced most of my breakpoints, and a PHP version bump went across three platforms.