Flyway author here. Rollback is an illusion once your transaction has been committed. At best you can attempt to issue a compensating transaction to undo some of the effects. More details: https://flywaydb.org/documentation/faq.html#downgrade
As for reducing the risk of damage to the data itself from badly behaved application code, I think the best approach is to design your architecture such that you can't lose important data.
Releasing less frequently actually only makes the problem worse. Infrequent changes are often too big to have a chance of understanding their potential affect on the production system. You're also less likely to immediately know how to respond to a problem.
Never rolling back makes a lot of sense, and ia often asserted by the fact that many migration frameworks dont even provide rollback facility e.g. flyway. A database migration is a major high risk change IMO and should be well thought through.
If you add a column and real customer data is written to that column, then "rolling back" means deleting it. That isn't a problem that can be "solved".
I am never thinking about doing rollback on production, downgrade is nice on dev machine when you work on multiple branches. I'd rather do database restore and code restore to pev version.
I would not roll out new code with migrations without ad-hoc db backup. Any new code as well.
It's always possible to make changes to your schema without needing to potentially roll back the database. It may just be more annoying than you are willing to attempt. You might need to roll back your code to a previous version, but if you structure your changes right, you'll do it in multiple steps. (These are basically what's laid out in the article, but broken down more.)
1. Change your code to write to old and new schema; keep reading from old schema.
2. Migrate data to new schema in background.
3. Add a flag to control where you're reading from. Default it to read from old location. Keep writing to both.
4. Flip the flag on some subset of your jobs. Ensure everything is still running smoothly for as long as you like.
5. Change the default to read from new schema. Wait as long as you like to be comfortable that the change is working properly.
6. Delete the code that reads from the old schema.
7. Delete the code that writes to the old schema.
8. Drop the data in the old schema.
At any point prior to deleting the old data, if you encounter problems you can roll back to an old version of the code. If your schema changes are incompatible, you can make an entire new database with the new schema. This may temporarily waste some storage space, but it's very safe.
These guys avoid the problem by never rolling back the database, and never making changes that might require that.