HPOS (High-Performance Order Storage) changes how WooCommerce stores orders. Instead of relying on the generic WordPress wp_posts and wp_postmeta tables, orders are stored in a specialized WooCommerce structure. This reduces the number of complex database queries and creates a stronger foundation for stores with large catalogs and extensive order histories.
At the same time, the migration can cause errors if a plugin, theme, or custom code treats orders as WordPress posts with the post_type shop_order. Below is a practical plan for migration, compatibility testing, and identifying problems before they affect customers.
What Is HPOS and Why Does It Affect Integrations?
In the classic WooCommerce storage mode, orders are represented as shop_order posts, while their fields and metadata are stored primarily in wp_posts and wp_postmeta. HPOS moves the main order data into specialized WooCommerce tables. This does not change the store’s standard interface for working with orders, but it does change the internal data model.
Problems arise when code does not use the WooCommerce API and instead reads WordPress tables directly. For example, a WP_Query using post_type => shop_order may stop returning the expected results after the full migration to HPOS. SQL queries against wp_postmeta, manual status changes, and assumptions that an order ID can always be handled as a regular post are also risky.
Compatible extensions use WooCommerce CRUD classes, including WC_Order, and do not depend on a specific storage method. Therefore, the main task before migration is to find direct references to the old structure and verify third-party integrations.
- Potentially risky components include payment gateways, CRMs, ERPs, shipping services, inventory systems, accounting modules, and custom importers.
- Pay particular attention to code that synchronizes orders on a schedule or responds to status changes.
- HPOS does not replace backups and testing: it is a change to the storage structure, not merely a performance switch.
Preparation: Backups, Staging, and Dependency Inventory
Do not enable HPOS without a current backup of the database and site files. The backup should not only be created but also tested: make sure it can be restored in a separate environment. For a store, it is also important to preserve payment-service configuration, webhook keys, and integration settings.
First, repeat the planned migration on a staging copy of the store. Staging should contain realistic data: orders with different statuses, products with variations, refunds, coupons, taxes, guest purchases, and orders with custom fields.
Create a list of every component that may read or modify orders. Review the documentation and changelog for each plugin, and confirm compatibility with the developer for critical integrations. Update WooCommerce, the theme, and extensions in the test environment, but do not perform a mass production update without a separate rollback plan.
- Record the current number of orders, their statuses, and the number of orders created over the last few days.
- Check cron queues, scheduled actions, and background tasks that create or transmit orders.
- Prepare a maintenance window with minimal traffic if synchronization or validation may take a long time.
- Decide in advance who will verify payments, shipping, the CRM, inventory, and refunds after the switch.
How to Find Incompatible Code Before Migration
Start by searching your custom code, mu-plugins, child theme, and custom extensions. Do not search only for shop_order; also look for signs that orders are being handled through the WordPress API or SQL.
The presence of such a code fragment does not always mean it will fail: some compatible scenarios may work through a compatibility layer. However, every result should be reviewed, especially if the code runs during checkout or modifies order data.
- WP_Query or get_posts using post_type => shop_order.
- Direct SQL queries against wp_posts or wp_postmeta, or searches for order metadata keys through $wpdb.
- Calls to get_post_meta(), update_post_meta(), and delete_post_meta() for order fields.
- Checks such as get_post_type($order_id) or treating an order as a WP_Post.
- Custom reports, exporters, and importers that select orders by date, status, or customer through WordPress tables.
/* Ризиковий підхід */
$orders = get_posts([
'post_type' => 'shop_order',
'post_status' => 'wc-processing',
]);
/* Підхід через API WooCommerce */
$orders = wc_get_orders([
'status' => 'processing',
'limit' => 50,
]);
What to Use Instead of Direct Access to Orders and Metadata
Use wc_get_order() to retrieve a single order. It returns a WC_Order object and hides the specific table structure from your code. For order lists, use wc_get_orders() or WC_Order_Query. These methods are the recommended WooCommerce access layer and are better able to withstand changes to internal storage.
Read order fields through getters and modify them through setters or the object’s metadata methods. After making changes, call save() so WooCommerce writes the data correctly. Do not combine this approach with manual updates to wp_postmeta, as this can create inconsistencies between data sources.
For complex scenarios, build filtering around the parameters supported by the WooCommerce API. If you need a non-standard criterion, first check the documentation and capabilities of the current WooCommerce version instead of adding an SQL query against the old tables.
- Single order: $order = wc_get_order($order_id).
- Order list: wc_get_orders() with status, customer_id, limit, date, and other supported arguments.
- Customer and payment data: use get_billing_email(), get_payment_method(), and other WC_Order methods.
- Metadata: $order->get_meta('key'), $order->update_meta_data('key', $value), followed by $order->save().
- Status: use order-object methods rather than manually changing post_status in the database.
$order = wc_get_order($order_id);
if ($order) {
$email = $order->get_billing_email();
$source = $order->get_meta('_order_source');
$order->update_meta_data('_exported_to_crm', 'yes');
$order->save();
}
$orders = wc_get_orders([
'status' => ['processing', 'completed'],
'limit' => 100,
'return' => 'objects',
]);
Enable HPOS in Compatibility Mode
Perform the initial switch on staging. In WooCommerce settings, select the order storage mode that uses HPOS and leave compatibility mode enabled if it is available in your version. Its purpose is to maintain synchronization between the new tables and the legacy storage while you test the migration.
Do not consider the migration complete immediately after activation. Existing orders must be synchronized, and the process may run through background tasks. Monitor the synchronization status, errors in the WooCommerce logs, and scheduled-action queues. Do not delete the old data or disable compatibility mode until you have confirmed that all critical processes work correctly.
After synchronization is complete, compare the number of orders, statuses, totals, taxes, addresses, coupons, and metadata used by integrations. If you find discrepancies, stop the next stage of the migration and investigate the cause on staging.
- Verify that new test orders can be created and appear in the admin area.
- Check that older orders open correctly and that filters, search, and bulk actions work.
- Do not run the migration and a large-scale plugin update at the same time unless necessary.
- Save error logs and validation results as the basis for the production migration plan.
What to Test After the Switch
Testing should imitate real business processes rather than being limited to opening the settings page. Create test orders in different ways and trace the entire data path: from checkout to payment, from transmission to the CRM or inventory system, and through the final status change.
Test older orders separately. An integration may process new records correctly but fail when selecting historical data, refunds, or records for re-synchronization.
- Checkout for registered and guest customers.
- Successful payment, declined payment, retry attempts, and payment-webhook processing.
- Status changes, email notifications, stock reduction, and transmission of the order to fulfillment.
- Refunds, full and partial refunds, cancellations, and manual order edits.
- CRM, ERP, shipping, analytics, exports, and all integrations triggered by cron or webhooks.
- Admin search and filtering, reports, and custom manager pages.
- Orders with variations, coupons, taxes, custom fields, and digital products.
Common Mistakes and a Safe Rollback Plan
The most common mistake is enabling HPOS directly on the live site without checking code that has not been updated for a long time. Another frequent problem is testing only checkout while ignoring webhooks, refunds, and background synchronization. A further mistake is manually changing database records without understanding which storage is authoritative.
If errors appear on staging or in production, record the time, the specific order, the user action, the log entry, and the names of active extensions. If possible, return to the previous mode through the official WooCommerce settings without deleting the HPOS tables. Restoring a backup may be necessary if data has already been modified incorrectly by incompatible code.
After rollback, test payment processing and order creation again. Do not consider the issue resolved until you have updated or replaced the incompatible extension and repeated the migration in a test environment.
- Do not interrupt synchronization in the middle of an active process without understanding the consequences.
- Do not edit HPOS or wp_postmeta tables directly as a quick fix for a single order.
- Do not clear logs before diagnosis is complete.
- Plan another validation round after updating payment and inventory integrations.
Frequently asked questions
Do I need to enable HPOS for every store?
No. HPOS is the recommended direction for WooCommerce development, but the decision depends on the compatibility of the theme, plugins, and custom code. For a store with critical integrations, test the migration on staging first.
Will existing orders disappear after HPOS is activated?
With a correct migration, they should remain available. WooCommerce synchronizes data from the legacy storage to the HPOS structure, but you must create a backup before migrating and wait for the process to finish.
Can I continue using get_post_meta() for orders?
For HPOS-compatible code, it is better to use WC_Order methods: get_meta(), update_meta_data(), and save(). Direct access to the WordPress metadata table may not correspond to the current order storage.
How can I tell whether a plugin is incompatible with HPOS?
Check the developer’s compatibility statement, review the logs after activation, and test the plugin on staging. Errors during order creation, status-based queries, webhook delivery, refunds, or CRM synchronization are particularly suspicious.
Is testing only new orders enough?
No. Test both new and existing orders, as well as non-standard scenarios such as partial refunds, guest purchases, coupons, taxes, repeated webhooks, and background tasks.
Conclusion
A safe migration to HPOS is a staged process, not a one-time switch. A backup, staging environment, code audit, compatibility mode, and testing of all integrations reduce the risk of data loss and sales disruptions.
Replace direct queries to shop_order and wp_postmeta with the WooCommerce API, wait for synchronization to finish, and move to full HPOS use in production only after successful testing.