A rewrite is rarely a code problem. It is a problem of everything the code does that nobody wrote down.
Five questions decide it before the first commit.
What behaviour is load-bearing but unintentional? Old systems collect quirks, and downstream consumers have already adapted to them. Rounding that falls one way every time. A field that arrives empty instead of null. You can reproduce it or you can break it, but decide that on purpose rather than find out in production.
Where does state live outside the database? Queue messages in flight, cron runs that are half done, retry counters sitting in a worker. A cutover moves rows. What is mid-flight it hardly ever moves.
Who calls this that you do not control? Integrations you cannot version on your own schedule: their change window sets your timeline, not the other way round.
Can both systems run at once and be compared? If you cannot push real traffic through the old path and the new one and diff the outputs, you are not migrating, you are hoping. Reconciliation is the migration.
What is the unit of rollback? One endpoint, one service, one table. If the honest answer is "the whole thing", your plan has no brakes.
In Python 2 to 3 migrations and monolith-to-services splits the failures cluster in the same place: not the new code, but the assumptions the old code was quietly satisfying for years. The new implementation is usually correct. Correct against a specification nobody ever agreed to...
I wrote more on how I approach cutovers like this: https://polycratia.com/c/ef123070
Of those five, one gets skipped most often in the rewrites you have been part of.