Plugin Lifecycle Management: Migrations and Secure Uninstallation
Master WordPress Plugin Lifecycle management. Learn to handle version-specific database migrations and implement professional, non-destructive uninstall routines.
Previously in this course, we covered Versioning and Release Management: Professional WordPress Plugin Standards. While versioning handles the "what" and "when" of your updates, this lesson focuses on the "how"—specifically, managing the state of your plugin’s data as it evolves over time.
Professional plugin engineering requires more than just adding features; it demands a defensive approach to how your code interacts with the WordPress database during installation, updates, and removal.
The Plugin Lifecycle Architecture
A robust plugin lifecycle is governed by three primary events: activation, updates, and uninstallation. In a production-grade plugin, these should never be handled by scattered logic in your main plugin file. Instead, we treat the lifecycle as a series of state transitions.
1. Version-Specific Database Migrations
As discussed in Integration Testing for WordPress: Database and API Workflows, database changes are the most common source of production outages. You should never rely on simple dbDelta calls without tracking the current version of your schema.
We implement a MigrationManager that checks the version stored in wp_options against the current plugin version constant.
PHPnamespace MyPlugin\Lifecycle; class MigrationManager { public function maybe_migrate() { $installed_version = get_option('my_plugin_version', '0.0.0'); $current_version = MY_PLUGIN_VERSION; if (version_compare($installed_version, $current_version, '<')) { $this->run_migrations($installed_version, $current_version); update_option('my_plugin_version', $current_version); } } private function run_migrations($from, $to) { #6A9955">// Logic to execute specific SQL files based on version gaps if (version_compare($from, '1.1.0', '<')) { $this->migrate_to_1_1_0(); } } }
2. The Uninstall Strategy
The uninstall.php file is a special file that WordPress executes only when a user deletes a plugin from the admin dashboard. Note that this file runs in a separate process, meaning it does not have access to your defined classes or constants.
Crucial: Always check defined('WP_UNINSTALL_PLUGIN') to prevent direct access to the file.
PHP#6A9955">// uninstall.php if (!defined('WP_UNINSTALL_PLUGIN')) { exit; } global $wpdb; #6A9955">// Only delete data if the user hasn't opted to "Keep Data" $keep_data = get_option('my_plugin_keep_data_on_uninstall', false); if (!$keep_data) { $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}my_plugin_data"); delete_option('my_plugin_settings'); }
Hands-on Exercise: Implementing a Migration Hook
Your task is to integrate a migration check into the plugin's boot process.
- Create a
LifecycleServiceProviderthat hooks intoadmin_init. - Within the service, create a method that compares the stored database version against your
MY_PLUGIN_VERSIONconstant. - If an update is required, trigger a log event (to verify it ran) and update the
my_plugin_versionoption. - Challenge: Ensure your migration logic uses
$wpdb->prepareas defined in Preventing SQL Injection in WordPress: A Deep Dive into $wpdb.
Common Pitfalls
- Assuming Classes Exist in
uninstall.php: As noted,uninstall.phpis isolated. Keep your logic procedural here. If you need to share logic, use a separate file that yourequirecarefully, ensuring no dependencies on the main plugin bootstrapper. - Destructive Uninstalls: Never delete user data by default. Always provide an option in your settings panel to "Delete data on uninstall." If the user deletes the plugin, they might intend to reinstall it later; wiping their database without consent is a major UX failure.
- Race Conditions: If your plugin is network-activated on a Multisite, lifecycle hooks behave differently. Always use
is_network_admin()checks if you are modifying global tables.
Recap
Proper lifecycle management ensures your plugin is a "good citizen" in the WordPress ecosystem. By versioning your database schema, using uninstall.php defensively, and providing users control over their data, you build trust and maintain stability. Always treat your database as the long-lived component of your software that must survive even when the plugin code itself is removed.
Up next: Performance Monitoring — adding instrumentation to track query times and system health.
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.

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.