Start here. This is the direct spoken answer to practice first.
Why this question matters
A generated migration is executable code against live data. A harmless-looking rename, constraint, type change, or index operation can discard data, block traffic, or break the overlap between old and new application versions.
I inspect both the migration operations and generated SQL before production. For a rename, drop, type change, or new NOT NULL constraint, I use an expand-and-contract sequence: add the new compatible shape, deploy code that can work during transition, backfill and validate data, switch reads and writes, and remove the old shape in a later release. I do not combine irreversible cleanup with the first deployment that depends on the new schema.