Skip to main content
The PHP attribute that yells at you for ignoring a return value: #[\NoDiscard] in 8.5
php

The PHP attribute that yells at you for ignoring a return value: #[\NoDiscard] in 8.5

PHP 8.5's #[\NoDiscard] makes the engine warn you when you call a function and throw away its result — the classic immutable-API footgun ($url->withScheme('https') that quietly did nothing). Why it's the rare attribute that improves your code by nagging you, where it fits in Drupal (Url, cache metadata, fluent withX() setters), the (void)-cast escape hatch, and why it only helps if your CI fails on warnings.

2026-09-07 / 03 min
Immich v3 quietly swapped my vector database — read the release notes to the end
tooling

Immich v3 quietly swapped my vector database — read the release notes to the end

Immich is the self-hosted Google Photos replacement everyone recommends, and it ships features faster than its commercial rivals. v3 also swaps the Postgres vector extension underneath your library (pgvecto.rs → VectorChord) on a point-major bump. 'Ships fast' and 'boring backup for irreplaceable photos' are in tension — snapshot the DB before you pull, pin your version, and keep a dumb app-independent copy of the actual files.

2026-09-07 / 04 min
RAG cost per query is a lie — the real bill is the re-index you haven't done yet
ai

RAG cost per query is a lie — the real bill is the re-index you haven't done yet

Everyone budgets RAG by the per-query LLM call, then gets blindsided three sprints in when they change chunking or swap embedding models and have to re-embed the whole corpus. Real numbers, why an embedding model is a lock-in decision disguised as a benchmark score (incompatible vector spaces), and how to budget for the re-index — plus when it genuinely doesn't matter.

2026-09-02 / 04 min
Proxmox went ARM — and it made me defend my boring x86 VPS
tooling

Proxmox went ARM — and it made me defend my boring x86 VPS

Proxmox VE 9.2 shipped official ARM64 support (Aug 2026, first-ever second architecture — but Grace/Vera servers, not Raspberry Pis). The homelab crowd cheered; I looked at my one x86 VPS running Docker on the host and defended it. Why a hypervisor is overhead you don't need on a single-tenant box — and the exact moment it would be worth it.

2026-09-02 / 04 min
Craft's project.yaml, from someone who's been burned by Drupal config sync
craft cms

Craft's project.yaml, from someone who's been burned by Drupal config sync

Picking up a Craft CMS backend as a Drupal veteran, the first thing I looked for was config management. Craft's project config solves the same problem with a gentler philosophy — the database stays the runtime source of truth and you can rebuild the YAML from it. Gentler is mostly good, and a drift trap if you skip allowAdminChanges=false. A working comparison.

2026-09-02 / 04 min
An honest OTel diet for a one-box blog
tooling

An honest OTel diet for a one-box blog

Observability tooling sells the full logs+metrics+traces meal, but my blog is one PHP process and one DB. Distributed tracing on a single-service box is air-traffic control for a paper plane. What actually earns its place: centralized log search + RED metrics from the access log. No always-on OpenTelemetry, no Tempo. The complement to my Beszel setup.

2026-08-24 / 04 min
Async PHP grew up in 2026 — does a Drupal developer need to care?
php

Async PHP grew up in 2026 — does a Drupal developer need to care?

Fibers are mature, AMPHP and ReactPHP share one event loop via Revolt, and the concurrency jokes are finally wrong. So how much must a Drupal dev learn? Almost none directly — Drupal's synchronous DB blocks the whole model. The one real win (parallel outbound HTTP in a queue worker) Guzzle already gives you, no Fibers required.

2026-08-24 / 04 min
You don't need Harbor: a tiny Zot pull-through cache for faster Coolify redeploys
tooling

You don't need Harbor: a tiny Zot pull-through cache for faster Coolify redeploys

Docker Hub's 100-pulls-per-6-hours (per IP) limit and slow cold pulls bite exactly when you're redeploying under pressure. A single-binary Zot pull-through cache fixes both. An honest comparison (Zot is more modern than registry:2, far lighter than Harbor — but not magically 'lighter' than registry:2), the real downsides, and whether one blog even needs it.

2026-08-17 / 04 min
Stop new \DateTime(): treat time as a dependency you inject
drupal

Stop new \DateTime(): treat time as a dependency you inject

Every time() and new \DateTime() buried in your logic is a hidden dependency on the one thing you can't pause — and it's why your time-sensitive tests are flaky. Inject Drupal's TimeInterface (already there) or Symfony's Clock (freeze and advance instantly in tests). Where Drupal's built-in falls short, and the discipline that makes 'what happens at midnight?' a test you can actually run.

2026-08-17 / 04 min
I ran one openssl command and found my blog was already post-quantum
security

I ran one openssl command and found my blog was already post-quantum

I've never configured a cipher on this blog, yet it negotiates hybrid post-quantum key exchange (X25519MLKEM768) by default — the same group as cloudflare.com — because Traefik's Go runtime switched it on. What that is, why it arrived for free, the caveats (it depends on the Go build; a 'hardened' curve config can silently disable it), and the honest 'it changes almost nothing for a public blog' verdict.

2026-08-17 / 04 min
Subscribe to Engineering