GuidesBest Practices

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/orders should 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.

On this page