Even with this setup in place you need a heightened level of caution relative to a monolith. In a monolith I can refactor function signatures however I desire because the whole service is an atomically deployed unit. Once you have two independently deployed components that goes out the window and you now need to be a lot more mindful when introducing breaking changes to an endpoint’s types
You don't have to. The producers of the microservice also produces an adapter. The adapter looks like a regular local service, but it implements the code as a REST request to another microservice. This was you get you type-safety. Generally you structure the code as
Proj:
|-proj-api
|-proj-client
|-proj-service
Both proj-client and proj-service consume/depend-on proj-api so they are in sync of what is going on.
Now, you can switch the implementation of the service to gRPC if you wanted with full source compatibility. Or move it locally.