Comparison and Decision Guide

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This sub-section’s WordPress bibliography lists the reference material consulted while preparing these pages.

Most WordPress projects make the same dozen choices, from where the site runs to whether WordPress is the right tool at all. This page collects them as decision tables. Each row links the page of this sub-section that explains the option and the official source behind it, and each table ends with the choice the Lumen Press project made. For a dated, sourced comparison of WordPress with fifteen other CMSs, see the CMS Comparison.

The guide was written against WordPress 7.1.3 (examples tested; released 2026-10-06), with PHP 7.4 minimum and 8.3+ recommended, WP-CLI 2.12.0, @wordpress/env 12.0.0, Action Scheduler 4.2.0 and WPGraphQL 2.25.0, and checked on 2026-10-28. WordPress 7.1.4, released on 2026-10-28, is the current security release and should be used in production; it changes no API. WordPress 7.2 is planned for 2026-12-08 and does not change any of the recommendations below so far. No prices are given anywhere: plans and fees change too often to be useful in a reference.

How to use this guide

Start from the decision tree, then read the matching table row and follow its link: the tables summarise trade-offs, the linked pages show configuration, code and pitfalls. Two principles run through every table:

  • Prefer what core provides. Core APIs (registered meta, the REST API, the Interactivity API, block themes) are maintained with every release and documented in the official handbooks. Ecosystem tools (page builders, WPGraphQL, Action Scheduler, field plugins) are good choices when a core API does not cover the need, but each adds a dependency that must be vetted and updated.

  • Choose the smallest thing that is enough, and keep the exit open. A custom table, a network, a headless front end or a page builder is hard to undo. Each table lists what leaving costs.

flowchart TD START(["A new Lumen Press feature"]) --> Q1{"Is it content that editors create and manage?"} Q1 -- yes --> Q2{"Is it one value per book, author or post?"} Q2 -- yes --> META["Registered post meta with show_in_rest, e.g. lp_isbn"] Q2 -- no --> Q3{"Many rows per item, queried or summed by value?"} Q3 -- yes --> TABLE["Custom table, e.g. the lp_stock movement history"] Q3 -- no --> CPT["Custom post type or taxonomy, e.g. lp_book, lp_genre"] Q1 -- no --> Q4{"Does it change how pages look?"} Q4 -- "layout or style" --> THEME["lumen theme: theme.json, template or pattern"] Q4 -- "a reusable element" --> BLOCK["A block in lumen-catalog: static, dynamic or PHP-only"] Q4 -- no --> Q5{"Does the browser need data from the server after the page loads?"} Q5 -- yes --> Q6{"Is it part of a block on the public site?"} Q6 -- yes --> IAPI["Interactivity API store calling a lumen/v1 REST route"] Q6 -- no --> REST["REST route in lumen/v1 with api-fetch"] Q5 -- no --> Q7{"Does it run on a schedule or in the background?"} Q7 -- yes --> Q8{"Thousands of jobs, retries or a history needed?"} Q8 -- yes --> AS["Action Scheduler, run by WP-CLI from system cron"] Q8 -- no --> CRON["WP-Cron event, triggered by system cron"] Q7 -- no --> Q9{"Is it an application with its own business rules and data model?"} Q9 -- yes --> APP["A separate application, e.g. Laravel, using WordPress over REST"] Q9 -- no --> HOOK["Hooks and core APIs in lumen-catalog"]

Figure: which approach to take for a new Lumen Press feature; each leaf is a row in a table below.

Where the site runs

"WordPress" names both the open-source software and commercial services built on it, and the choice decides who maintains the server and what may be installed. The differences are explained in Introduction and Architecture and the hosting types in Requirements and Hosting.

Option Choose when Costs and limits Covered in Official source

Self-hosted WordPress on a VPS, cloud or containers

The team can run Linux, PHP, a database and backups, and needs full control: any plugin, WP-CLI, custom server configuration, system cron, a persistent object cache

Operating system and PHP updates, TLS, backups, monitoring and incident response are the team’s job

Requirements and Hosting, Containers, Docker and Kubernetes

WordPress.org: Requirements

Self-hosted WordPress on shared hosting

A small site with low traffic and no administrator

Old PHP versions, low limits, often no SSH or WP-CLI, other customers on the same server

Evaluating a host

Hosting Handbook: Server Environment

Managed WordPress hosting

A content team that wants staging, backups, page caching and core updates handled by the host, and still needs the self-hosted software with its own plugins

Some plugins are disallowed, a host-specific caching layer, and export and lock-in terms to read before signing

Hosting types, Performance and Caching

WordPress.org: Hosting

WordPress.com

A blog or brochure site whose owner wants no server work at all

A hosted service from one company; what can be installed depends on the plan (installing plugins requires a paid plan); not the software this sub-section documents

Introduction and Architecture

WordPress Documentation: WordPress.org and WordPress.com

WordPress VIP and other enterprise platforms

Very large sites that want a platform with code review, deployment pipeline and support contract

Platform rules on plugins, code and deployment; contract terms

Scaling and High Availability

WordPress VIP

The deciding test is the exit: a full copy of files and database at any time, and nothing the host owns in the site (Importing, Exporting and Migrating).

Lumen Press runs self-hosted WordPress on two virtual machines (staging and production) with nginx, PHP-FPM 8.4, a managed MariaDB 11.8 database, a Redis object cache and a CDN for media, as described in Requirements and Hosting.

Which kind of theme

WordPress supports block themes and classic themes; "hybrid" is a classic theme with some block features, and page builders and multipurpose themes are ecosystem products layered on either. The kinds are defined in Themes Overview.

Option Choose when Costs and limits Covered in Official source

Block theme

New sites; owners should edit templates, headers and global styles without a developer; a consistent design system expressed in theme.json

Design freedom limited to what theme.json and patterns expose; PHP logic moves into blocks and plugins

Themes Overview, theme.json Settings, Templates and Template Parts

Theme Handbook

Classic theme

An existing theme that works; templates that depend heavily on PHP logic; a team that knows the template hierarchy

No Site Editor; the owner’s design surface is the Customizer, Menus and Widgets; new core features arrive as opt-ins

Classic Theme Anatomy and The Loop, Customizer API

Theme Handbook: Classic Themes

Hybrid theme

A classic theme on the way to becoming a block theme, or one that stays classic but wants theme.json, block template parts and patterns

Still a classic theme: wp_is_block_theme() is false and pages render through PHP templates; each stage is a valid stopping point

Hybrid Themes and Migration to Block Themes

Theme Handbook: Global Settings and Styles

Page builder or multipurpose theme

Non-technical editors need layout freedom on many unique landing pages and the site accepts the dependency

Lock-in (layouts in private meta or shortcodes), heavier front end, yearly licences for Pro features, leftovers on deactivation

Page Builders and Commercial Themes

Themes Team Handbook: Required

Two rules hold whatever the choice. Post types, taxonomies, blocks, shortcodes and roles are plugin territory, so content survives a theme change; and a third-party theme is customised through a child theme, never edited in place (Child Themes).

Lumen Press uses the block theme lumen and its child lumen-child (lumen-classic only teaches classic themes and the migration); editors fill patterns rather than design pages, the case that suits a block theme.

Existing plugin or custom code

Option Choose when Costs and limits Covered in Official source

Plugin from the WordPress.org directory

A common need (forms, SEO, backups, security, caching) that a maintained plugin covers; the directory listing passes the vetting checks

Code the team does not control; updates to follow; overlapping plugins add work to every request

Choosing and Managing Plugins and Themes

WordPress.org Plugin Directory

Commercial or freemium plugin

The paid features are needed and the vendor has a good update record

Licence renewals for the life of the site; an expired licence is an unpatched plugin; off-directory code needs its own review

Free, freemium and commercial

Plugin Handbook: Detailed Plugin Guidelines

Field plugin (ACF, Secure Custom Fields, Meta Box, Pods)

A site-specific build that needs field groups, repeaters and relationships quickly

The data model is owned by the plugin; leaving it means a migration; fields must be checked for REST exposure

Metadata and Custom Fields

Plugin Handbook: Metadata

Custom plugin

The feature is the business: its own content model, rules or integrations, which no plugin provides as needed; code that must be tested, reviewed and deployed with the site

The team owns maintenance, security reviews, compatibility with each core release and, if distributed, the Plugin Directory review

Plugin Fundamentals, Security for Developers

Plugin Handbook: Best Practices

Must-use plugin

Site policy that must always run and must not be deactivatable from the dashboard: update filters, environment settings, small integrations

No activation hooks, no automatic updates, loaded on every request; one file per plugin unless loaded by a loader

Must-use plugins

Advanced Administration Handbook: Must Use Plugins

Theme functions.php

Presentation only: theme supports, assets, block styles of that theme

Disappears on a theme change; never for post types, roles or data

functions.php, Theme Supports and Assets

Theme Handbook: What Is a Theme?

When a custom plugin needs another plugin, it declares it with the Requires Plugins header (6.5), which blocks activation until the dependency is active; see Plugin dependencies.

Lumen Press builds its catalogue, blocks, REST routes and jobs in the custom plugin lumen-catalog, uses vetted directory plugins for generic needs, and keeps update policy and environment settings in must-use plugins under version control, following its plugin and theme policy.

Where data is stored

The Plugin Handbook treats metadata as the default storage for data attached to content and custom tables as the exception. The detailed criteria, and measurements on the Lumen Press catalogue, are in WP_Query and the Database API.

Option Choose when Costs and limits Covered in Official source

Custom post type and taxonomies

A kind of content with its own editor screen, archive, URLs and permissions (books, events); classification shared by many items (genres, series)

Every item is a row in wp_posts; show_in_rest is needed for the block editor and REST

Custom Post Types and Taxonomies

Plugin Handbook: Post Types

Post meta (registered)

One value per post, read with the post, edited in the editor, exposed through REST or Block Bindings, exported with WXR, revisioned

Values are untyped strings in a key-value table; filtering or sorting many posts by meta value is slow

Metadata and Custom Fields

Plugin Handbook: Metadata

User, term or comment meta

A value that belongs to a user (a wishlist), a term or a comment

Same model and limits as post meta; user meta is personal data for the privacy tools

Term, user and comment meta, Privacy and Personal Data

Plugin Handbook: Metadata

Options and transients

Site-wide settings (options); cached results that may vanish at any time (transients)

Autoloaded options are read on every request; transients are not storage

Options, Transients and Object Cache

Common APIs Handbook: Options

Custom table

Many rows per item or none (logs, history, statistics) queried, filtered or summed by value; real column types, unique or composite indexes, atomic updates; written by a background process

Schema migrations with dbDelta(), uninstall clean-up, per-site tables on Multisite, own caching; no REST, export or privacy exporter for free

Custom tables and dbDelta

Plugin Handbook: Creating Tables with Plugins

Lumen Press stores books as the lp_book post type with the lp_genre and lp_series taxonomies; ISBN, page count and publication date as registered meta (lp_isbn, lp_pages, lp_published_on); the current stock of each book as the post meta lp_stock, written by the stock sync; the stock movement history (one row per change the sync makes) in the custom table {$wpdb->prefix}lp_stock; settings in the option lp_catalog_settings; and each reader’s wishlist in the user meta lp_wishlist.

How the browser talks to the server

Option Choose when Costs and limits Covered in Official source

admin-ajax.php

Existing code and admin screens that already use it

One URL for everything, the full admin bootstrap on every call, a nonce and capability check the handler must write itself, no schema; not deprecated, but not the choice for new code

JavaScript, Ajax and Heartbeat

Plugin Handbook: AJAX

REST API route

New code: anything a script, a block, the editor, a headless front end or another service reads or changes

Every route needs a permission_callback; cookie-authenticated requests need the wp_rest nonce or they run as an anonymous user

REST API: Extending It, REST API: Using It

REST API Handbook: Adding Custom Endpoints

Interactivity API (6.5+)

Front-end behaviour of server-rendered blocks: toggles, counters, filters, a wishlist button, region-based navigation; state shared between blocks

A front-end layer, not a transport: it still calls a REST route for data; directives and script modules to learn; full client-side navigation is still experimental

Interactivity API

Block Editor Handbook: About the Interactivity API

Heartbeat API

Periodic, low-priority polling on admin screens that already run Heartbeat (post locks, live counters)

Runs through admin-ajax.php on a timer; adds load on every open screen

The Heartbeat API

Plugin Handbook: Heartbeat API

Lumen Press exposes its data through the lumen/v1 REST namespace (/books, /loans, /books/<id>/read, /books/<id>/stock, /wishlist) and builds the public wishlist button as the Interactivity API block lumen/wishlist, whose store calls the /wishlist routes. The admin-ajax.php version on the JavaScript page exists only to compare the two transports.

How background work runs

WP-Cron is a scheduler triggered by page loads; it is not a queue. The details are in WP-Cron and Scheduled Tasks.

Option Choose when Costs and limits Covered in Official source

WP-Cron triggered by page loads (the default)

Hosts without a system scheduler; tasks that may run late (update checks, clean-ups) on a site with steady traffic

Late on quiet sites, an extra check on busy ones; depends on loopback requests

How WP-Cron works

Plugin Handbook: Cron

WP-Cron triggered by a system scheduler

Any server where cron, a Kubernetes CronJob or a managed scheduler is available: DISABLE_WP_CRON plus wp cron event run --due-now every few minutes

Both steps are required, or nothing runs; run it from one node only; once per site on Multisite; WP-CLI 2.12.0 does not stop overlapping runs, so wrap it in flock

Running WP-Cron from a system scheduler

Plugin Handbook: Hooking WP-Cron Into the System Task Scheduler

Action Scheduler (ecosystem library)

Hundreds or thousands of jobs, asynchronous jobs, retries after failures, a history of what ran, cron expressions

Not part of core; adds four tables; still needs WP-Cron or its WP-CLI runner, so the system-cron advice applies

Action Scheduler

Action Scheduler

ALTERNATE_WP_CRON

Last resort: loopbacks are blocked and no system scheduler exists

Visitors get a redirect and a doing_wp_cron query string that reaches analytics and caches

ALTERNATE_WP_CRON

Advanced Administration Handbook: wp-config.php

Lumen Press schedules lp_catalog_sync_stock (every six hours) and lp_catalog_privacy_cleanup as WP-Cron events and drives WP-Cron from the system scheduler every five minutes, under flock. The Action Scheduler version of the stock sync, with one asynchronous action per book, is the documented upgrade path when the catalogue grows.

One site or a network

Option Choose when Costs and limits Covered in Official source

Single site

One site, or several sections and languages of one site (a custom post type, a page tree or a multilingual plugin)

Nothing extra

Installation

Advanced Administration Handbook: Before You Install

Multisite network

Several sites run by one organisation with the same plugins and a shared design system, one login for staff, central updates; many similar sites created on demand

One plugin update or fatal error reaches every site; shared PHP and database; many plugins are untested on Multisite; splitting a site out later is manual work; content is not shared between sites without code

Multisite Networks, Multisite Development

Advanced Administration Handbook: Before You Create A Network

Separate installations

Sites owned by different clients, with different PHP or plugin versions, release schedules, or that may be sold or moved independently

Updates, users and monitoring repeated per site; shared code needs a deployment pipeline

Deployment and CI/CD, Composer-Based WordPress

Advanced Administration Handbook: Multisite

Lumen Press runs its imprints Lumen Kids and Lumen Noir in a Multisite variant with subdirectories, mapping Lumen Noir to its own domain, because all three sites use lumen-catalog, the lumen theme family and the same editors.

Which local environment

All the tools run the same repository; the choice is about what the developer is doing. The standard stack is described in Local Development Environments.

Option Choose when Costs and limits Covered in Official source

wp-env (@wordpress/env 12.0.0)

Plugin, theme and block development; PHPUnit tests with the WordPress test files; the tool used by Gutenberg

Needs Node.js and Docker; less control of the server stack; pin the database image (mariadb:11.8) and set testsEnvironment: false when the test instance is not needed

wp-env, Block Development Environment

Block Editor Handbook: @wordpress/env

Docker Compose with the official images

Full control of PHP, web server, database and extra services (Redis, Mailpit, nginx); closest to a containerised production stack

More configuration to write and maintain

Docker Compose with the official images, Containers, Docker and Kubernetes

Docker Hub: wordpress (official image)

Playground CLI and Playground in the browser

Throwaway sites in seconds, bug reproduction, demos and pull-request previews from a Blueprint, teaching

SQLite instead of MySQL, PHP compiled to WebAssembly, some extensions and network features missing; not a production replica

Playground and Blueprints

Playground documentation: Playground CLI

Desktop applications (WordPress Studio, Local, DDEV and others)

Developers and site builders who prefer a graphical tool, or a team already standardised on one

Each has its own configuration format; check the PHP and database versions it provides

Desktop applications

Advanced Administration Handbook: Running a Development Copy of WordPress

Lumen Press develops with wp-env, keeps a compose.yaml for work that needs the production services, and publishes a blueprint.json for demos and pull-request previews.

Traditional, hybrid or headless

The same installation is traditional when visitors read its theme’s pages, headless when only API clients read it, and hybrid when both happen; the general model is in CMS (Content Management System).

Option Choose when Costs and limits Covered in Official source

Traditional (theme renders every page)

A content site whose editors should see exactly what readers see: previews, Site Editor, plugins that add front-end output (SEO tags, forms, sitemaps) working unchanged

The front end uses WordPress’s stack (PHP, blocks, the Interactivity API)

Themes Overview, Interactivity API

Theme Handbook

Hybrid (theme site plus API clients)

The main site stays a theme, and some content also feeds an app, a widget or a second front end

An API surface to secure, version and cache alongside the site

REST API: Extending It

REST API Handbook

Headless (a separate front end renders every page)

Content reused across channels; a front-end team committed to a JavaScript framework; strict separation of the editing back end from the public site

Two applications to run and secure; previews, menus, SEO tags, sitemaps, forms and plugin front-end output must be rebuilt; authentication for drafts, CORS and cache invalidation on publish

Headless WordPress, Next.js

REST API Handbook: Using the REST API

Lumen Press is hybrid: the public site is the lumen block theme, and a Next.js reader app reads the catalogue over the REST API (Headless WordPress).

REST API or WPGraphQL

Core ships the REST API. WPGraphQL is an ecosystem plugin (version 2.25.0 at the check date, requiring WordPress 6.0 and PHP 7.4) that adds a GraphQL endpoint at /graphql; its directory listing describes it as becoming a canonical plugin. Faust.js, maintained by WP Engine, is a headless framework built on WPGraphQL.

Option Choose when Costs and limits Covered in Official source

REST API (core)

Most cases: no extra plugin, every core and many plugin features already exposed, plain GET URLs that HTTP caches and CDNs cache, custom routes with schemas and permission callbacks

Fixed response shapes: related objects need _embed or extra requests, and _fields trims nested properties of the resource itself (dotted names since 5.3) but not the objects inside _embedded

REST API: Using It, REST API: Extending It

REST API Handbook: Global Parameters

WPGraphQL (ecosystem plugin)

A headless front end that needs nested data (posts with authors, terms and meta) in one request with exactly the fields asked for, typed tooling, or Faust.js

One more plugin to vet and update; custom post types and taxonomies must opt in to the schema (show_in_graphql); queries sent by POST are not cached by HTTP caches without extra work (the WPGraphQL Smart Cache plugin)

Headless WordPress

WPGraphQL: WPGraphQL vs. WP REST API, WPGraphQL: Custom Post Types

The difference shows on a test site (WordPress 7.1.3, WPGraphQL 2.25.0, two posts by maria.editor, plain permalinks, so the REST route is passed as rest_route). The REST request asks for the post titles and the author’s name only:

curl -s 'http://lumenpress.test/?rest_route=/wp/v2/posts&per_page=2&_embed=author&_fields=id,title.rendered,_links.author,_embedded.author.name'

The response (1,867 bytes) contains the titles and, for each post, the whole embedded author object: id, name, url, description, link, slug, three avatar_urls and its own _links. The nested _fields entry did not trim the embedded object. The GraphQL request names exactly the fields it needs:

curl -s http://lumenpress.test/graphql \
  -H 'Content-Type: application/json' \
  -d '{"query":"{ posts(first: 2) { nodes { databaseId title author { node { name } } } } }"}'
{"data":{"posts":{"nodes":[{"databaseId":5,"title":"Spring catalogue announced","author":{"node":{"name":"maria.editor"}}},{"databaseId":4,"title":"The Glass Orchard is out","author":{"node":{"name":"maria.editor"}}}]}},"extensions":{"debug":[{"type":"DEBUG_LOGS_INACTIVE","message":"GraphQL Debug logging is not active. To see debug logs, GRAPHQL_DEBUG must be enabled."}]}}

The GraphQL response is 375 bytes, most of it the debug notice. The difference matters on list pages with many related objects, where REST needs _embed with full objects or one request per related resource; the REST URL, in turn, is a cacheable GET with no plugin. A query for users { nodes { email } } sent anonymously returned null for every email: WPGraphQL applies its own visibility rules, which must be checked for every type a plugin adds.

Lumen Press uses the REST API for its reader app: the routes it needs already exist in wp/v2 and lumen/v1, its requests are plain GET URLs that HTTP caches can store, and no additional plugin is required.

When WordPress is not the right tool

WordPress is a content management system: its strengths are editing, publishing, media, roles, themes and a large extension ecosystem. The general criteria are in When a CMS is the right tool, and when a framework is.

Situation Signs Better choice Covered in Official source

A complex application

The core is business logic, not content: bookings, billing, workflows with many states, a multi-tenant SaaS; most data does not fit posts and meta and ends up in many custom tables; the team fights the hook order and the request lifecycle more than it uses them

A web framework, with WordPress kept for the marketing site or blog if needed, the two talking over REST

PHP and Laravel, Web Frameworks Comparison, Backend Frameworks Comparison

Plugin Handbook: Introduction

An API with no editors

No one logs in to write content; the product is an API for apps or services

A backend framework; a headless CMS only if editors appear later

Backend Frameworks Comparison

REST API Handbook

An online shop

Products, carts, payments, taxes and orders are the core of the site

WooCommerce, the WordPress e-commerce plugin, for a shop that also publishes content; a dedicated e-commerce platform when the shop is the whole product. Both are the subject of the E-commerce section

WooCommerce Overview and E-commerce Platforms Comparison

WordPress.org Plugin Directory

The boundary is rarely all or nothing: a Laravel application can serve the logged-in product while WordPress serves the marketing pages, and a WordPress site can call a separate service, as lumen-catalog does with its distributor API.

Lumen Press is a content business (news, pages, a book catalogue, authors, events), which is the case WordPress was built for. Its loans and stock are small enough to stay in lumen-catalog, and it sells no books on the site: each book page links to local bookshops through the hooked lumen/local-bookshop block.

Checking where an existing site stands

On an inherited site, most of the questions above can be answered from the code and the database before reading a line of the theme. The script below prints the facts the tables depend on and runs under wp eval-file:

<?php
/**
 * Prints the facts the comparison tables depend on, for the site WP-CLI is pointed at.
 * Run with: wp eval-file decision-snapshot.php
 */
global $wpdb;

$theme = wp_get_theme();
if ( wp_is_block_theme() ) {
	$theme_kind = 'block';
} elseif ( wp_theme_has_theme_json() ) {
	$theme_kind = 'classic with theme.json (hybrid)';
} else {
	$theme_kind = 'classic';
}

$namespaces = rest_get_server()->get_namespaces();

$cron_events = 0;
foreach ( _get_cron_array() as $hooks ) {
	foreach ( $hooks as $events ) {
		$cron_events += count( $events );
	}
}

$rows = array(
	'WordPress'          => get_bloginfo( 'version' ) . ' (PHP ' . PHP_VERSION . ')',
	'Environment type'   => wp_get_environment_type(),
	'Theme'              => $theme->get_stylesheet() . ': ' . $theme_kind,
	'Multisite'          => is_multisite() ? 'yes, ' . get_blog_count() . ' sites' : 'no',
	'WP-Cron trigger'    => ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) ? 'system scheduler (DISABLE_WP_CRON)' : 'page loads',
	'Cron events'        => $cron_events,
	'Action Scheduler'   => class_exists( 'ActionScheduler' ) ? 'loaded' : 'not loaded',
	'Object cache'       => wp_using_ext_object_cache() ? 'persistent' : 'per request only',
	'Active plugins'     => count( (array) get_option( 'active_plugins', array() ) ),
	'REST namespaces'    => implode( ', ', $namespaces ),
	'GraphQL endpoint'   => function_exists( 'graphql' ) ? 'yes' : 'no',
	'postmeta rows'      => (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->postmeta}" ),
);

foreach ( $rows as $label => $value ) {
	printf( "%-19s %s\n", $label . ':', $value );
}

On a 7.1.3 test site after switching to the classic Twenty Twenty-One theme, activating WPGraphQL 2.25.0, setting DISABLE_WP_CRON and WP_ENVIRONMENT_TYPE with wp config set and publishing two posts:

$ wp eval-file decision-snapshot.php
WordPress:          7.1.3 (PHP 8.4.27)
Environment type:   staging
Theme:              twentytwentyone: classic
Multisite:          no
WP-Cron trigger:    system scheduler (DISABLE_WP_CRON)
Cron events:        9
Action Scheduler:   not loaded
Object cache:       per request only
Active plugins:     1
REST namespaces:    oembed/1.0, wp/v2, wp-graphql/v1, wp-site-health/v1, wp-block-editor/v1, wp-abilities/v1
GraphQL endpoint:   yes
postmeta rows:      6

Reading the output against the tables: a classic theme without theme.json is the starting point of the migration path; DISABLE_WP_CRON without a matching system cron entry means nothing runs; a large postmeta rows count with slow catalogue pages points to the custom-table criteria; and per request only on a busy site is the first item of Performance and Caching. The wp-abilities/v1 namespace is the Abilities API that core ships since 6.9 (Abilities API and MCP Adapter).

Where older books are outdated

The books this sub-section was compared with make several of these decisions for the reader, and some of the choices they present no longer hold:

  • Ultimate WordPress Handbook builds its sites around the Customizer and a GeneratePress/GenerateBlocks combination and calls full site editing "still maturing". Block themes have been the default since 5.9 and the focus of the Theme Handbook; a builder or multipurpose theme is a valid option with the trade-offs above, not a requirement.

  • Pro WordPress covers classic themes only and says core offers two-factor authentication and built-in caching and minification. Both are plugin or server features; the "existing plugin" and "where the site runs" tables are where they are chosen.

  • All-in-One For Dummies states that plugins cannot depend on other plugins. Since 6.5 the Requires Plugins header declares dependencies, which changes the "custom plugin that extends another plugin" case.