AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Wordpress Performance

skill-iwritec0de-wp-dev-wordpress-performance · by iwritec0de

>-

No reviews yet
0 installs
23 views
0.0% view→install

Install

$ agentstack add skill-iwritec0de-wp-dev-wordpress-performance

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-iwritec0de-wp-dev-wordpress-performance)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
5mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Wordpress Performance? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

WordPress Performance

Performance optimization patterns for WordPress plugins, themes, and live sites.

Critical Rules

  1. Never query without limitsposts_per_page => -1 and unbounded $wpdb queries are the most common cause of memory exhaustion on production sites.
  2. Cache expensive queries — any query that runs on every page load and doesn't change per-request must be cached with transients or the object cache.
  3. Use no_found_rows — set 'no_found_rows' => true on any WP_Query that doesn't need pagination; this skips SQL_CALC_FOUND_ROWS which is expensive on large tables.
  4. Avoid meta queries on unindexed keysmeta_query on wp_postmeta performs full table scans unless you add custom indexes. Consider a custom table for heavily queried data.
  5. Defer heavy work — operations on init run on every request. Use admin_init for admin-only work, rest_api_init for REST-only, and wp_loaded or later hooks when possible.
  6. Prime caches, don't loop-query — use update_post_meta_cache(), update_post_caches(), or _prime_post_caches() to batch-load metadata instead of calling get_post_meta() in a loop.

Query Performance Patterns

Efficient WP_Query

// Good — limited, cache-friendly, no unnecessary data:
$query = new WP_Query( [
    'post_type'              => 'product',
    'posts_per_page'         => 20,
    'no_found_rows'          => true,   // Skip pagination count query.
    'update_post_meta_cache' => false,  // Skip if not reading meta.
    'update_post_term_cache' => false,  // Skip if not reading terms.
    'fields'                 => 'ids',  // Return only IDs when full objects aren't needed.
] );

// Bad — unbounded, forces full table scan:
$query = new WP_Query( [
    'post_type'      => 'product',
    'posts_per_page' => -1,  // Never do this.
] );

N+1 Query Prevention

// Bad — N+1 pattern (1 query per post in the loop):
foreach ( $posts as $post ) {
    $price = get_post_meta( $post->ID, '_price', true );  // Query per iteration.
}

// Good — prime the cache first, then loop reads from cache:
$post_ids = wp_list_pluck( $posts, 'ID' );
update_meta_cache( 'post', $post_ids );  // Single query loads all meta.

foreach ( $posts as $post ) {
    $price = get_post_meta( $post->ID, '_price', true );  // Reads from cache.
}

Batch Operations

// Bad — individual inserts in a loop:
foreach ( $items as $item ) {
    $wpdb->insert( $table, $item );  // N queries.
}

// Good — single bulk insert:
$values = [];
foreach ( $items as $item ) {
    $values[] = $wpdb->prepare( '(%d, %s, %f)', $item['user_id'], $item['status'], $item['total'] );
}
if ( $values ) {
    $wpdb->query( "INSERT INTO {$table} (user_id, status, total) VALUES " . implode( ', ', $values ) );
}

Caching Strategies

Transient Caching Pattern

function myplugin_get_featured_products(): array {
    $cache_key = 'myplugin_featured_products';
    $data      = get_transient( $cache_key );

    if ( false !== $data ) {
        return $data;
    }

    $query = new WP_Query( [
        'post_type'      => 'product',
        'posts_per_page' => 12,
        'meta_key'       => '_featured',
        'meta_value'     => 'yes',
        'no_found_rows'  => true,
        'fields'         => 'ids',
    ] );

    $data = $query->posts;
    set_transient( $cache_key, $data, HOUR_IN_SECONDS );

    return $data;
}

// Invalidate when products change:
add_action( 'save_post_product', function (): void {
    delete_transient( 'myplugin_featured_products' );
} );

Object Cache for Per-Request Deduplication

function myplugin_get_settings(): array {
    $cached = wp_cache_get( 'settings', 'myplugin' );
    if ( false !== $cached ) {
        return $cached;
    }

    $settings = get_option( 'myplugin_settings', [] );
    wp_cache_set( 'settings', $settings, 'myplugin' );

    return $settings;
}

When to Use Each Layer

| Scenario | Strategy | |----------|----------| | Same data fetched multiple times per request | wp_cache_* (object cache) | | Expensive query, result valid for minutes/hours | set_transient() | | External API response | set_transient() with timeout matching API rate limits | | Data that changes on save | Transient + delete_transient() on save_post | | User-specific data | Transient with user ID in key, or get_user_meta() | | Full-page output | Consider wp_cache_* with a persistent backend (Redis/Memcached) |

Hook Performance

Defer Initialization

// Bad — runs on every request including REST, AJAX, cron:
add_action( 'init', 'myplugin_heavy_init' );

// Good — scope to context:
add_action( 'admin_init', 'myplugin_admin_setup' );      // Admin only.
add_action( 'rest_api_init', 'myplugin_register_routes' ); // REST only.
add_action( 'template_redirect', 'myplugin_frontend' );    // Frontend only.

// Good — conditional loading:
add_action( 'init', function (): void {
    if ( ! is_admin() && ! wp_doing_ajax() && ! wp_doing_cron() ) {
        // Frontend-only logic.
    }
} );

Lazy Loading

// Bad — always loads all classes:
require_once __DIR__ . '/includes/class-admin.php';
require_once __DIR__ . '/includes/class-reports.php';
require_once __DIR__ . '/includes/class-import.php';

// Good — load only when needed:
add_action( 'admin_menu', function (): void {
    require_once __DIR__ . '/includes/class-admin.php';
    new Myplugin_Admin();
} );

// Best — PSR-4 autoloader via Composer:
require_once __DIR__ . '/vendor/autoload.php';

Autoload Optimization

Options with autoload = yes are loaded into memory on every page load via a single SELECT from wp_options. Large or rarely-used options bloat this query.

// Bad — large data autoloaded:
add_option( 'myplugin_log_history', $huge_array );  // Defaults to autoload = yes.

// Good — disable autoload for large or infrequent data:
add_option( 'myplugin_log_history', $huge_array, '', false );

// Or with update_option (autoload param is 4th arg since WP 4.2):
update_option( 'myplugin_log_history', $huge_array, false );

Rule of thumb: autoload only small, frequently-accessed options (settings, feature flags). Disable autoload for logs, large serialized arrays, and data accessed only on specific pages.

Database Indexing

// Add indexes to custom tables on activation:
register_activation_hook( __FILE__, function (): void {
    global $wpdb;
    $table = $wpdb->prefix . 'myplugin_orders';

    // Check before adding to make activation idempotent.
    $index_exists = $wpdb->get_var( $wpdb->prepare(
        'SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND INDEX_NAME = %s',
        DB_NAME, $table, 'status_created_idx'
    ) );

    if ( ! $index_exists ) {
        $wpdb->query( "ALTER TABLE {$table} ADD INDEX status_created_idx (status, created_at)" );
    }
} );

Indexing Post Meta for Queries

If you frequently filter by a specific meta key, consider adding an index:

-- Only add this if meta_query on this key is a known bottleneck:
ALTER TABLE wp_postmeta ADD INDEX meta_key_value_idx (meta_key(50), meta_value(50));

Warning: modifying core tables is risky and may conflict with updates. Prefer custom tables for heavily queried structured data.

Anti-Patterns

| Anti-Pattern | Impact | Fix | |---|---|---| | posts_per_page => -1 | Loads unlimited posts into memory | Set a reasonable limit or paginate | | get_post_meta() in a loop | N+1 queries | update_meta_cache() before loop | | Heavy logic on init | Runs on every request | Use context-specific hooks | | get_option() on every function call | Redundant queries (without persistent cache) | Cache in a static variable or wp_cache_* | | Large serialized arrays in autoloaded options | Bloats the autoload query | Set autoload to false | | $wpdb->query() inside foreach | N inserts instead of 1 | Batch insert with concatenated VALUES | | LIKE '%search%' on large tables | Cannot use index (leading wildcard) | Full-text search index or prefix-only LIKE | | Missing no_found_rows | Extra COUNT query when pagination isn't needed | Set true on non-paginated queries | | wp_remote_get() on every page load | Blocks rendering on external service | Cache response with transient | | Autoloading large options | Loaded on every request even when unused | add_option() with autoload false |

For query analysis and EXPLAIN usage, see reference/query-optimization.md. For caching architecture and invalidation strategies, see reference/caching-strategies.md.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.