I might add one change. You say add a column migration, ship it, then add the feature that uses the column and ship again. I was planning on shipping the code at the same time (or even before) the migration, but having the feature disabled with a feature-flipper until the migration is done.
1. Create new structure 2. Add trigger to old tables for insert/update to new tables. 3. Create batch job that migrates records that aren't added by 2 above. 4. Ship.
When migration of data is completed.
1. Deploy new application code to all servers. 2. Remove old tables & triggers only after code is deployed and verified.
Make sure that you don't have any code that does "select " elsewhere in your system. Otherwise, the result sets' shape changes when you start adding columns. (And you can get into naming collisions where suddenly a select is ambiguous where it wasn't before (when both a and b have a column named ID, for example).
It's obviously fine to do some selects... :)