In Thinking about APIs I talked about the common problem of coupling between a client application and a server, via their shared knowledge of resource URLs:
As I noted in that previous article, and as @Ivan pointed out in the comments, this is exactly analogous to a local function call, in that both the client and the server need to agree on the resource path (Connascence of Name) and the structure of the call and its response (Connascence of Type?).
But what makes this problem a real challenge — in my case at least — is that the client and server’s code live in two different repos and have two very different deployment cycles. I can’t simply invoke a Rename Method
refactoring!
So this is another example of Implicit Coupling. There’s nothing in the source code at either end that could raise a flag if the other end has changed; I have to wait for a runtime error or a failure in some form of contract test. And this is a tangible problem for me, because recently I needed to completely re-design the API for various reasons.
So if I want to make this coupling (more) Explicit, what are my options?
The first change I made was to remove (most of) the Connascence of Name, by adopting HATEOAS (hypermedia as the engine of application state). The client now knows only the root path ‘/’
, and all other resource paths are discovered by navigating links returned in API responses. But now the client and server need to agree on the syntax and semantics of those navigation journeys. The Connascence of Name is gone, but my code has even greater reliance on Connascence of Type!
And I think that’s as far as I can go. The coupling will always be present (because the client will always need to call the server’s API) and it will always be implicit. I can provide improved runtime help via Content-Type
header negotiation etc, but at the end of the day it’s still happening at runtime and there are still plenty of server-side changes that would break the contract with the client.
Looks like I’ll just have to live with that…
If you’re enjoying this journey of discovery, please share these articles widely and ask all of your friends and colleagues to subscribe. That will in turn help others find it, and it will help keep me motivated too. I have exciting plans for this newsletter which I’ll be sharing next month, so stay tuned. Thanks for reading this far!
A perrennial problem indeed, Kevin. The best way I know to bring this back before runtime is consumer driven contract testing (such as the Pact framework) - given you own both repos. If you allow the consumer to break the providers build Ive seen this facilitate TDD (red/green/refactor) across the API. Sure gets the collaboration dialled up when consumer and provider repos are shared across two distinct teams ;)
I’m really enjoying this series. Thanks for sharing it.
For the topic of this article, Gary Bernhardt came up with a really interesting approach when writing www.executeprogram.com. You can read about that solution on their blog: https://www.executeprogram.com/blog/porting-to-typescript-solved-our-api-woes.