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:

PromiseEvidenceOwner
Accepted actions reach a terminal stateDurable action status and age alertProducer
Retries do not duplicate the external effectIdempotency test and duplicate-effect monitorProducer
Consumers stop unsafe work during degradationDependency health signal and fallback testConsumer
Breaking changes arrive with migration timeVersion policy and active-consumer inventoryProducer 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.