Schema changes, post type migrations, option renames — all of these can be done without taking the site down if the pattern is “add first, migrate, then remove” rather than “change in place.”
Most WordPress operations that feel risky are actually safe if you do them in the right order. The naive approach — rename a meta key in place, update all callers in one deploy — produces reads that return empty for every record created before the rename. The safe approach is a multi-phase migration that leaves the site running correctly throughout.
The Anchor build needed a schema migration at month 4: the lead source tracking field needed to expand from a text meta value to a taxonomy. The naive approach was to rename the meta key and update all callers in one deploy. The safe approach was a 3-phase migration. The migration took 2 deploys and a background cron job — 4 days elapsed, 0 customer-facing impact.
For any rename or structural migration, the pattern is: add new alongside old → background-migrate → switch reads → remove old. Never combine the first and third steps:
function [client]_save_lead_source( int $lead_id, string $source ): void {
// Legacy field — keep writing during migration
update_post_meta( $lead_id, '[client]_lead_source', $source );
// New field — start populating
wp_set_object_terms( $lead_id, $source, '[client]_lead_source_tax' );
}
function [client]_migrate_lead_sources(): void {
$leads = get_posts( [
'post_type' => '[client]_lead',
'posts_per_page' => 100,
'meta_key' => '[client]_lead_source',
'meta_compare' => 'EXISTS',
'fields' => 'ids',
] );
foreach ( $leads as $lead_id ) {
$source = get_post_meta( $lead_id, '[client]_lead_source', true );
if ( $source && ! wp_get_object_terms( $lead_id, '[client]_lead_source_tax' ) ) {
wp_set_object_terms( $lead_id, $source, '[client]_lead_source_tax' );
}
}
}
function [client]_get_lead_source( int $lead_id ): string {
$terms = wp_get_object_terms( $lead_id, '[client]_lead_source_tax' );
if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
return $terms[0]->slug;
}
// Fallback for records not yet migrated
return get_post_meta( $lead_id, '[client]_lead_source', true ) ?: '';
}
Bulk operations on large datasets run in batches with a pause between passes to avoid locking the database under live traffic:
function [client]_backfill_in_batches( callable $process, int $batch_size = 50 ): int {
$offset = 0;
$processed = 0;
do {
$ids = [client]_get_next_batch( $offset, $batch_size );
if ( empty( $ids ) ) {
break;
}
foreach ( $ids as $id ) {
$process( $id );
$processed++;
}
$offset += $batch_size;
usleep( 100000 ); // 100ms pause between batches
} while ( count( $ids ) === $batch_size );
return $processed;
}
Changes that can’t be cleanly rolled back go behind a WP option flag. Flip the flag, watch metrics, flip it back if something breaks — no deploy required:
function [client]_feature_enabled( string $flag ): bool {
static $flags = null;
if ( $flags === null ) {
$flags = get_option( '[client]_feature_flags', [] );
}
return ! empty( $flags[ $flag ] );
}
The expand-then-contract pattern’s cost is one extra deploy cycle. The benefit is that the site continues functioning correctly throughout the migration. If the migration fails partway through phase 2, the site still runs on the old structure — no data is lost, no readers are broken.
The batch sleep pattern matters under real load. A loop that updates 1,000 rows without breathing will lock the wp_postmeta table long enough to cause 504 timeouts on inventory queries running concurrently.
3 schema migrations using the expand-then-contract pattern. 0 downtime windows required for any of them. The lead source migration took 2 deploys and a background cron job — total elapsed time 4 days, 0 customer-facing impact. The batch backfill ran at 50-row batches with 100ms sleeps: 4,200 lead records processed in 14 minutes with no lock-induced errors in the error log.
get_option call. The ability to flip a bad change off without a deploy is worth more than that.Every lesson stays free — no account, no paywall, no email gate, ever. But if you’d rather have this system standing on your business than wire all 48 lessons yourself, leave your email. We’ll send you a direct line to a build — and you’ll be first to hear when we add new tools to the curriculum.
None of this gates a single lesson. The curriculum was free before you got here and it stays that way.
You came here to understand the system, and now you do. If you’d rather have it standing on your business than spend the next three months wiring it yourself, GAP Concierge is the same architecture from these lessons — a white-label AI agent that knows your catalog and captures your leads — set up for you, from $97/mo.
See GAP Concierge →