How to implement a staged rollout for your WordPress plugin

This post is for authors of plugins that are on the official WordPress repository.

Imagine the new major release of your WordPress plugin has a serious bug that was not found during the testing.

We are humans, so it may happen.

If your plugin is installed on thousands of installations, the pressure on your support system will become higher, and it may risk collapse if the support team is not well dimensioned.

In many cases, the support team is only you.

Here I will show you how I try to prevent this kind of situation for Freesoul Deacttivate Plugins.

FDP is still young but is growing, so better to think in advance about all the things we can do to prevent too high pressure on the support system. And if your plugin is in a similar situation, or with a higher number of active installations, I suggest you think about it.

The method that I’m going to show you is based on a few lines of code, so, without spending more time or physical resources. After you implement the code, the help will be for free.

The idea is to provide an automatic staged rollout based on the risk of failures after the update.

The idea is not mine. Many software companies already do that for popular software.

 

Here you have the code.

define( 'EOS_DP_PLUGIN_BASE_NAME', untrailingslashit( plugin_basename( __FILE__ ) ) );

//Get plugin information
function my_get_plugin_info( $slug ){
    $response = wp_remote_get( 'https://api.wordpress.org/plugins/info/1.0/freesoul-deactivate-plugins.json' );
    if( !is_wp_error( $response ) ){
        $json = wp_remote_retrieve_body( $response );
        $info = json_decode( $json );
        $return = array();
        if( is_object( $info ) ){
            $return['threads'] = $info->support_threads;
            $return['solved_threads'] = $info->support_threads_resolved;
            $return['last_updated'] = $info->last_updated;
            $return['versions'] = $info->versions;
            return $return;
        }
    }
    return false;
}

add_filter( 'site_transient_update_plugins','my_manage_staged_rollout' );
//Staged rollout for major releases depending on the risk of having issues
function my_manage_staged_rollout( $value ){
    if( defined( 'FDP_STAGED_ROLLOUT' ) && false === FDP_STAGED_ROLLOUT ) return $value;
    if( isset( $value ) && is_object( $value ) ){
        if( isset( $value->response[EOS_DP_PLUGIN_BASE_NAME] ) ){
            $plugin_data = $value->response[EOS_DP_PLUGIN_BASE_NAME];
            $new_version = $plugin_data->new_version;
            if( substr_count( $new_version,'.' ) < substr_count( EOS_DP_VERSION,'.' ) ){
                //It's a major release, let's notify the update first to installations with less risks of having issues
                $info = eos_dp_get_plugin_info( 'freesoul-deactivate-plugins' );
                if( $info ){
                    if( isset( $info['last_updated'] ) ){
                        $last_checked = $value->last_checked;
                        $active_plugins = array_unique( get_option( 'active_plugins',array() ) );
                        $need_update = count( $value->response );
                        $risk = 0;
                        if( $active_plugins ) $risk = $risk + absint( ( count( $active_plugins ) - 20 ) );
                        if( $need_update ) $risk = $risk + absint( $need_update );
                        $unsolved_threads = absint( max( 0,$info['threads'] - $info['solved_threads'] - 10 ) );
                        $risk = $risk + 10 * $unsolved_threads;
                        $risk = min( 100,$risk )/10; //after 10 days since the publication of the major release, everybody will see the upgrade notice in any case
                        $since_last_update = time() - strtotime( $info['last_updated'] );
                        if( $risk > $since_last_update/( 60 * 60 * 24 ) || $since_last_update < 60 * 60 ){
                            //Update notice not showed if the risk is higher than the number of days since last major release, or if the publication of the major release was less than 1 hour ago
                            unset( $value->response[EOS_DP_PLUGIN_BASE_NAME] );
                        }
                    }
                }
            }
        }
    }
    return $value;
}

If you have read until here, you are a plugin author, so the code above doesn’t need many explanations.

EOS_DP_PLUGIN_BASE_NAME will be something that looks like your-plugin-folder-name/your-plugin-main-file-name.php.

The code above works if you write it in the main file of your plugin. In the real world, you will define the plugin basename constant in the main file and the two functions will be included in a PHP file that you will have somewhere in the folders of your plugin, probably inside a PHP class. This is up to you.

The function my_get_plugin_info returns an array of information about the plugin. Of course, replace freesoul-deactivate-plugins with the slug of your plugin.

We hook the filter site_transient_update_plugins with the function my_manage_staged_rollout.

Inside our function my_manage_staged_rollout we first ask for the last updated data about our plugin.

If the new official version detected by WordPress is related to a major release we go ahead asking for information that will help us to calculate the risk of failures after the update.

Freesoul Deactivate Plugins is usually installed with dozen of other plugins. In this case, I prefer to give important weight to the number of installed plugins.

The risk of failures is higher for installations where many plugins are installed. So users who have many plugins will see the updating notification later than users who have installations with fewer plugins.

During the first hours and days, users with few plugins will test the new major release in a low-risk environment and the support system will risk less pressure in the beginning.

But the same, if a serious bug was not detected during the testing, someone will knock at your support system, but hopefully not dozen of users, as it may happen when everybody receives the updating notification.

Moreover, the risk takes also into account the number of open threads on the support forum. If there are many unresolved threads, the risk will be higher, and fewer users will see the updating notification.

Inside the function, we give a limit of 10 days, beyond which everybody will see the updating notification, and we ensure nobody sees it during the first hour after the publication. This is to have the time to perform the very last tests on a version that we already consider stable and still have the possibility to switch to the previous version if something that was really unexpected jumps out.

Of course, you are free to modify the risk calculation taking into account your specific case.

 

 

If you want everybody to see the updating notification, just don’t assign a version number that contains fewer dots than the actual official version.

If e.g. the actual official version is 1.8.8.6, if you publish 1.8.8.7 everybody will be notified, but if you publish 1.8.9 the staged rollout will take place.

I hope it helps to prevent peaks of requests on your support system after major releases.