PlanetScale’s "safe migrations" are less about preventing DDL errors and more about eliminating downtime during schema changes.
Let’s see it in action. Imagine we have a users table:
CREATE TABLE users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
And we want to add a status column. A naive ALTER TABLE users ADD COLUMN status VARCHAR(50); would lock the table, preventing writes and reads while the schema is updated. This is a no-go for production.
PlanetScale’s approach is a multi-step process that keeps the application online throughout. The core idea is that changes are applied in a way that the old schema and the new schema can coexist temporarily.
Here’s how it works for adding a status column:
-
"Shadow" Schema Creation (or "Before Schema"): PlanetScale first creates a new, temporary version of the table behind the scenes. This new table is identical to the old one, but it has the
statuscolumn. Crucially, this new table is not yet used for reads or writes. This step is usually very fast. -
Data Backfill: Now, PlanetScale starts populating the
statuscolumn in the new table with data derived from the old table. For a new column, this might mean backfilling with a default value (likeNULLor a specified default). This is a background process that can take time depending on the table size. During this phase, your application continues to read and write from the originaluserstable. -
"Online" Schema Swap (or "After Schema"): Once the backfill is complete, PlanetScale performs a near-instantaneous atomic swap. The old table is renamed to a temporary name, and the new table (now fully populated) is renamed to
users. Because this swap is atomic, there’s no period of unavailability. Your application is now hitting the new schema. -
Cleanup: After a grace period (during which PlanetScale monitors for any issues), the old table is dropped.
The key levers you control are the DDL statements themselves and the timing of their deployment. PlanetScale handles the complex orchestration. You initiate the schema change, and PlanetScale manages the multi-step deployment to ensure zero downtime.
Consider adding an index. The process is similar: a new index is built on a shadow table, then applied atomically. Or, dropping a column: the column is marked as "deleted" in the new schema, backfilled by copying data from the old, and then swapped.
The most surprising aspect is how PlanetScale manages foreign key constraints and triggers during these swaps. It doesn’t simply do an atomic swap of the table definition. Instead, it often involves creating new versions of dependent objects (like foreign keys pointing to the table being altered) that are compatible with the new schema, and then atomically switching the references. This ensures referential integrity is maintained throughout the process, even if the underlying table data or definition is changing.
The next concept you’ll likely grapple with is handling complex schema changes that can’t be done in a single "safe" migration, requiring multiple coordinated steps.