Implementing Schema Evolution

1. Adding Columns Safely

StepDetail
NULLable firstADD COLUMN without DEFAULT — instant in PG 11+
Backfill in batchesUPDATE WHERE ... LIMIT
Add NOT NULL afterOnce backfill complete
Default-with-volatileAvoid full table rewrite

2. Removing Deprecated Columns

Deprecation Workflow

  1. Stop writes from app (release N)
  2. Stop reads (release N+1)
  3. Verify no production usage
  4. DROP COLUMN (release N+2)

3. Renaming Columns Without Downtime

Expand-Contract Rename

  1. ADD new column
  2. App writes both
  3. Backfill new from old
  4. App reads new
  5. App writes only new
  6. DROP old column

4. Changing Column Data Types

ApproachDetail
ALTER TYPE compatibleVARCHAR → TEXT — no rewrite
USING clauseALTER COLUMN ... TYPE int USING col::int
Shadow columnAdd new col, dual-write, switch reads
pg_repack / gh-ost / pt-oscOnline schema change tools

5. Adding Indexes Online

DBCommand
PostgresCREATE INDEX CONCURRENTLY
MySQL 8ALTER TABLE ... ADD INDEX ... ALGORITHM=INPLACE, LOCK=NONE
SQL ServerWITH (ONLINE = ON)
OracleCREATE INDEX ... ONLINE

6. Modifying Constraints Safely

OperationPattern
Add CHECKADD ... NOT VALID, then VALIDATE CONSTRAINT
Add FKNOT VALID + VALIDATE — avoids full lock
Add NOT NULLBackfill, add CHECK NOT VALID, validate, then SET NOT NULL
Drop constraintGenerally fast (metadata only)

7. Handling Backward Compatibility

PracticeDetail
Tolerant readerApp ignores unknown fields
Additive changesAdd new; never repurpose existing
Version flagschema_version + branching code
Views as facadeStable contract over evolving tables

8. Implementing Forward Compatibility

PracticeDetail
Reserve unknown fieldsJSONB extra blob
Use enums carefullyPrefer lookup tables
Schema registryAvro / Protobuf compat checks
Don't enforce strict shapeIf clients may be ahead

9. Documenting Schema Changes

ArtifactDetail
Migration filesOrdered, version-controlled (Flyway/Liquibase)
CHANGELOGHuman-readable per release
Schema diffmigra, schemaspy, dbml
ADRsWhy this change

10. Testing Schema Changes

TestDetail
CI migration applyFrom scratch + from prod snapshot
Rollback pathDown-migration tested
Load on stagingVerify lock time / perf impact
Shadow runApply against prod clone