Largest Contentful Paint measures how long it takes for the largest visible content element — usually a hero image or an H1 heading — to finish rendering in the viewport. Google's recommended threshold is under 2.5 seconds on the 75th percentile of real user loads. Most sites fail this not because of a single bottleneck, but because of a chain of preventable delays, each adding a few hundred milliseconds.
GitHub Actions is the dominant CI/CD platform for PHP projects, and for good reason: it is free for public repositories, tightly integrated with your code history, and the marketplace has ready-made actions for every part of a PHP pipeline. This guide builds a complete, production-grade workflow from scratch: Composer install with caching, PHPStan static analysis, PHPUnit tests with a database, code style checks, and deployment to a remote server via SSH.
Drupal's performance story depends almost entirely on caching. A Drupal site without caching tuned correctly will struggle to serve a few dozen concurrent users. The same site with all three cache layers configured properly can handle hundreds. The three layers — Drupal's internal cache, Redis as a cache backend, and Varnish as a reverse proxy cache — serve different purposes and work together. This article explains what each layer does, how to configure it, and how to diagnose problems when the layers interact unexpectedly.
PHP 8.1 introduced enums as a first-class language feature. After a few years in the wild they are well-supported across frameworks, Drupal, and static analysis tools — but many PHP developers still use constants or pseudo-enum classes out of habit. This article covers everything you need to use enums effectively: pure enums, backed enums, methods on enums, interface implementation, and the patterns that emerge when you integrate enums into domain models, Drupal fields, and API responses.
Running Drupal locally with a Docker Compose stack gives you a reproducible environment that matches production. Every team member gets the same PHP version, the same MariaDB version, and the same Nginx configuration — no more "works on my machine" debugging sessions. This guide builds a complete, production-mirroring stack from scratch: Nginx as the web server, PHP-FPM for request processing, MariaDB for the database, and optional Redis for caching.
The result is a stack you understand completely, unlike opaque tools that hide the configuration from you.
Emacs has two mature LSP clients: Eglot, which ships with Emacs 29+ as a built-in package, and lsp-mode, a third-party package with a far larger feature surface. Choosing between them is not just a matter of features — it is a decision about how much configuration complexity you want to carry, what your workflow actually demands, and whether you are optimising for responsiveness or for capability.
This article compares both in detail, with specific guidance for PHP and Drupal development where both are viable choices.
For years Drupal used docblock annotations — special comments parsed at runtime by the Doctrine Annotations library — to declare plugins, entity types, and field formatters. PHP 8.0 introduced native attributes as a first-class language feature, and Drupal 11 now supports them everywhere annotations were used. Drupal 11.1 went further, making PHP attributes the preferred mechanism and marking the annotations approach for eventual deprecation.
Every Drupal project eventually needs custom code. Whether it is a new content type with business logic, a custom REST endpoint, or a block that pulls data from an external API — the answer is a custom module. This guide walks through building a complete Drupal 11 module from scratch: the required file structure, routing, a controller, a service, a custom block, and hooks implemented with the PHP attribute syntax introduced in Drupal 11.
All examples use PHP 8.3+, Drupal 11 conventions, and the web/ Composer project layout.
Property hooks are the headline feature of PHP 8.4. They let you attach get and set logic directly to a property declaration, removing the need for hand-written getter and setter methods in many common situations. IDEs and static analysis tools understand them natively — unlike magic __get/__set pairs, which are effectively invisible to tooling.
In a previous article I covered getting GPTel running with LM Studio and a local Qwen model — a great setup when privacy and offline access matter. But the Emacs AI ecosystem has grown considerably, and there is now a whole family of packages targeting different parts of a coding workflow.