So in practice that could look like the interface to your "module" should use parameter objects [1] for function calls, and build in the assumption that most information retrieval or processing requests are async. Then when the time comes swap out the local version of the module of the client stub version.
To avoid manually writing the serialization, those parameter objects should have been code generated already. Although the goals of something like tRPC [2] are admirable, having to manually check inputs with typeof [3] honestly shouldn't be necessary. And allowing non-nullable fields may make field deprecation extremely difficult or impossible. Guess what? If you have any native mobile clients they may not upgrade for years! OpenAPI also exists, but seems a bit too verbose to read, or author manually.
So what would I suggest? Just use proto3/gRPC [4]. It defines the JSON encoding if you haven't clients that can't talk native Protobuf, or don't want to have bikeshedding over what type-casing to use for JSON serialization. If your cloud provider doesn't do it for you already, just drop an Envoy with the gRPC-JSON transcoder in front [5]. If you can't prove you'd have an actual performance problem due to gRPC/Protobuf, then it probably isn't worth the effort to use a less battle-tested messaging library.
So back to the main topic... it doesn't have to be hard to do microservices when the time comes, just make sure your codebase is easier to convert.
[1] https://wiki.c2.com/?ParameterObject
[2] https://trpc.io/
[3] https://trpc.io/docs/quickstart#add-a-mutation-procedure
[4] https://developers.google.com/protocol-buffers/docs/proto3#j...
[5] https://www.envoyproxy.io/docs/envoy/latest/configuration/ht...