Although old, JDBC is a perfect example for efficient and pragmatic API-SPI design (except the SQLExceptions :-)). The JDBC API is the canonical representation of the functionality and both parties
used it from the opposite "directions".
JDBC is Java, but you get considerably more duplication with something like CORBA. Then you will get an IDL file, which describes the contract, then a Java interface what is generated from the
IDL for the stub and the same interface with skeletons for the server. You could even go one step further and use instead of binary protocols like IIOP, SOAP and WSDL. This could be considered as CORBA over ASCII :-).
Even something lightweight and pragmatic as REST causes duplication. You are exposing a resource with Java methods, with JAXB DTOs as parameters. Those resources are mapped to URL and the DTOs to payload.
On the client side you have to parse the JSON, XML or other formats into DTOs for easier consumption. The client side DTOs and the corresponding serverside parameters are surprisingly similar if not identical (except perhaps their package declarations)
Things get a little bit strange in enterprise or corporate projects, where architects try to decouple modules introducing an artificial level of indirection. It becomes especially "funny", if the client and
sever are both developed in Java and the duplication becomes obvious. But the real problem is the shift of the canonical representation from the contract to the client or serverside. In vast majority
of all cases not the contract, but the database or the UI become the canonical "master". Everything else has to be derived from the single "truth". Ruby On Rails folks built a whole framework
around this principle and try to leverage as much as possible from the database and its meta data. RoR is said to work well :-).
Loose coupling is only healthy and easy to maintain, in case the service user must not be derived from the resource behind the service provider. You can change then the resource and even the service implementation without breaking the contract or the client.
A typical RFE, like the introduction of an additional text control to a form, changes the service, the indirection and the UI - and so all participants. Such RFEs can be more efficiently implemented without any indirections - in contrary you should try to derive as much as possible from the canonical representation of the domain object / entity.