A workflow sends an appointment request to a scheduling service. The service commits the appointment, but its response is lost. The workflow retries. The customer now has two appointments.
Both teams can claim their component behaved as documented. The producer accepted two valid requests. The consumer retried a timeout. The user still received the wrong result.
The missing architecture is the promise between the teams: how an action is identified, how its final state is confirmed, and who repairs ambiguity.
Describe the outcome before the interface
I start with the result the two teams jointly provide. A caller receives a confirmed appointment. A payment is either applied once or known not to have been applied. An account change is visible to every system that makes a decision from it.
The outcome reveals obligations that an API schema cannot. A response code may report that a request was accepted without proving that the customer-visible state changed. The consumer needs to know what the response guarantees and what it must still verify.
For each cross-team path, I write the promise in operating terms:
| Promise | Evidence | Owner |
|---|---|---|
| Accepted actions reach a terminal state | Durable action status and age alert | Producer |
| Retries do not duplicate the external effect | Idempotency test and duplicate-effect monitor | Producer |
| Consumers stop unsafe work during degradation | Dependency health signal and fallback test | Consumer |
| Breaking changes arrive with migration time | Version policy and active-consumer inventory | Producer and consumer |
The owner is responsible for keeping the promise observable. The other team should not need private knowledge of the implementation to determine whether the contract holds.
Give both sides obligations
Service contracts are often written as producer guarantees. Consumers also shape reliability.
A producer may promise a latency distribution, idempotent handling, and a deprecation window. The consumer may promise bounded retries, valid identifiers, and a fallback when the dependency reports a known degraded state. Without the second half, one team can overwhelm a recovering service or turn a contained failure into a customer-visible outage.
The agreement should state the supported workload and authority. A service tested for read-only lookup should not quietly become the source of truth for an irreversible action. If the consumer changes the consequence of failure, both teams need a new decision.
Assign failure before it happens
When a cross-team operation fails halfway, the architecture needs a repair owner. “The workflow team owns the experience” and “the platform team owns the service” can both be true while neither team owns reconciliation.
I ask four questions before release:
- Which team first detects each important failure?
- Which team can determine the actual state?
- Which team has authority to repair it?
- What context must move with an escalation?
If detection, diagnosis, and repair sit with different teams, the handoff must be tested. An escalation channel alone is weak. The receiving team needs the request identity, observed state, attempted recovery, customer consequence, and time limit for action.
Design the change path
An interface that cannot change safely becomes a tax on both teams. The contract should define how a new field, behavior, or failure mode enters production.
Versioning is only one tool. The teams also need an inventory of active consumers, a compatibility test, a rollout signal, and a way to reverse the change. A long deprecation period does not help when nobody can tell which consumer still relies on the old behavior.
I prefer a change proposal that states the affected promise. “Add an endpoint” describes implementation. “Allow consumers to confirm the final action state without replaying the action” explains why the boundary should change and how to judge the result.
Review the promise together
Cross-team contracts decay when each side reviews only its own component. I want the teams to examine the path using shared evidence: outcome completion, latency by consumer, retry behavior, unresolved actions, repair load, and recent contract violations.
A joint failure exercise is especially useful. Disable the dependency, delay a terminal event, or return an ambiguous result. Then observe who notices, what the customer experiences, and whether the agreed repair path works with the people actually on call.
Each promise should have one owner. The complete user outcome should have no interval that both teams can reasonably call someone else’s problem.
A box-and-arrow diagram shows where data moves. I consider the boundary designed when both teams can state what an arrow guarantees, how they will prove it, and who acts when it fails.