Replacing a system incrementally requires something able to send some requests to the old implementation and some to the new. Without it, the migration is all-or-nothing however carefully the code is written.
location /api/orders { proxy_pass http://new-service; }
location /api/reports { proxy_pass http://new-service; }
location / { proxy_pass http://legacy; }
The router is usually nginx and occasionally the legacy application itself, which is worse but avoids a new component. The important property is that moving one route is a configuration change rather than a deploy of either system, so a route can go back in seconds. Route by path first; routing by user or by percentage is a later refinement, and it needs the new path to be genuinely equivalent rather than merely working.