Contracts
Treat cross-boundary interfaces as promises that outlive the current edit.
Identify the Contract
Before changing a boundary, name the contract and its consumers:
- HTTP, RPC, GraphQL, or CLI request and response.
- Database schema, migration output, or persisted data shape.
- Event, queue message, webhook, or background job payload.
- Public module export or package API.
- Config, environment variable, feature flag, or file format.
- UI route, URL shape, or externally visible behavior.
If callers may exist outside the current repo, assume compatibility matters.
Classify the Change
Classify contract changes before implementation:
- Additive: adds optional fields, new endpoints, new enum values, or new capabilities without changing existing meaning.
- Narrowing: rejects inputs, removes states, tightens validation, or reduces accepted behavior.
- Breaking: removes, renames, retypes, reorders, or changes the meaning of existing fields or behavior.
- Semantic: keeps the shape but changes observable meaning.
Additive changes are usually safest. Narrowing, breaking, and semantic changes require an explicit migration or compatibility plan.
Preserve Compatibility
Prefer compatibility-preserving paths:
- Add before removing.
- Read old and new shapes during migration windows.
- Write new shape only after old readers are gone.
- Version externally consumed APIs when behavior cannot remain compatible.
- Keep unknown fields tolerated unless strictness is part of the contract.
- Document defaults and absent-field behavior.
Do not update only the producer or only the consumer unless the other side is proven unaffected.
Test the Boundary
Validate contracts at the boundary where consumers observe them:
- Contract tests for public APIs and events.
- Migration tests for persisted data.
- Round-trip tests for encode/decode or import/export.
- Compatibility fixtures for old payloads or schemas.
- Type or schema checks where the toolchain supports them.
Internal unit tests are not enough to prove a contract remains compatible.
Completion Criterion
The contract is named, consumers are considered, the change is classified, compatibility or migration handling is explicit for narrowing/breaking/semantic changes, both producer and consumer impact are checked, and validation covers the boundary observable by dependents.
1---2name: contracts3description: Contract-design principles for APIs, schemas, events, jobs, module exports, configuration, and other cross-boundary interfaces. Use when defining, changing, or reviewing a public interface or data shape that other code, users, services, jobs, or future versions may depend on.4---56# Contracts78Treat cross-boundary interfaces as promises that outlive the current edit.910## Identify the Contract1112Before changing a boundary, name the contract and its consumers:1314- HTTP, RPC, GraphQL, or CLI request and response.15- Database schema, migration output, or persisted data shape.16- Event, queue message, webhook, or background job payload.17- Public module export or package API.18- Config, environment variable, feature flag, or file format.19- UI route, URL shape, or externally visible behavior.2021If callers may exist outside the current repo, assume compatibility matters.2223## Classify the Change2425Classify contract changes before implementation:2627- **Additive**: adds optional fields, new endpoints, new enum values, or new capabilities without changing existing meaning.28- **Narrowing**: rejects inputs, removes states, tightens validation, or reduces accepted behavior.29- **Breaking**: removes, renames, retypes, reorders, or changes the meaning of existing fields or behavior.30- **Semantic**: keeps the shape but changes observable meaning.3132Additive changes are usually safest. Narrowing, breaking, and semantic changes require an explicit migration or compatibility plan.3334## Preserve Compatibility3536Prefer compatibility-preserving paths:3738- Add before removing.39- Read old and new shapes during migration windows.40- Write new shape only after old readers are gone.41- Version externally consumed APIs when behavior cannot remain compatible.42- Keep unknown fields tolerated unless strictness is part of the contract.43- Document defaults and absent-field behavior.4445Do not update only the producer or only the consumer unless the other side is proven unaffected.4647## Test the Boundary4849Validate contracts at the boundary where consumers observe them:5051- Contract tests for public APIs and events.52- Migration tests for persisted data.53- Round-trip tests for encode/decode or import/export.54- Compatibility fixtures for old payloads or schemas.55- Type or schema checks where the toolchain supports them.5657Internal unit tests are not enough to prove a contract remains compatible.5859## Completion Criterion6061The contract is named, consumers are considered, the change is classified, compatibility or migration handling is explicit for narrowing/breaking/semantic changes, both producer and consumer impact are checked, and validation covers the boundary observable by dependents.