The API contract testing decision record: why the contract test model you chose determines your backward compatibility confidence surface and your breaking change detection latency
API contract testing decisions are made in three sessions: the initial integration session that installs Pact because it is the most popular library, writes a few consumer interactions, and sets up provider verification in CI without documenting who owns the contracts or what "can I deploy?" means in this codebase; the API change session that adds a new required request field and ships it without running consumer pacts because the change "only adds things"; and the multi-team expansion session where a second consumer team uses the API without registering their pacts. What none of these sessions produce is the contract ownership model, the "can I deploy?" enforcement policy, the contract evolution definition, or the test scope boundary — the gaps that determine whether contract testing provides a backward compatibility guarantee or records breaking changes after they have already reached production.
The required field that broke every existing consumer
A fintech startup builds an internal payments platform. The platform team exposes a POST /payments endpoint consumed by three product services: the checkout service, the subscription billing service, and the refunds service. A developer on the platform team asks Claude: "How do I make sure our API doesn't break consumers when we change it?" Claude returns a Pact consumer-driven contract testing setup: the consumer services write pact files specifying the interactions they depend on, the pact files are uploaded to a pact broker, the provider CI pipeline runs verification against all consumer pacts before merging. The setup takes an afternoon. The checkout team writes three pact interactions: create payment, get payment status, and cancel payment. The billing team writes two. The refunds team writes one.
For six months, the system functions as intended. When the platform team modifies the response structure of the payments endpoint, the provider verifier catches failures in the checkout team's pact before the change reaches production. Three breaking changes are caught in CI. The contract testing is working.
In month seven, the platform team adds a new compliance requirement: all payment creation requests must include a requestedBy field containing the user ID of the person initiating the payment. This field is required at the API level — the endpoint returns a 422 if it is absent. The platform engineer implements the change and opens a PR. The PR description reads: "Adding required requestedBy field for audit compliance. Consumer teams will need to update their request payloads."
The engineer runs the test suite locally. The provider's unit tests pass — the tests were updated to include the new field. The platform team's integration tests pass — they also send the new field. The PR reviewer approves. Nobody runs the provider verifier against the consumer pacts before merging, because the change "only adds a required field — the existing consumer tests won't cover this, we'll tell them separately." The change is merged and deployed to production within two hours of PR approval.
The checkout service starts returning 422 responses on payment creation immediately after the platform deploy. The checkout service's production calls to POST /payments were sending the pre-change payload — no requestedBy field, because the checkout service hadn't received or implemented the update. The billing service similarly begins failing. The refunds service, whose only pact interaction was GET, is unaffected. The platform team detects the failures twelve minutes after deployment through error rate monitoring on the payments endpoint — but by then, 847 checkout payment attempts have failed and the billing service has missed its 09:00 billing window.
The post-mortem focuses on process: "We should have told the consumer teams before deploying." The Pact infrastructure is reviewed. The pact broker shows that the last successful provider verification run (from six days prior) verified all consumer pacts against the previous provider version. There is no record of a verification run before the merge. The team adds a rule to their PR checklist: "run pact verification before merging API changes." The rule is a comment in a checklist. It is not enforced in CI. The "can I deploy?" check was never wired into the pipeline — it was in the Pact setup documentation they had followed, listed as a step to "add later once you have full coverage." The check was never added.
The requestedBy failure would have been caught by the "can I deploy?" check before deployment: the checkout team's pact specifies a POST /payments request that does not include requestedBy. When the provider verifier replays that request against the updated provider, it returns a 422. The verification fails. "Can I deploy?" returns false. The deployment is blocked. The consumer teams are notified that they need to update their pacts — which means they also need to update their code — before the provider can ship. The contract testing infrastructure was present and accurate. The enforcement gate was absent.
The OpenAPI validation that tested the wrong direction
A B2B SaaS platform team manages an internal service registry API consumed by eleven other teams across the organization. The platform team adopts OpenAPI schema validation: every PR adds a response validation step that checks whether the service registry's responses conform to the OpenAPI specification. When the response structure diverges from the spec, the CI pipeline fails. The team presents this in an all-hands as "we now have API contract testing in place." The approach is technically sound — response validation catches provider-side drift, ensuring that consumers who rely on the documented schema receive what the spec promises.
The response validation covers the provider's output faithfully. It does not cover consumer request inputs.
Eight months after adopting OpenAPI validation, the platform team adds a new field to the service registration request body. The field is ownerTeam, a required string identifying which team owns the registered service. The compliance team has requested this for an audit trail. The platform team updates the OpenAPI spec: ownerTeam is required in the requestBody schema. The OpenAPI validation CI step passes — the spec is valid. The endpoint now rejects any registration request that omits ownerTeam.
The platform team communicates the change through the internal engineering newsletter. Six of the eleven consumer teams see the newsletter and update their service registration code before the deadline. Five do not — two teams are in code freeze for an unrelated release, one team has the person who manages that integration on leave, and two teams simply missed the newsletter in their inboxes.
The platform team deploys the new required field to production on schedule. The five teams that haven't updated begin failing their service registration calls immediately. Two of them are running CI/CD pipelines that register services on successful deployment; those pipelines now fail at the registration step, blocking further deployments for those teams. One team's monitoring service fails to re-register on its restart cycle, causing it to fall off the service registry and become invisible to the platform's health checking system. The platform team spends four hours in incident calls helping each team update their registration clients.
Consumer-driven contract testing would have caught this failure through a different mechanism than the required field failure in the first story. The service registration consumer teams would have pact files specifying the POST /services request they send — without ownerTeam, because that is the request they were sending. The platform team's provider verifier would replay those requests against the updated endpoint and receive 422 responses, failing the verification. Before the platform team could deploy, "can I deploy?" would return false against each of the five consumer pacts that don't include ownerTeam. The platform team would be blocked from deploying until the consumer teams updated their pacts — which means until they also updated their code.
The OpenAPI validation had been working correctly the entire time. It caught three response structure divergences over eight months. It was doing what it was designed to do: validate that the provider's responses match the provider's declared schema. It was not designed to catch the case where the provider's schema changes in a way that breaks existing consumer requests — because consumer requests are not part of OpenAPI response validation. The decision to adopt "API contract testing" via OpenAPI response validation was made in a single AI chat session that produced a correct implementation of one contract testing model. The decision between consumer-driven and provider-driven testing — and the coverage ceiling of each — was never made explicitly. It was inherited from the tutorial that the AI suggested.
The decision that was never written down was: what does "API contract testing" mean in this codebase? Which direction of the contract does it test — provider responses, consumer requests, or both? What failure mode is it protecting against? The absence of that decision record meant that eleven teams believed they had "API contract testing" protecting them. Five teams discovered in production that the protection ran in a different direction than the one that broke them.
Three structural properties the API contract testing decision determines
1. The contract test model and its coverage ceiling
Every API contract testing approach makes a specific coverage claim and has a specific set of failure modes it cannot catch. Choosing a contract testing model without understanding both the coverage and the gaps is choosing a protection surface without knowing which attacks it doesn't stop.
Consumer-driven contract testing (Pact, Spring Cloud Contract) tests the interface from the consumer's perspective. The consumer specifies both the request it will send and the response fields it depends on. The provider verifier replays the consumer's exact request against the real provider implementation and checks that the response matches the consumer's expectations. The critical coverage property: because the consumer writes the request, the provider verifier tests whether the provider accepts the requests that consumers are actually sending — not just whether the provider produces valid responses per its own schema. Adding a required request field breaks the consumer's existing request payload, and the verification catches it.
Consumer-driven testing's coverage ceiling is bounded by pact completeness. Pacts only cover the interactions that consumer teams wrote. A consumer that uses the API without writing a pact has no protection from the contract system. An endpoint with no consumer pacts has no contract coverage — any change to that endpoint's request schema will not be caught. The pact coverage gap is not visible from the provider's perspective without a coverage audit: which endpoints have at least one registered consumer pact, and which do not.
Provider-driven contract testing (OpenAPI schema response validation, protobuf schema compatibility checks) tests the interface from the provider's perspective. The provider publishes a schema, and validation confirms that the provider's responses conform to that schema. This catches the case where the provider implementation drifts from its declared schema — the provider's unit tests pass with a manually specified response, but the actual endpoint returns a different structure. It catches response structure errors introduced by the provider without consumer input.
Provider-driven testing's coverage ceiling is the provider's schema coverage. It tests that the provider delivers what the schema declares. It does not test that the schema's changes are safe for existing consumers. A new required request field is valid according to the new schema — the 422 response to a request without the field conforms to the error response schema — but it breaks consumers who were sending the old request. The provider's schema validation reports "provider conforms to its schema" while consumers are breaking in production.
Bidirectional contract testing (PactFlow's bidirectional feature, Specmatic) attempts to combine both perspectives. The consumer publishes test results specifying the interactions it depends on. The provider publishes an OpenAPI spec. The broker performs automated compatibility analysis: it checks whether every consumer interaction is compatible with the current provider spec. The compatibility check catches both directions: response fields that consumers depend on but the spec no longer includes, and request field requirements that the spec mandates but existing consumer pacts don't send. The coverage ceiling is the accuracy of the OpenAPI spec — if the spec declares that a field is optional when the implementation requires it, the compatibility check will pass while the implementation rejects consumer requests.
For microservices architectures with independent deployment schedules, the failure mode that contract testing must prevent is: provider deploys a change that breaks consumers who haven't yet received the update. The only model that systematically catches this is consumer-driven, because it tests the provider against the requests that existing consumers are actually sending. Provider-driven and bidirectional models can catch this failure when the schema accurately represents the implementation's behavior — a coverage property that must be verified rather than assumed.
For a monolith or a single team controlling all consumers and the provider, the failure mode is different: the team accidentally introduces a regression in the API structure during a refactor. Provider-driven OpenAPI validation is often sufficient for this case — it catches unintended response structure changes without the overhead of managing consumer pact files across teams. The contract test model decision is partly a decision about team topology and deployment independence, not purely a technical question.
2. The "can I deploy?" enforcement model and breaking change detection timing
The "can I deploy?" check answers a specific question before each deployment: has the version I am about to deploy been verified against all the consumer contracts (or provider contracts) it needs to satisfy in the target environment? A positive answer means the deployment is safe from the contract's perspective. A negative answer means at least one consumer or provider contract has not been verified against this version — meaning there is at least one consumer whose interaction with this version has not been confirmed to work.
The "can I deploy?" check is valuable only when it gates deployment. Configured as an informational step — logging the result but not failing the build — the check documents contract violations after the fact but does not prevent them. The first story's payments failure would still have occurred with an informational "can I deploy?" check: the check would have returned false, the log would have recorded the result, and the deployment would have proceeded. The consumer breakage is the same. The only difference is that the post-mortem would have a log entry saying "can I deploy returned false twelve minutes before we deployed." That information is interesting but not preventative.
The transition from informational to hard gate is the most consequential decision in a contract testing rollout, and it is often deferred indefinitely. The deferral rationale is always the same: "we don't have complete coverage yet, so a hard gate would produce false positives — blocking safe deployments because the changed interaction isn't covered by any pact." This is correct but forward-pointing: the solution is to achieve sufficient pact coverage, not to wait for complete coverage before enforcing the gate. Teams that wait for "complete coverage" before enforcing the gate discover that complete coverage never arrives, because coverage cannot be grown under time pressure if the existing gate has no enforcement teeth.
The practical path from informational to hard gate requires three preconditions. First, the pact broker must be configured with explicit environments (production, staging) so the "can I deploy?" check can be scoped to the deployment target — a provider version verified in staging does not satisfy the check for production until it has been verified specifically against pacts that have been deployed to production. Second, the critical consumer paths must have pact coverage: the interactions most likely to break consumers when the provider changes must be in a pact, or the gate will not catch the failures that matter. A provider with pact coverage on three of its twelve endpoints can enforce the gate on those three endpoints; the other nine remain unprotected until their pact coverage is added. Third, the consumer registration process must be established: consumer teams must know they are responsible for registering pacts before the provider can deploy to production, and new consumer teams must have a documented path for registering their first pacts.
The pact broker's deployment environment model also determines the bootstrap problem: when a new provider version is first submitted to the broker, no consumer pacts have been verified against it yet. The "can I deploy?" check returns false — but not because any consumer is actually broken, rather because verification simply hasn't run. The solution is to trigger verification on the CI pipeline associated with every provider commit, not just on pact change events. When the provider merges a new commit, the CI pipeline runs the provider verifier against all currently registered consumer pacts, publishes the verification results to the broker, and then queries "can I deploy?" The check returns true because verification just ran. This flow requires that verification runs in the provider's CI pipeline on every commit, which adds a verification step that grows in duration as the number of registered consumer pacts grows. This is the correct tradeoff: the provider's CI pipeline is the natural place to verify that the provider satisfies all its consumers.
3. The contract evolution policy and the scope boundary
Contract tests verify that the interface between a consumer and a provider has a specific structure. They do not verify that the interface behaves correctly for the consumer's use case, that error codes have meaningful semantics, that rate limiting produces consistent responses under load, or that the provider correctly implements the business logic the consumer depends on. The scope boundary between what contract tests verify and what they do not verify is a decision that must be made explicitly, because the failure modes outside the contract test scope still exist — they are simply invisible to the contract testing system.
Within the scope boundary, the contract evolution policy defines what changes a provider can make without breaking registered consumer contracts. The definition is not intuitive, because "breaking" at the contract level depends on the matchers that consumers use in their pacts — as described in the FAQ below. The policy should document three categories explicitly:
Always safe to ship without consumer notification: adding optional response fields when all consumers use type matchers (not exact match); adding new API endpoints that no existing pact references; relaxing a request field's validation (making a previously required field optional); expanding an enum that consumers match by type rather than by value; adding new optional fields to responses that consumers don't reference in their pact matchers.
Safe to ship after verifying pact coverage: changes to response field semantics (the field is still present with the same type, but the values carry different meaning — this is a behavioral change outside contract test scope, but existing pact matchers will not catch it, so the change is "safe" from the contract's perspective while potentially breaking consumers in production); changes to error response structures that consumers don't explicitly test in their pacts; performance characteristic changes.
Always breaking — requires consumer coordination before deployment: removing a response field that any consumer pact references; changing a response field's type; adding a required request field; making a previously optional request field required; removing an endpoint; changing an endpoint's URL structure or HTTP method.
The breaking change category defines the deprecation process: how long before removal a field must be marked deprecated, how "deprecated" is signaled (response header, documentation annotation, a _deprecated metadata field in the response body alongside the original), and what the migration path looks like for consumers who depend on the field. The API deprecation strategy is the companion decision to the contract evolution policy — the evolution policy specifies what constitutes a breaking change, and the deprecation strategy specifies how breaking changes are introduced over time without breaking consumers who haven't yet migrated.
The API versioning strategy creates distinct contract surfaces. An API with URL-based versioning (/v1/payments, /v2/payments) has two separate pact surfaces — consumer pacts that reference /v1 endpoints are separate from pacts referencing /v2 endpoints, and the provider must verify both. The versioning strategy determines how long old API versions remain in service (and therefore how long old consumer pacts remain active and must be verified) and how consumers migrate from one version to the next. A contract testing policy that requires all consumers to maintain pacts for each API version they use, and a deprecation policy that provides ninety days for consumer migration before removing a version, combine to create a bounded operational overhead for the pact broker's verification matrix. An organization without both policies ends up with an ever-growing set of active pacts as API versions accumulate, with no mechanism for retiring them.
The scope boundary at the behavioral level is equally important to document. Contract tests do not catch: business logic bugs (the provider returns the correct field names and types but computes the values incorrectly); race condition behavior (the provider's response to concurrent requests is not tested); rate limiting semantics (which response code the provider returns when the rate limit is exceeded, and whether consumers handle that code correctly); authentication and authorization edge cases (which error responses consumers should expect for invalid tokens, expired sessions, or insufficient permissions). The test strategy decision record should explicitly map contract tests to the failure modes they cover and identify the complementary test types (integration tests, end-to-end tests, chaos experiments) responsible for the failure modes outside the contract test scope. Without this mapping, teams discover that "we have contract testing" provides no coverage for behavioral failures during incidents.
Five ADR sections the API contract testing decision record needs
1. Contract test model selection and coverage ceiling
Document the chosen contract testing model and the specific failure modes it covers and does not cover. The model selection should follow the team topology: consumer-driven contract testing (Pact, Spring Cloud Contract) for multiple teams with independent deployment schedules, where the critical failure mode is a provider deploying a change that breaks consumers who haven't received the update; provider-driven schema validation (OpenAPI response validation) for single-team ownership of both consumer and provider, where the critical failure mode is unintended regression during refactoring; bidirectional contract testing for organizations with OpenAPI-first provider development and consumer teams who can write Pact-compatible consumer tests, where both coverage properties are needed but maintaining consumer pacts across all teams is the primary operational constraint.
For consumer-driven testing, document the pact file format (Pact v2, v3, or v4 — the version determines available matchers and interaction types), the language-specific consumer libraries in use and their version pinning policy (breaking changes in Pact libraries have caused verification failures when consumer and provider use incompatible library versions), and the broker infrastructure (self-hosted Pact Broker vs PactFlow, the version compatibility between the broker and the libraries in use).
For provider-driven OpenAPI validation, document what is validated (response body structure, response headers, HTTP status codes per endpoint and method) and explicitly what is not validated (request body structure from existing consumers, request header requirements, query parameter requirements). Record the known gap: request schema changes that break existing consumers will not be caught by response validation, and the remediation for this gap (consumer teams are responsible for notifying the provider team before the provider can change request schemas, backed by an explicit communication protocol).
Document the integration points with the API gateway: whether the gateway enforces schema validation at the request routing layer (rejecting requests that don't conform to the registered schema before they reach the provider implementation), and whether gateway-level schema enforcement interacts with contract test coverage in the pact broker (gateway rejections may produce 4xx responses from the gateway layer rather than the provider layer, which can confuse provider verification results).
2. Contract ownership and consumer registration
Document who owns each pact file: the consumer team that writes the interactions and is responsible for keeping them current. The consumer owns the pact because the pact represents what the consumer depends on — if the consumer's code changes to depend on a new field, the consumer team updates their pact; if the consumer's code is updated to no longer depend on a field, the consumer team removes it from their pact. A provider team that edits a consumer's pact file (to make verification pass after a provider change) is defeating the purpose of consumer-driven testing: the pact should represent what the consumer actually sends and actually depends on, not what the provider wishes the consumer would send.
Document the consumer registration process: how a new consumer team registers their pacts with the broker, what information they must provide (consumer application name, consumer version tagging strategy), and what the provider team must do when a new consumer registers (add the consumer to the verification matrix, confirm the "can I deploy?" check is configured to include the new consumer before the provider's next production deployment). The registration process must prevent the orphaned pact problem: consumer services that are retired but whose pacts remain active in the broker, blocking provider deployments indefinitely because the orphaned consumer pacts still appear in the "can I deploy?" check. Document the deregistration process: when a consumer service is retired, its pacts must be explicitly removed from the broker's active consumer list. The trigger for deregistration is the consumer service's last deployment being tagged with a "retired" environment tag, which removes it from the active deployment matrix.
For organizations with many consumer teams, the pact coverage audit is a routine maintenance task: identify which provider endpoints have no registered consumer pacts (zero pact coverage), which pacts have not been updated in more than six months (potential orphans if the consumer service isn't actively deploying), and which consumer teams are not registering pacts for services they are actively consuming (gap between actual API usage and contract coverage). The audit frequency should be proportional to the rate at which consumer teams onboard and provider APIs evolve — monthly for active platform teams, quarterly for stable APIs.
3. "Can I deploy?" gate configuration
Document the specific "can I deploy?" configuration per environment. Production should require that the provider version being deployed has been verified against all consumer pact versions currently deployed in production. Staging may require verification against pacts deployed to staging — which may be a different consumer version than production if consumer teams deploy to staging before production. The distinction matters: a provider version that breaks a consumer's staging pact may or may not be blocking for a production deployment, depending on whether the consumer's staging and production deployments are in sync.
Document the gate's enforcement status (hard gate vs informational) and the migration plan if it is currently informational. The migration plan should specify: the coverage threshold at which the gate will be promoted to hard (percentage of critical-path endpoints with at least one consumer pact, not percentage of all endpoints), the timeline for reaching the coverage threshold, and who is responsible for driving consumer teams to add pact coverage for uncovered critical-path endpoints. Without a named owner and a deadline, the migration plan is aspirational rather than operational.
Document the gate's behavior under the bootstrap scenario: when a new provider version has no verification results in the broker (because verification has not yet run), "can I deploy?" returns false. The CI pipeline should be configured so that provider verification runs as part of the pipeline — not as a separate post-merge step — so that verification results are available in the broker before the "can I deploy?" check queries them. This requires that the provider CI pipeline: (1) run the provider verifier against all consumer pacts using the current broker's pact versions; (2) publish the verification results to the broker tagged with the provider version being built; (3) query "can I deploy?" using the same provider version and the target deployment environment. Steps 1-3 run in sequence; step 3 queries results written by step 2.
Document the override process for when the gate fails due to a genuine incompatibility: which role has authority to approve an override (platform team lead, not the individual engineer deploying), what justification is required (written rationale in the PR, linked to a ticket tracking the consumer team's remediation), and what the maximum override duration is before the consumer team must complete their remediation. The override process prevents the gate from becoming a bureaucratic bottleneck while maintaining the requirement that every gate failure is tracked and resolved.
4. Contract evolution policy and backward compatibility definition
Document the backward compatibility definition at the matcher level. The intuitive API designer's definition ("adding optional fields is safe; removing fields is breaking") is incomplete without the qualifier "when consumers use type matchers." An organization where some consumers use exact match matchers (common when pact files are generated by recording actual API responses rather than written deliberately) has a stricter effective backward compatibility definition than they may realize — adding optional response fields breaks their exact-match consumers even though the change seems safe.
Document the matcher policy: which matcher type is the default for new consumer pact interactions, and why. Type matchers (Pact's "like", "each like") are the correct default for most cases: they specify that the consumer depends on a field being present with a compatible type, not that the response body must be exactly what was captured during recording. Exact matchers should be reserved for fields where the consumer depends on a specific value (not just a value of a specific type) — enum values that the consumer branches on, status codes that the consumer uses to determine control flow, or field values that the consumer directly presents to end users without transformation. Document these cases explicitly so that future pact authors know when to deviate from the type matcher default.
For the gRPC and protobuf case, document the protobuf field evolution rules used in practice: field numbers cannot be reused (a deleted field's number is retired, not reassigned to a new field); required fields (proto2) are never added to existing messages; optional fields (proto3 default) can be added and removed with the understanding that consumers may not handle absent fields unless they were written to handle them. The protobuf wire format's backward compatibility guarantees are structural — the wire format can decode old messages with new message definitions — but the semantic compatibility must still be verified through consumer contract tests if consumer code has assumptions about field presence or value ranges.
Document the deprecation lifecycle for fields that will be removed. The minimum deprecation period should be derived from the slowest consumer team's deployment cadence: if the slowest consumer team deploys every three months, the minimum deprecation period before removal is three months plus a coordination buffer. The deprecation signal options include: marking the field with a deprecation annotation in the OpenAPI spec (visible in documentation but not at runtime), including a Deprecation response header per RFC 8594 (visible in responses, catchable by monitoring), including a _meta.deprecated_fields array in response bodies for consumers who inspect it, or publishing a deprecation notice in the pact broker as a comment on the relevant consumer pact interactions. The deprecation signal must be visible to consumer teams without requiring them to monitor a separate communication channel.
5. Contract test scope and coverage monitoring
Document what is in scope for contract testing and what complementary test types cover the failure modes outside scope. Contract tests verify interface structure: that the API's request and response shapes match what the consumer expects and the provider declares. Contract tests do not verify behavioral correctness: that the provider computes the right answer given a valid request. A payment service's contract test verifies that POST /payments returns a response with paymentId (string) and status (string). It does not verify that the payment was actually processed, that the status field reflects the payment processor's response, or that the paymentId can be used to retrieve the payment via GET /payments/{id}. Behavioral verification is the responsibility of integration tests and end-to-end tests.
Document the interactions that are deliberately excluded from contract test scope and why. Rate limiting behavior is excluded: testing rate limiting through contract tests requires either triggering the rate limit during verification (which may have side effects on the broker's verification results) or mocking the rate limiting layer (which reduces coverage fidelity). Authentication edge cases are excluded from contract test scope when the pact verification uses a test authentication token that bypasses the production authentication stack — the contract tests verify the API structure under authenticated conditions but do not verify the API's behavior for expired tokens, revoked tokens, or insufficient permissions. These exclusions should be explicit so that incident responders know which failure modes are covered by contract tests and which are not.
Document the coverage audit cadence. The minimum useful metric is endpoint coverage: for each endpoint and HTTP method in the provider's API, how many consumer pacts reference it? Zero coverage means the endpoint has no contract protection. Low coverage (one pact, one consumer team) means a change that breaks other consumer teams will not be caught. Full coverage means all consumer teams have pacts for this endpoint and the "can I deploy?" check provides complete protection for this endpoint. Publish the coverage audit in the engineering wiki quarterly and share it with provider and consumer teams so that coverage gaps are visible and actionable rather than invisible until an incident occurs.
For organizations using the service mesh or an API gateway with traffic mirroring, consider augmenting contract test coverage with production traffic replay: capture actual consumer request payloads from production traffic, add them to the contract test suite as additional interaction examples, and verify that new provider versions accept the production payload shapes. This fills the pact coverage gap for consumer teams who are using the API in production but have not written formal pact files — their production traffic becomes their implicit contract. The operational requirement is a traffic capture and replay pipeline that can filter for unique request shapes rather than replaying all production requests (which would overwhelm the provider verifier with volume).
The new technical leader inheriting a codebase with "we have contract testing" cannot determine from the code or the pact broker alone: whether the "can I deploy?" check is a hard gate or informational, which endpoints have no pact coverage, whether consumer teams know they are responsible for registering pacts, or what "backward compatible" means in this codebase's matcher configuration. These are decisions made in the initial contract testing adoption session that are invisible in the infrastructure if not documented in an ADR. The pact broker shows verification results — it cannot show why the verification model was chosen, what scope it covers, or what the enforcement policy is.
Further reading
- Test strategy decision record — contract testing is one layer in the test pyramid; the test strategy maps contract tests to the failure modes they cover and identifies the complementary test types for behavioral correctness
- CI/CD pipeline decision record — the "can I deploy?" check gates the deployment pipeline; the pipeline architecture determines whether verification runs before merge, before deploy, or not at all
- API versioning strategy decision record — versioning strategy creates distinct contract surfaces; the deprecation lifecycle and consumer migration period determine how long old consumer pacts remain active in the broker
- API deprecation strategy decision record — the deprecation lifecycle is the companion to the contract evolution policy; it specifies how breaking changes are introduced over time without breaking consumers who haven't yet migrated
- API schema design decision record — the schema design determines what the contract tests verify; schema decisions about optional vs required fields directly determine which changes are breaking at the contract level
- Microservices vs monolith decision record — consumer-driven contract testing was developed for microservices architectures with independent deployment schedules; the need for contract testing is proportional to the degree of deployment independence
- API gateway decision record — the gateway can enforce schema validation at the routing layer; gateway-level enforcement interacts with broker-level contract verification results
- Service mesh decision record — the service mesh provides runtime enforcement capabilities that complement contract test coverage; traffic mirroring can augment pact coverage with production request shapes
- The new CTO onboarding problem — inheriting a codebase with "we have contract testing" without knowing the model, the enforcement policy, the coverage gaps, or the backward compatibility definition
- Decisions never written down — the contract test model, ownership model, "can I deploy?" enforcement policy, and backward compatibility definition are made in the initial adoption session and invisible in the infrastructure if not documented