1.Versioning APIs and events carefully
An API or an event schema is a contract between whoever produces it and every consumer that reads it, and unlike a function inside one codebase, that contract cannot be changed and redeployed everywhere at once — a mobile app version from six months ago, a partner's integration, and an internal service that has not been redeployed yet may all still be calling the old contract long after a new one ships. Versioning is the discipline of managing that reality deliberately rather than assuming every consumer upgrades in lockstep with the producer. The safest changes are additive and backward-compatible — adding a new optional field, adding a new endpoint — which every existing consumer can simply ignore without being broken. A breaking change (removing a field, changing a field's type or meaning, changing required parameters) needs an explicit versioning strategy: a new version number in the URL or a header, coexisting for a defined deprecation window with the old version, so consumers migrate on their own schedule rather than breaking the instant the change ships. The same discipline applies to events published onto a queue or log (Kafka, SQS): a consumer reading an event schema has the same lockstep problem as an API caller, except often with less visibility into who all the consumers even are, since a queue or topic can have consumers a producer does not know about at all.
Key features
- Additive, backward-compatible changes (new optional fields, new endpoints) do not need a version bump — every existing consumer can ignore what it does not recognize
- A breaking change (removing/renaming a field, changing its type or required-ness, changing an endpoint's semantics) needs an explicit version strategy — a new API version, or a new event schema version — with the old one still served during a deprecation window
- Common API versioning strategies are a version segment in the URL (/v2/orders), a version header, or content negotiation — the choice matters less than picking one and applying it consistently
- Events have the same lockstep problem as APIs but often worse visibility: a topic or queue can have consumers the producer does not know about, so "just tell everyone about the breaking change" is not always possible the way it might be for a documented, registered set of API clients
- A deprecation window (a defined period where both the old and new version are served) is what actually makes a breaking change safe — a breaking change shipped with no old version served at all forces every consumer to migrate at the exact moment the producer chooses, not the moment each consumer is ready
- Semantic versioning conventions (major.minor.patch) map cleanly onto this: a major version bump signals a breaking change, a minor bump signals a backward-compatible addition — communicating the nature of a change, not just that a change happened
Change type, and whether it needs a version bump
- T+0: v2 released — v1 continues unchanged; both live side by side
- T+0 to T+90d: Deprecation window — consumers migrate to v2 on their own schedule
- T+60d: Sunset warning — v1 responses include a deprecation header
- T+90d: v1 retired — only after the documented window elapses
Command
# Two APIs live at once during a deprecation window
GET /v1/orders/42
{"id": 42, "total": 59.99}
GET /v2/orders/42
{"id": 42, "total": {"amount": 5999, "currency": "USD"}}
# v1 kept serving unchanged until its documented
# sunset date; v2 is where the breaking change
# (total's type changed from a plain number to an
# object) actually livesAdditive, backward-compatible changes need no version bump — existing consumers can ignore what they do not recognize. A breaking change needs an explicit new version, served alongside the old one for a defined deprecation window, so every consumer — including the ones a producer does not have full visibility into — migrates on its own schedule rather than breaking the instant the change ships. This applies to events on a queue or log exactly as much as to a request/response API.