Earlier quoted context omitted.
So it's actually 100% the same as you would do with on-the-fly migrations in SQL: (1) Add column and add code moves the old data when you access it. Deploy. (2) Let it run for a while. Run a background job that migrates the rest (this might be done days or months later). (3) Remove the column and the custom code. The more I hear about "schemaless" the more I realize that it doesn't make any difference at all.
For a large table, adding a new column can be non-trivial, each record is updated when the new column is added.
As other posts have already mentioned, adding a new column to a table in postgres does so instantly unless you set a default (telling postgres that you want it to update all the old records too).
If you add the default separately from adding the column, you get the default but don't rewrite all the old rows (which remain NULL).