Skip to content
Blog

AI-Generated WordPress Code Review Checklist

November 12, 2026 · Nordic Digi Solutions · 17 min read

AI will write you a WordPress feature in seconds: PHP snippets, CSS fixes, JavaScript, WooCommerce hooks, .htaccess rules, even whole templates. The code can look right and still break checkout, forms, redirects, AJAX, the REST API, login or the mobile layout, and the risk climbs when it touches WooCommerce, server rules, user data or payments.

This checklist is a practical way to review AI-generated code before it goes near functions.php, a snippets plugin, a custom plugin, .htaccess, theme templates or your JS and CSS. Keep using AI; just review what it gives you before your customers see it. For PHP snippets in particular, our companion post on what to check before you paste AI-generated PHP into WordPress goes deeper.

Why AI-generated WordPress code needs review

A real site runs a theme, a stack of plugins, often WooCommerce, caching, AJAX, the REST API and third-party scripts. AI writes for a clean example instead, and the gap shows up as checkout warnings, redirect loops, blocked requests or clashing function names. A quick review is cheaper than emergency debugging.

1. Understand what the code is supposed to do

Write the purpose in one sentence:

Make the WooCommerce phone field required.

If you can’t, don’t add the code yet. Be clear about which page or feature it should affect, which files it changes, whether it is a design, functionality or server change, who it affects, and whether it needs to run everywhere or on one page. Good code has a clear scope.

2. Check where the code should go

AI often says “add this to functions.php“. The WordPress Theme Handbook explains the trade-off: a theme’s functions.php only runs while that theme is active, so a theme change takes the feature with it, and features that should work whatever the site looks like belong in a plugin. A practical split:

  • Theme files for layout, templates, menus and asset loading. If you didn’t write the theme, use a child theme, so the next theme update doesn’t wipe out your change.
  • A small custom plugin for business logic: checkout rules, order metadata, redirects, form processing, user roles and custom post types.
  • WooCommerce hooks and filters for checkout fields, emails and cart behaviour, once you know which checkout the store runs (point 14).
  • .htaccess only for changes that truly belong at server level.

3. Check whether the code uses WordPress hooks correctly

WordPress code runs through hooks, and the hook decides when it runs. The first six below fire in this order on a front-end page load; admin_init belongs to admin requests and save_post to saving content:

init
wp_loaded
template_redirect
wp_enqueue_scripts
wp_head
wp_footer
admin_init
save_post

WooCommerce hooks AI likes to use (the checkout ones belong to the classic checkout, see point 14):

woocommerce_checkout_fields
woocommerce_before_checkout_form
woocommerce_after_checkout_form
woocommerce_thankyou
woocommerce_email_order_meta
woocommerce_checkout_update_order_meta

The wrong hook runs code too early, too late, on the wrong page, during AJAX or admin requests, or before WooCommerce is ready. This redirect is hooked far too broadly:

add_action('init', function() {
    wp_redirect(home_url('/login/'));
    exit;
});

init fires on front-end pages and wp-admin requests alike, and before WordPress answers a REST API call, so this sends all of them, /login/ included, to /login/. The site loops and wp-admin is unreachable. Adding a check like is_page('login') doesn’t rescue it either: conditional tags only work once WordPress has run its query, and before that they quietly return false. WordPress’s documentation names template_redirect as the hook for redirects; point 10 shows a safe version.

4. Check variable safety

AI-generated PHP often assumes data exists:

$id = $_GET['id'];

If id is missing, PHP 8 raises Warning: Undefined array key "id". Safer:

$id = isset($_GET['id']) ? absint($_GET['id']) : 0;

Treat $_POST, $_SESSION, $_COOKIE, order meta, options and user meta the same way: does the key exist, is the value the expected type, and what happens if it is missing? Where only a few values make sense, validate against a list; WordPress prefers validation to sanitization and recommends a strict in_array() check for this. Small warnings matter: a PHP warning printed into a WooCommerce AJAX response is one of the causes WooCommerce lists for a checkout that never stops loading.

5. Check sanitization

Sanitizing cleans input before you store or use it. Common functions:

sanitize_text_field()
sanitize_email()
sanitize_key()
sanitize_title()
absint()
sanitize_url()
wp_kses_post()

Bad:

$name = $_POST['name'];
update_option('myshop_customer_name', $name);

Better:

$name = isset($_POST['name']) ? sanitize_text_field(wp_unslash($_POST['name'])) : '';
update_option('myshop_customer_name', $name);

Two details AI often gets wrong. WordPress adds slashes to $_GET, $_POST and $_COOKIE, so strip them with wp_unslash() before you sanitize. And match the function to the data: sanitize_title() turns a string into a slug, so it is the wrong tool for a product title, while sanitize_url() (esc_url_raw() is an alias) is right for a URL you are about to store.

6. Check escaping

Escaping makes output safe at the moment it is printed. Bad:

echo '<p>' . $message . '</p>';

Better:

echo '<p>' . esc_html($message) . '</p>';

Use esc_attr() for attributes, esc_url() for URLs and wp_kses_post() for limited HTML. WordPress’s rule is to escape as late as possible, ideally on the line that prints the value, so a reviewer can see at a glance that it is safe. Unescaped output is how cross-site scripting (XSS) gets in, and AI examples often skip escaping to stay short.

7. Check function names

AI likes generic names such as custom_checkout_fields(). If another plugin already declared that function, PHP stops with a fatal error. This is the PHP 8.5 wording:

Fatal error: Cannot redeclare function custom_checkout_fields() (previously declared in ...)

The Plugin Handbook asks you to prefix everything global (functions, classes, global variables, options and transients) with at least four letters, five recommended, that aren’t a common English word: myshop_fix_checkout_phone_field(), not a two-letter prefix. Watch for AI wrapping every function in if (!function_exists(...)). The handbook calls that a fatal flaw: if a function with the same name loads first, yours is never defined and your code breaks.

8. Check plugin dependencies

AI code may call plugin functions that only exist when the plugin is active:

WC()->cart->get_cart();

Guard it:

if (!function_exists('WC') || !WC()->cart) {
    return;
}

Both halves matter. WooCommerce may be inactive, and WooCommerce only loads the cart for front-end and AJAX requests, from init onward, so in wp-admin screens and cron jobs WC()->cart is null. Conditionals such as is_product() don’t exist without WooCommerce either, so check function_exists('is_product') first. For a block of WooCommerce-only code, WooCommerce’s developer docs suggest the woocommerce_init hook, which only fires when WooCommerce is active.

9. Check AJAX and REST API safety

AJAX and REST requests expect clean JSON, so code that prints HTML, warnings or debug text during them breaks the feature. Avoid this:

add_action('init', function() {
    echo 'Debugging...';
});

Never leave var_dump(), print_r() or die() in live code. Log instead:

error_log(print_r($data, true));

Useful guards, when code shouldn’t run during these requests:

if (wp_doing_ajax()) {
    return;
}

if (defined('REST_REQUEST') && REST_REQUEST) {
    return;
}

Know what they cover. wp_doing_ajax() is true for admin-ajax.php requests and, from init onward, for the ?wc-ajax= requests the classic checkout uses. The REST check only works from the parse_request action onward, where WordPress defines REST_REQUEST; on init it is always false. wp_is_serving_rest_request() (WordPress 6.5) has the same timing rule. It matters for the Checkout block, which uses the Store API under /wp-json/wc/store/.

10. Check redirect logic

A redirect can be dangerous even on the right hook. This one has no conditions:

add_action('template_redirect', function() {
    wp_redirect(home_url('/login/'));
    exit;
});

template_redirect only fires when a front-end page is about to load, so wp-admin and REST API calls never reach it. Every front-end URL does: cart, checkout, the order-received page a guest lands on after paying, and /login/ itself, which now loops. A safer version:

add_action('template_redirect', function() {
    if (is_user_logged_in() || is_page('login')) {
        return;
    }

    // On a store, let guests reach cart, checkout and their order receipt.
    if (function_exists('is_checkout') && (is_cart() || is_checkout() || is_wc_endpoint_url('order-received'))) {
        return;
    }

    wp_safe_redirect(home_url('/login/'));
    exit;
});

wp_safe_redirect() only redirects to allowed hosts, by default your own (anything else falls back to wp-admin), and neither redirect function stops the script, so exit must follow. Check that every exclusion the site needs is there and that nothing can loop.

11. Check .htaccess rules carefully

On Apache, .htaccess rules decide how a request is served before WordPress handles it, so one AI-generated line can reach every request:

RewriteRule ^$ /home.html [L]

That exact rule is what we traced in a checkout case where the rewrite intercepted WooCommerce AJAX. Meant for a static homepage, it also matched the classic checkout’s requests to the site root (/?wc-ajax=update_order_review), so checkout got HTML instead of JSON. Test any new rule against:

/wp-admin/
/wp-login.php
/wp-json/
/cart/
/checkout/
/my-account/
/?wc-ajax=

Back up the file, change one thing at a time, clear caches and check for loops. Our post on how AI-generated .htaccess rules can break WordPress covers the common mistakes one by one.

12. Check CSS scope

AI-generated CSS can reach too far:

button {
    display: none;
}

That hides every button on the site, checkout included. Scope rules to the section they are meant to change:

.woocommerce-checkout .form-row input {
    width: 100%;
}

Look for selectors that are too broad, hide important elements or cause horizontal scrolling on mobile.

13. Check JavaScript dependencies

AI JavaScript may assume elements exist. Bad:

document.querySelector('.menu-button').addEventListener('click', function() {
    document.querySelector('.menu').classList.toggle('open');
});

querySelector() returns null when nothing matches, so on any page without .menu-button this throws an error and the rest of the script stops. Safer:

const menuButton = document.querySelector('.menu-button');
const menu = document.querySelector('.menu');

if (menuButton && menu) {
    menuButton.addEventListener('click', function() {
        menu.classList.toggle('open');
    });
}

Load scripts with wp_enqueue_script() on the wp_enqueue_scripts hook, with jQuery or any other library in the dependency list, instead of hardcoding a script tag into a template.

14. Check WooCommerce-specific impact

Check whether the code touches the cart session, checkout fields, payment or shipping methods, totals, coupons, order creation, the thank-you page or emails. Then check which checkout the store runs, because AI usually writes for the classic one. Stores set up since WooCommerce 8.3 (November 2023) get the Cart and Checkout blocks by default, while the filter AI reaches for first is documented for the shortcode checkout:

add_filter('woocommerce_checkout_fields', function($fields) {
    return $fields;
});

On the Checkout block, hiding the Company, Address line 2 or Phone field, or making it optional or required, is a block editor setting, and new fields go through the Additional Checkout Fields API. WooCommerce also says hook-based checkout customisations usually need extra work to support the blocks, so test a classic-checkout snippet on the checkout your customers actually see.

Order data has a similar trap. AI often saves order details with update_post_meta(), but since WooCommerce 8.2 new stores keep orders in their own tables (High-Performance Order Storage), and WooCommerce warns that writing to the old post tables may update an order that is never read. Use $order->update_meta_data() and $order->save(); our HPOS compatibility guide covers the switch itself. After any WooCommerce change, test the whole path:

Product → Cart → Checkout → Payment → Order Received → Email

15. Check performance impact

Code can work and still slow the site: queries or API calls on every page, assets loaded everywhere, heavy loops, no caching. A bad pattern:

add_action('wp_footer', function() {
    $orders = wc_get_orders(array('limit' => -1));
    // output something
});

In wc_get_orders() a limit of -1 means no limit: every order in the store, on every page view. WooCommerce’s advice is to limit and paginate order queries and cache the results where that makes sense. Ask whether the code needs to run on every page at all.

16. Check security and permissions

Code that changes settings, saves data, processes forms or performs admin actions needs two separate checks: a capability check (for settings, usually current_user_can('manage_options')) and a nonce. WordPress is explicit that a nonce confirms intent and helps against cross-site request forgery, but must never be relied on as a permission check. Bad:

if (isset($_POST['save_settings'])) {
    update_option('my_setting', $_POST['my_setting']);
}

Better, hooked so it only runs on admin requests, with wp_nonce_field('myshop_save_settings') printed in the form:

add_action('admin_init', function() {
    if (!isset($_POST['myshop_save_settings'])) {
        return;
    }

    check_admin_referer('myshop_save_settings');

    if (!current_user_can('manage_options')) {
        return;
    }

    $value = isset($_POST['myshop_setting']) ? sanitize_text_field(wp_unslash($_POST['myshop_setting'])) : '';
    update_option('myshop_setting', $value);
});

If the code builds its own SQL, every variable goes through $wpdb->prepare(), and where a WordPress function already does the job, use the function. AI examples skip these details because they are written to be short.

17. Check translation readiness

Hardcoded text causes problems on multilingual and non-English sites. Wrap it in a translation function with your plugin’s text domain, which WordPress requires to match the plugin’s slug, and tell translators what each placeholder is:

printf(
    /* translators: %s: order number */
    esc_html__('Order number: %s', 'my-plugin'),
    esc_html($order_number)
);

18. Check error handling

Good code fails safely when data is missing, an API fails, a plugin is inactive, the cart is empty or an ID is invalid. Bad:

$order = wc_get_order($order_id);
echo $order->get_billing_email();

wc_get_order() returns false when there is no such order, and the next line stops the page with “Call to a member function get_billing_email() on false”. Better:

$order = wc_get_order($order_id);

if (!$order) {
    return;
}

echo esc_html($order->get_billing_email());

19. Test on staging first

A staging copy lets you catch PHP errors, layout changes, checkout problems, plugin conflicts, redirects and cache behaviour without risking real orders. WordPress’s own debugging guide opens with the same advice: use a staging site or take a backup before you change anything. Then deploy at a quiet time, clear caches and retest the critical flows.

20. Keep a rollback plan

Know how to undo the change before you make it: a full backup, a Git commit, a copy of the old file, a disabled snippet or a saved copy of .htaccess. For anything bigger than a snippet, use Git. A rollback plan turns a risky change into a controlled one.

Let tools do the first pass

Two free checks catch a lot before anyone reads a line. php -l file.php checks the syntax without running the code: a missing bracket or semicolon, but not runtime errors such as a call to a function that doesn’t exist. Then run PHP_CodeSniffer with the WordPress Coding Standards (vendor/bin/phpcs --standard=WordPress file.php). Its sniffs flag unescaped output, form processing without nonce checks, unsanitized input, redirects that should use wp_safe_redirect() and globals without a prefix.

Quick AI WordPress code review checklist

Before adding AI code, confirm:

  • I understand what the code does.
  • I know where the code should go.
  • It uses the correct WordPress hook.
  • It runs only where needed.
  • Variables are checked before use.
  • User input is validated or sanitized.
  • Output is escaped.
  • Function names are uniquely prefixed.
  • Plugin dependencies are checked.
  • It does not output during AJAX or REST requests.
  • Redirects have safe conditions.
  • .htaccess changes are backed up and tested.
  • CSS selectors are scoped.
  • JavaScript checks elements before using them.
  • It works with the checkout the store really uses.
  • Performance impact is acceptable.
  • Capabilities and nonces are checked where needed.
  • Text is translation-ready where relevant.
  • Errors are handled safely.
  • It passed php -l and PHPCS, was tested on staging, and there is a rollback plan.

If even one important item is unclear, pause and review the code before going live.

What to do if AI code already broke your site

  1. Remove or disable the last change, and restore the previous file if needed.
  2. Check the PHP error log.
  3. Clear caches and test the broken flow again.
  4. Review the code properly before re-adding it, instead of stacking another AI snippet on top.

The usual suspects are functions.php, a snippets plugin, custom plugin files, .htaccess, theme templates and the theme’s JS and CSS. Locked out of wp-admin by a fatal error? Since WordPress 5.2, the site admin gets an email with a recovery-mode link that lets you log in with the broken plugin or theme paused. Otherwise, remove the code through your host’s file manager or SFTP.

Final thoughts

AI-generated code deserves the same review as any other code, and the faster it arrives, the more the review matters.


Has AI-written code already broken something? Our WordPress fix service starts from €199, the diagnosis is free, and it is no fix, no fee. For fatal errors and white screens, see PHP bug fixing. If the code hasn’t gone live yet, send it to us first: we check it against your real setup and agree a fixed price in writing before any work starts.

Sources

Last checked 12 November 2026.

Chat on WhatsApp