I wish relational databases would offer One Simple Trick that I think would improve migrations immensely:
Every table should—by default—be a view of the actual underlying table. In other words, a table is a shim for the real table. This way, when it's time to perform migrations, the shim to the underlying table could change independently of the actual migration, since the interface to the table would be preserved for all applications.
I know there are overhead concerns for this shim-view idea, but I'm sure many of them can be cleverly elided under many conditions.
This is precisely what I did for my automated zero-downtime migration tool and it works pretty well: https://github.com/fabianlindfors/reshape. At least for Postgres, simple views like these have almost no overhead as queries are simply rewritten for the underlying table.