In this lesson, you will learn how to orchestrate complex schema evolutions where database changes and application code updates must be deployed in a strict, interdependent sequence.
When performing non-trivial schema changes—such as renaming a column, moving data between tables, or splitting an entity—you cannot simply run a migration and deploy code simultaneously. If your application code expects a new schema before the migration finishes, or if the database is altered while the application is still using the old schema, the system will fail.
The Phased Deployment Strategy
To manage these dependencies, we break the evolution into three distinct, idempotent phases that ensure compatibility at every step:
- Expand (Database Change): Apply a migration that makes the database additive. For example, if you are renaming
user_nametofull_name, add thefull_namecolumn but keepuser_nameintact. - Migrate (Code Change): Deploy application code that supports both columns. The code should write to both columns but prefer the old one for reading. Once the code is stable, update the logic to read from
full_nameand write to both. Finally, deploy code that only uses the new column. - Contract (Database Cleanup): Once the code is successfully running on the new column and no legacy references remain, run a final migration to drop the obsolete
user_namecolumn.
Example: Renaming a Column
If you simply dropped user_name and added full_name in one migration, any application server running the old code would crash immediately upon trying to SELECT user_name. Instead, use this transition:
-- Phase 1: Expand (Add new column)
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
-- Phase 2: Code updates (Application writes to both, then reads from new)
-- Phase 3: Contract (Cleanup)
ALTER TABLE users DROP COLUMN user_name;
By decoupling the database schema change from the application logic, you prevent the "all-or-nothing" deployment trap. This approach relies on your migrations being fully idempotent, ensuring that even if a phase is executed multiple times, it does not cause state corruption or errors.
Key Takeaway: Never deploy a breaking schema change in a single step; use an Expand-Migrate-Contract pattern to ensure your database and application code remain synchronized throughout the deployment lifecycle.