Handling Plugin Updates: Database Schema and Migration Strategies
Learn to manage plugin updates effectively by tracking version numbers, implementing dbDelta for schema changes, and running one-time migration scripts.
Previously in this course, we built a robust Model Layer for Data to abstract our database interactions. Now that our plugin is growing, we need a reliable way to evolve our database structure as we add features.
Software is never finished. When you release a new version of your plugin, you often need to change the database schema or reorganize existing data. Unlike Laravel migrations which are highly structured, WordPress requires us to manually manage the plugin version and trigger the necessary updates.
Tracking Plugin Versioning
The first step in managing updates is knowing which version of your plugin is currently installed. WordPress stores the plugin version in the header of your main plugin file, but that doesn't help your code know if it needs to run an update.
We store the "installed version" in the wp_options table. This allows us to compare the version in our code against the version saved in the database.
PHP#6A9955">// Inside your main plugin class or activation/upgrade handler public function check_for_updates() { $installed_version = get_option('my_kb_plugin_version', '0.0.0'); $current_version = '1.1.0'; if (version_compare($installed_version, $current_version, '<')) { $this->run_upgrades($installed_version); update_option('my_kb_plugin_version', $current_version); } }
Implementing Schema Updates with dbDelta
When you need to add a column or create a new table, you shouldn't run raw CREATE TABLE queries directly. Instead, use dbDelta(). This function parses your SQL and compares it against the existing database schema, only applying the necessary changes.
It is strict about formatting:
- You must put each field on its own line.
- You must include the
PRIMARY KEYdefinition. - You must use two spaces for keywords like
BIGINTorNOT NULL.
PHPfunction my_kb_update_schema() { global $wpdb; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); $table_name = $wpdb->prefix . 'kb_articles'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id mediumint(9) NOT NULL AUTO_INCREMENT, title varchar(255) NOT NULL, content text NOT NULL, version_added varchar(10) DEFAULT '1.0.0', PRIMARY KEY(id) ) $charset_collate;"; dbDelta($sql); }
Running One-Time Upgrade Scripts
Sometimes a schema change isn't enough; you might need to move data from a legacy format to a new structure. We handle this by checking the installed version and executing specific migration methods.
PHPprivate function run_upgrades($installed_version) { #6A9955">// If the user is upgrading from a version older than 1.1.0 if (version_compare($installed_version, '1.1.0', '<')) { #6A9955">// Run migration logic: e.g., move data from post_meta to custom table global $wpdb; $wpdb->query("UPDATE {$wpdb->prefix}kb_articles SET version_added = '1.1.0'"); } }
Hands-on Exercise
- Locate your main plugin class.
- Add a
check_for_updates()method that compares your plugin's current version constant against an option inwp_options. - Hook this method into the
admin_initaction so it runs when an administrator visits the dashboard. - Create a
run_upgrades()method that adds a new column to your Knowledge Base table usingdbDelta.
Common Pitfalls
- Forgetting
dbDeltaformatting: If you don't use the exact spacing requirements (like two spaces beforePRIMARY KEY),dbDeltawill fail silently or recreate tables unnecessarily. - Running migrations on every request: Always use
version_compareto ensure your migration logic only runs once during the upgrade process. - Not using
require_once:dbDeltais defined inwp-admin/includes/upgrade.php, which is not loaded by default on the frontend. You must include it manually.
Managing updates is a foundational skill for any professional WordPress plugin architecture. By versioning your database changes, you ensure that your users have a seamless transition between plugin releases without losing their data.
Up next: Internationalization (i18n) — preparing your plugin for global audiences.
Work with me

Custom WordPress Plugin Development
Custom WordPress & WooCommerce plugins built to standard — by the developer behind a plugin with 5,000+ active installs and a SaaS with 10,000+ users.

FilamentPHP Admin Panel & Dashboard Development
A powerful admin panel for your Laravel app — built with FilamentPHP so you can manage everything without touching the database.