API versioning
Build defensively against additive evolution - parsers that ignore unknowns, enum fallbacks, version pinning
The API promise is the contract: Octopus Cards won't break v1. This page is the technique — the integration patterns that absorb additive changes (new fields, new enum values, new endpoints) without breaking.
Defensive parsing
The single most important habit: the parser must ignore unknown fields. When Octopus Cards adds refund_reason to an order response, the integration shouldn't care.
Go: encoding/json already does this. No configuration needed.
TypeScript / JavaScript: destructure only the fields actually needed; don't use Object.keys(…).forEach with a closed set.
// ✓ Resilient
const { id, status, vouchers } = response.data;
// ✗ Brittle - breaks on every new field
const ALLOWED = ['id', 'status', 'vouchers'];
for (const k of Object.keys(response.data)) {
if (!ALLOWED.includes(k)) throw new Error(`Unexpected field ${k}`);
}Python: pydantic models with model_config = ConfigDict(extra='ignore') (or Pydantic v1's Config.extra = 'ignore'). Default dataclasses.dataclass will reject unknowns — opt into loose mode.
Java/Kotlin: Jackson with @JsonIgnoreProperties(ignoreUnknown = true). It is not the default.
Enums & error codes - handle the unknown
Same principle, applied to strings that look like enums:
// ✓ Resilient - unknown status maps to a conservative default
func classify(status string) Outcome {
switch status {
case "DELIVERED": return Delivered
case "PENDING": return Pending
case "FAILED": return Failed
default: return Unknown // log it, don't crash
}
}
// ✗ Brittle - new status = panic
func classify(status string) Outcome {
return map[string]Outcome{
"DELIVERED": Delivered,
"PENDING": Pending,
"FAILED": Failed,
}[status]
}New statuses, new error codes, new event types — treat all of them as "log and fall through to a safe default." If a value was truly semantic, it will have been announced first.
Pinning & testing
- Pin the path in code, not in a runtime config file.
/api/v1/ordersshould be a constant in the SDK layer. - Run the test suite against sandbox regularly. Sandbox receives additive changes first; nightly CI catches compatibility issues before they reach production.
- Record sample payloads. When Octopus Cards adds a new optional field, golden-file snapshots should update cleanly, not break.
Summary
- Ignore unknown fields in JSON responses (default in Go; explicit configuration in TS/Python/Java).
- Treat unknown enum values, error codes, and event types as "log and fall through to safe default."
- Pin
/api/v1/in a single constant, not at every call site. - Run nightly tests against sandbox.
These four habits are the entire integration cost of the API promise.