In this lesson, you will learn how to design database migrations that allow your application to remain stable even when running multiple versions concurrently during a rolling update.
When performing a rolling update or a canary release in Kubernetes, your cluster briefly hosts both the "old" and "new" versions of your application code. If the new version requires a database schema change, you cannot simply perform a destructive migration, as it would break the old application instances still handling live traffic.
The Expand-Contract Pattern
To maintain backward compatibility, we follow the Expand-Contract (or Parallel Change) pattern. This strategy breaks a single breaking migration into three distinct, non-breaking steps:
- Expand: Modify the database in a way that is additive. For example, if you are renaming a column, you first add the new column while keeping the old one. Your application code is updated to write to both columns (or double-write) but continues to read from the old one.
- Migrate: Backfill the new column with existing data from the old column.
- Contract: Once the new version is fully deployed and verified, you update the application to read from the new column and eventually drop the old column in a subsequent deployment.
Implementing in Kubernetes
In a CI/CD pipeline, these migrations are often executed as a Job or InitContainer before the rollout begins. Because the migration must support both versions, the SQL should be purely additive:
-- Migration Step: Expand
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
-- No columns are deleted yet, ensuring the 'old' binary still functions.
If your application needs to handle a rename, ensure your migration logic checks for column existence before executing. By keeping the database state flexible enough to support the "n-1" version of your code, you decouple your deployment pipeline from strict database lock-step requirements. This allows your Argo Rollouts or standard deployments to shift traffic between versions without the database acting as a single point of failure during the transition.
Key Takeaway: Never execute destructive schema changes (dropping or renaming) in a single deployment; instead, use additive migrations that allow your database to support both old and new application versions simultaneously.