Solving the multi-armed bandit problem in WordPress

The default optimization advice for WordPress sites is to set up a static A/B test, split the traffic evenly, and wait. The waiting is the expensive part. Every week you spend chasing a p-value, half your visitors keep seeing the variation you already suspect is losing. Thompson sampling moves traffic while the data arrives instead of after the test closes.

Most WordPress optimization treats the decision as binary: serve variation A or serve variation B, at a fixed ratio, until somebody calls it. I have watched clients lose thousands in checkout revenue that way, because a weak variation held its 50% of traffic for a full month after the numbers were clear. Thompson sampling handles the same problem through the exploration and exploitation tradeoff.

The multi-armed bandit bottleneck

Say you have three candidate headlines for a WooCommerce product page. A traditional test gives each one a third of the traffic and keeps doing that to the end. The multi-armed bandit framing starts somewhere else: every impression spent on a losing headline is money you do not get back. What you want is an algorithm that reads each click, and each non-click, then adjusts how often it shows each headline while traffic is still coming in.

Fixed randomization throws away the data it just collected. You keep exploring options you have already watched fail, and you under-serve the one that converts. If your tests feel like guesswork today, my write-up on how to fix your A/B testing reliability is the place to start.

How Thompson sampling uses Bayesian logic

The algorithm runs on the Beta distribution, which tracks two counters per option: successes as alpha, failures as beta. Every headline starts level, and both counters climb as visitors interact with it. The part that matters is that Thompson sampling does not simply serve whichever option is ahead on average. It samples from the probability distribution behind each option.

So when headline A sits at a 20% conversion rate off 10 views and headline B is at 18% off 1,000, headline A still gets shown. The uncertainty is doing useful work there: an option almost nobody has seen yet deserves more traffic than its raw average would suggest, and the distribution encodes that automatically.

Implementing the logic in WordPress

This normally gets written in Python, but the core of Thompson sampling fits fine in PHP and WordPress transients. PHP has no native random_beta, so you either approximate the draw or pull in a small stats library. What you want to get right first is where the state of each bandit lives.

<?php
/**
 * Naive state storage for a Multi-Armed Bandit headline test.
 * We use WordPress options to persist Alpha (successes) and Beta (failures).
 */
function bbioon_update_headline_stats( $headline_id, $is_success ) {
    $stats = get_option( 'bbioon_bandit_headlines', [] );
    
    if ( ! isset( $stats[$headline_id] ) ) {
        $stats[$headline_id] = [ 'alpha' => 1, 'beta' => 1 ];
    }
    
    if ( $is_success ) {
        $stats[$headline_id]['alpha']++;
    } else {
        $stats[$headline_id]['beta']++;
    }
    
    update_option( 'bbioon_bandit_headlines', $stats );
}
?>

Do not call update_option on every impression in production, or you will spend the afternoon staring at database locks. Batch the writes, or keep the counters in an object cache like Redis where concurrent updates are not stepping on each other. The official analysis of Thompson sampling has the regret bounds if you want the math behind it.

Why you should ship it

The gap tends to be wide. Across simulations of 10,000 iterations and up, Thompson sampling can beat random assignment by 20% or more. On a WooCommerce store doing real volume, 20% of conversions is the margin a campaign lives or dies on. For the statistics side of testing, my post on the p-value meaning covers where the usual interpretations go wrong.

If bandit logic like this is eating your development hours, I can take it on. I have been wrestling with WordPress since the 4.x days.

Refining the architecture

A fixed traffic split is a poor default for a site that gets visitors every hour of the day. Keep two counters per variation, sample from them, and let the allocation move on its own. That is the whole change, and it keeps working when nobody is watching the dashboard.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.