Chapter 13 Flashcards — Contracts

flashcards saht contracts coupling consumer-driven stamp-coupling


What is a strict contract in service-to-service communication?
?
A strict contract requires the consumer to conform exactly to the producer’s full published schema. Every field is significant; additional fields are rejected or cause errors; missing fields cause failures. If the producer adds any field — even an optional one — consumers using strict parsing may fail, requiring coordinated release of both producer and consumer. Strict contracts provide high type safety but create tight coupling that makes schema evolution expensive.

What is a loose contract (Tolerant Reader pattern)?
?
A loose contract requires the consumer to extract only the fields it needs and ignore everything else. If the producer adds new fields, existing consumers are unaffected — they simply don’t see the new fields. Only the removal of a field the consumer actually uses is a breaking change. The Tolerant Reader pattern (Martin Fowler) formalizes this: write consumers that are tolerant of things they do not understand. Loose contracts sacrifice some boundary validation for significantly better evolvability.

What is the central trade-off between strict and loose contracts?
?
Type safety and validation vs. evolvability and consumer autonomy. Strict contracts give you compile-time or deployment-time assurance that producer and consumer are compatible, and allow validation of every field at the boundary. But any producer change forces consumer coordination. Loose contracts allow producers to evolve their schema without coordinating with consumers — but field removals may fail silently, and the boundary validates less. The right choice depends on the number of consumers, the rate of schema change, and whether regulatory requirements mandate full schema validation.

Under what conditions are strict contracts the better choice?
?
Strict contracts are better when: (1) both producer and consumer are owned by the same small team who can coordinate releases; (2) regulatory or compliance requirements demand full schema validation at the boundary (e.g., PCI-DSS, financial messaging); (3) the schema is stable and unlikely to evolve frequently; (4) type safety is a hard requirement for correctness. As the number of consumers grows, or as the schema change rate increases, the coordination cost of strict contracts becomes prohibitive.

Under what conditions are loose contracts the better choice?
?
Loose contracts are better when: (1) many consumers exist with different needs — forcing all to coordinate on every producer change is impractical; (2) the producer’s schema evolves frequently with new optional fields; (3) teams cannot synchronize releases; (4) the system uses event-driven architecture where events are broadcast to many subscribers. Consumer-driven contract testing (e.g., Pact) can add a safety net that catches real breaking changes without requiring strict schema compliance.

What is consumer-driven contract testing (CDCT)?
?
A technique where each consumer specifies what it actually needs from a producer as a machine-readable contract (a “pact”). The consumer’s test suite generates the pact; the pact is published to a shared broker; the producer’s CI pipeline verifies that it satisfies every consumer’s pact before deployment. This means the producer knows exactly which fields any consumer actually reads — not which fields exist in the schema. Removing an unused field is safe; removing a used field fails the producer’s build. Pact is the most widely used CDCT framework.

What are the key limitations of consumer-driven contract testing?
?
(1) Discipline requirement: All consumers must maintain up-to-date pacts; stale pacts create false assurance. (2) Semantic blindness: CDCT verifies that fields exist with the expected type — it does not verify semantic correctness (e.g., that a status field containing “OPEN” means the same thing to producer and consumer). (3) Infrastructure cost: Requires a Pact Broker, CI pipeline integration, and ongoing maintenance. (4) Not universal: Works best when teams control both consumer and producer test suites — external third-party consumers may not participate.

Name three versioning strategies for strict contracts and their key trade-off.
?
(1) URI versioning (/api/v1/, /api/v2/): Explicit, cacheable, easy to route. Con: consumers must explicitly upgrade; multiple URI versions must be maintained. (2) Header versioning (Accept: application/vnd.service.v2+json): URI remains stable; versioning is a negotiation. Con: less visible, harder to cache, requires content-negotiation infrastructure. (3) Semantic versioning of schemas (major.minor.patch): Formally encodes breaking vs. non-breaking changes. Con: consumers must be disciplined enough to handle minor version upgrades gracefully. URI versioning is the most operationally simple; header versioning gives the cleanest URIs; semantic versioning communicates intent most precisely.

What is stamp coupling?
?
Stamp coupling (data-structure coupling) occurs when a component passes a large, composite data structure to another component even though the recipient only needs a small subset of the fields. The recipient is “stamped” with a dependency on the full structure. Example: a NotificationService receiving a TicketFull object (15 fields) when it only reads ticketId and customerId. Any change to TicketFull’s schema potentially requires recompiling or redeploying NotificationService, even if the two fields it uses are unchanged.

What are the three main problems caused by stamp coupling?
?
(1) Unnecessary bandwidth: Serializing and deserializing a large object when only a few fields are needed wastes network bandwidth and CPU — especially significant at high message frequency or large object size. (2) Artificial schema coupling: The consumer is coupled to the producer’s internal data model; producer refactoring forces consumer changes even when the fields the consumer uses haven’t moved. (3) Versioning burden: Every schema change to the large object is a potential breaking change for every consumer, regardless of which fields they actually use. A fourth concern is security/privacy: fields the consumer shouldn’t see are transmitted and parsed unnecessarily.

How should stamp coupling be fixed in the non-workflow case?
?
Three approaches: (1) Purpose-built messages: The producer provides a slimmer, consumer-targeted event or API response containing only the fields the consumer needs (e.g., TicketNotificationEvent { ticketId, customerId }). (2) API composition: The consumer queries for only the fields it needs via a targeted API endpoint rather than receiving a fat event with everything. (3) Projections/views: The producer publishes multiple projections of the same underlying data — one per consumer type — so each consumer receives exactly its required fields. The right choice depends on the number of consumers and the communication style (events vs. request-response).

When is stamp coupling legitimate?
?
Stamp coupling is legitimate in workflow state management: when a large object serves as the carrier of accumulated state passed between ordered pipeline steps. Each step enriches the object and passes it to the next. Example: a ticket workflow where Step 1 creates the ticket, Step 2 adds the assigned technician, Step 3 adds the appointment time, Step 4 records the notification. Each step needs the context of all previous steps; passing the full TicketWorkflowMessage is the mechanism. The coupling is intentional and serves a purpose — it avoids requiring each step to query a central database for context (which would create database coupling and multiple round-trips).

What conditions make stamp coupling in workflow management problematic even when it starts out legitimate?
?
(1) Unbounded schema growth: The workflow object accumulates fields across releases with no removal, creating a bloated schema that nobody fully understands. (2) Cross-team ownership: When different teams own different steps, the shared workflow object becomes a shared schema — a coordination bottleneck equivalent to a shared database. (3) Least-privilege violations: If sensitive fields (billing codes, PII) are in the workflow object, steps that don’t need them are exposed to data they shouldn’t see. (4) Schema drift: Different steps interpret the same field differently because the workflow object serves as the coupling point between teams with different semantic understanding.

What is the “tolerance spectrum” in contract design?
?
A conceptual spectrum from strictest to loosest contract: Exact schema (consumer must match producer exactly, e.g., strict XML Schema or Protobuf) → Versioned schemas (multiple explicit versions maintained in parallel) → Consumer-driven contracts (each consumer specifies what it needs; Pact verifies) → Tolerant Reader (consumer ignores fields it doesn’t understand) → Amorphous (no schema enforced, e.g., raw JSON blob). Moving right increases evolvability and reduces boundary validation; moving left increases type safety and reduces evolvability. The appropriate position depends on consumer count, schema change rate, and regulatory requirements.

What does it mean for a contract to cause “version proliferation”?
?
Version proliferation occurs when strict contracts force the producer to maintain multiple simultaneous versions of the same API because different consumer teams upgrade at different rates. If v1 consumers haven’t upgraded when v2 launches, the producer must serve both /v1/ and /v2/ indefinitely. With many consumers on different upgrade schedules, a producer may accumulate three, four, or more live API versions — each requiring independent maintenance, monitoring, and bug fixes. Version proliferation is one of the primary long-term costs of strict contracts in a large organization with many consumers.

Why is “adding a field” often a breaking change under strict contracts?
?
In a strict-contract model, consumers are typically generated from a schema definition and their parsers are written to expect exactly the defined fields. If the schema allows “no extra fields” (which strict parsers often enforce), adding a new field to the producer’s response causes the consumer’s parser to reject the message as containing unexpected content — even though the new field is logically optional and harmless. This is why even “backwards-compatible” changes (adding optional fields) require coordinated consumer releases under strict contracts, and why many teams find strict contracts operationally expensive in practice.

How does stamp coupling relate to the principle of least privilege?
?
Least privilege states that a component should have access only to the information it needs to perform its function. Stamp coupling violates least privilege at the data level: by passing a large object to a consumer that only needs a few fields, the producer exposes sensitive or irrelevant data to the consumer unnecessarily. For example, if a TicketFull object passed to a NotificationService contains billingCode, auditLog, and customer PII, the notification service — which only needs ticketId and customerId — now processes and parses data it has no business need to see. This creates security and privacy exposure, and potentially creates compliance obligations for the consumer.

What question should you ask to determine whether your stamp coupling is a problem or a feature?
?
Ask: “Is the recipient in a different team from the producer, and does the recipient need fewer than roughly half the fields in the object?” If yes to both, it is likely a problem — refactor to a targeted event. Then ask: “Does the large object serve as accumulated workflow state passed through an ordered pipeline that a single team owns?” If yes, stamp coupling may be legitimate. Finally, regardless of the answer: “Does the object contain fields the recipient should not see for security or compliance reasons?” If yes, it is always a problem regardless of workflow legitimacy.

What is the relationship between the number of consumers and the choice of strict vs. loose contracts?
?
Number of consumers is the most important scaling factor for contract strictness. With one or two consumers (co-located with the producer), strict contracts are manageable — coordinated releases are a minor inconvenience. With five to ten consumers (across different teams), strict contracts create significant coordination overhead — every schema change requires five to ten release coordination events. With tens or hundreds of consumers (e.g., a public API), strict contracts are practically impossible to manage — the producer cannot coordinate with all consumers. Loose contracts with CDCT become the only viable approach at large consumer scale.


Total Cards: 19
Priority: HIGH
Last Updated: 2026-05-30