Document API Contracts & Versioning
The 3-Line Engineering Rule
Treat APIs as immutable public contracts; maintain authoritative OpenAPI specifications and enforce strict backward compatibility.
Renaming a field or changing a data type in an undocumented API silently breaks mobile apps and partner integrations, requiring emergency rollbacks.
Use OpenAPI 3.1 / Swagger as the single source of truth. Apply additive changes only (never remove or rename existing fields). Use URL path versioning (`/v1`, `/v2`) for breaking paradigm shifts.
Architectural Blueprint & Sequence
Hover over nodes to inspect state transitionsOpenAPI 3.1 Spec
openapi.yaml in git
CI Breaking Check
oasdiff detects breaking changes
Server Stubs
Generated Java/TS interfaces
Client SDKs
iOS, Android & Frontend clients
Interactive Docs
Swagger UI / Redoc
Key Engineering Takeaways
- OpenAPI spec is the contract: generate client SDKs, server stubs, and mock servers from it.
- Additive changes are safe: adding an optional field does not break existing clients.
- Renaming or removing a field is a breaking change; deprecate for 6-12 months before deletion.
- Enforce automated contract compatibility checks in CI (e.g. `oasdiff` or Spectral).
The Problem & Production Reality
An API is a binding promise to consumers. Mobile clients cannot be forced to update immediately; users may run older app versions for years. Writing and validating API contracts using OpenAPI ensures seamless cross-team communication and prevents surprise breaking changes.
Anti-Pattern: Silent Breaking Changes in Unversioned Endpoints
Renaming `customer_id` to `userId` or changing an integer timestamp to an ISO string without versioning.
// ❌ ANTI-PATTERN: Breaks all existing mobile clients on deploy!
// Yesterday: { "orderId": "101", "createdTimestamp": 1718900000 }
// Today:
public record OrderResponse(
String id, // ❌ Renamed from orderId! Mobile app crashes on null pointer!
Instant createdAt // ❌ Changed data type from long to string!
) {}Production Pattern: Additive-Only Evolution & OpenAPI Annotations
Keep existing fields intact, add new fields as optional, and document schemas with clear OpenAPI definitions.
// ✅ PRODUCTION PATTERN: Additive Schema Evolution & OpenAPI
@Schema(description = "Customer order response model")
public record OrderResponse(
@Schema(description = "Unique order identifier", example = "ord_8f9a2b")
String orderId,
@Schema(description = "Total amount in cents", example = "4999")
long amountCents,
@Deprecated
@Schema(description = "Legacy epoch timestamp (use createdAtUtc instead)", deprecated = true)
Long createdTimestamp,
// Additive non-breaking new field:
@Schema(description = "ISO-8601 formatted creation timestamp", example = "2026-10-23T12:00:00Z")
Instant createdAtUtc
) {}Trade-off Analysis & Staff Recommendations
| Aspect | Pros / Strengths | Cons / Hazards | Staff Recommendation |
|---|---|---|---|
| URL Path Versioning vs Header Versioning | URL path (`/v1/orders` -> `/v2/orders`) is explicitly visible in browser bars and CDN caching. | Header versioning (`Accept: application/vnd.company.v2+json`) keeps clean URLs. | Use URL path versioning (`/v1/`) for simplicity and transparent CDN cache separation. |
| Contract-First vs Code-First | Contract-first allows frontend and mobile teams to mock APIs before backend writes a line of code. | Requires editing YAML/JSON specs manually or with visual tools. | Use Contract-First for public APIs; Code-First with SpringDoc for internal rapid microservices. |