Optimise The Contract, Not Just The Calls

Most integration failures do not start with the dramatic part of the system. They start with the ordinary assumptions around it.
A service needs to filter records, so it first asks another endpoint for the list of valid actors. A sync job needs to minimise external calls, so it caches a small lookup table. A workflow needs to stay within a rate limit, so someone trims the request set to the smallest shape that appears to work. None of these decisions is careless. In fact, they are usually signs of healthy engineering judgement: reduce waste, keep dependencies narrow, and avoid asking more of a system than you need.
The trap is that an integration’s real dependency is not always the obvious business object. It is the chain of context required to use that object safely.
If a ticket only gives you an internal identifier, then the integration may also depend on the directory that explains that identifier. If an event stream only gives you a compact status code, then the consumer may depend on the reference data that turns that code into a decision. If a deployment pipeline depends on an external service during a build or release, then the health of that service is part of the delivery path, not a background detail.
This is why I like treating integrations as contracts, not just collections of API calls. The contract should say what the integration must know, how it discovers that information, how often it is allowed to ask, and what it does when the answer is missing or delayed.
That sounds heavier than it needs to be, but it can be very small. A useful integration contract might fit on half a page:
- the primary operation the integration performs
- the supporting lookups it requires before that operation is valid
- the rate, latency, and freshness assumptions for each lookup
- the cache and fallback behaviour when a lookup fails
- the health checks that prove the integration is still capable of making a correct decision
The important bit is not the document. It is the shift in conversation. Instead of asking, “Can we remove this call?”, we ask, “What decision did this call make possible?” Instead of asking, “Can we reduce the scope?”, we ask, “What context must remain available for the reduced scope to be truthful?”
This becomes especially important when a team is doing good optimisation work. Rate limits, smaller payloads, fewer permissions, and tighter network paths are all worth pursuing. But every optimisation is a bet about what is essential. Without a named contract, those bets get scattered across code, configuration, and memory. The system may still work in the happy path, but fail the moment a lookup becomes stale, a filter needs enrichment, or an upstream service returns a thinner response than expected.
The operational answer is to make the dependency visible before the optimisation lands. Add a smoke test that exercises the full decision path, not only the final write or read. Put supporting lookups in dashboards and alerts if they can stop the primary workflow. Record which calls are optional and which are load-bearing. If a dependency is intentionally removed, name the behaviour that replaces it.
This is also a leadership habit. During an incident or a hurried delivery window, people naturally focus on the part that is visibly broken. A clear integration contract gives the team a calmer map. It says, “Here are the things this workflow needs in order to tell the truth.” That makes diagnosis faster and blame less tempting.
Good systems are not merely frugal with their dependencies. They are honest about them.
Optimise the calls, certainly. Tighten the permissions. Cache the slow lookups. Remove the waste. But first, make sure the team can point to the contract and explain what the integration still needs to know. The smallest reliable integration is not the one with the fewest calls. It is the one where every remaining call has a job, every removed call has a replacement, and every failure mode has somewhere clear to go.


Share your thoughts