← All field notes
Debugging

Where AI-generated WordPress code quietly breaks — and how to catch it

The LLM writes clean-looking code until it doesn't. These are the five failure modes that pass code review and fail in production.

AI-generated WordPress rarely fails loudly. It fails quietly — the page renders, the plugin activates, the demo works — and then leaks data or corrupts output under conditions the model never simulated. Loud bugs you catch in seconds; these you catch in a breach report. Here are the five that slip through most often, and the exact tells that give them away.

Unescaped output and missing sanitization

Models happily echo variables straight into HTML. It looks fine with clean test data and becomes stored XSS the moment a value contains a tag. The tell is any echo of a variable without an esc_* wrapper. The fix is boring and non-negotiable: sanitize on the way in, escape on the way out, every time.

// What the model wrote:
echo '<a href="' . $url . '">' . $title . '</a>';

// What ships:
printf(
    '<a href="%s">%s</a>',
    esc_url( $url ),
    esc_html( $title )
);

Direct database queries without prepare()

Ask for a custom query and you'll often get string interpolation straight into SQL. It runs perfectly until an input contains a quote — or an attacker supplies one. Any $wpdb->query or get_results built with . concatenation or an interpolated variable is a red flag:

$rows = $wpdb->get_results(
    $wpdb->prepare(
        "SELECT * FROM {$wpdb->posts} WHERE post_status = %s LIMIT %d",
        $status,
        $limit
    )
);

Hook timing and the "works on my machine" bug

Models reach for hooks by name recognition, not lifecycle. They'll register a post type on init but try to use get_current_user_id() before pluggable.php is loaded, or enqueue on wp_head instead of wp_enqueue_scripts. The symptom is intermittent — fatal on a fresh load, fine after a cache warms. When something works inconsistently, suspect the hook, not the logic.

Deprecated functions and confident staleness

An LLM's training data is a graveyard of old WordPress. You'll get wp_get_sites(), get_currentuserinfo(), or a jQuery-bound admin script in a block-editor world. These don't error immediately; they rot. Grep every generated file against the current deprecation list before you trust it.

Catch it with a harness, not vigilance

You cannot eyeball this reliably at volume, and you shouldn't try. Run WordPress Coding Standards via PHPCS with the security sniffs on, and let the machine flag unescaped output and unprepared queries. Add a fast smoke test in wp-env. The model is a fast, forgetful junior developer — pair it with a linter that never forgets, and the quiet failures get loud exactly when you want them to: before deploy.

Why models drift

Half of "the AI wrote bad code" is really the model losing track of your constraints mid-session. That failure mode has a name and a research home: Hidden State Drift ↗.

More field notes

BlocksVibe-coding a Gutenberg block with an LLM in the loopSecurityThe security cost of AI-written plugins: the gaps models skipAI BehaviorHidden State Drift in your dev loop: why the model forgets your codebase mid-session