Versioning your APIs
Change a contract without breaking the callers you cannot redeploy at the same moment.
Open this lesson in the learning hubKey points
- You never deploy everything at once. Old and new callers run side by side during every rollout, so plan for both.
- Prefer additive changes. A new optional field breaks nobody; removing or renaming one breaks everybody.
- Consumers must ignore unknown fields. Spring Boot disables
FAIL_ON_UNKNOWN_PROPERTIESfor you - plain Jackson enables it, so leave Boot alone. - When you must break, run
/v1and/v2side by side and publish a sunset date you actually enforce. - Events need the same discipline. Use a schema registry (Avro or Protobuf) and only make backward-compatible schema changes.
- Spring Framework 7 (Spring Boot 4) adds built-in API version routing, worth a look if you are already on it.
Example
// Additive change: v2 adds fields, v1 keeps its exact old shape.
record OrderV1(String id, String status, long totalCents) {}
record OrderV2(String id, String status, long totalCents, String currency, Instant placedAt) {}
@RestController
@RequestMapping("/v1/orders")
class OrderControllerV1 {
@GetMapping("/{id}")
ResponseEntity<OrderV1> get(@PathVariable String id) {
return ResponseEntity.ok()
// RFC 8594: tell clients exactly when this endpoint dies.
.header("Sunset", "Wed, 31 Dec 2026 23:59:59 GMT")
.body(orders.viewV1(id));
}
}
@RestController
@RequestMapping("/v2/orders")
class OrderControllerV2 {
@GetMapping("/{id}")
OrderV2 get(@PathVariable String id) {
return orders.viewV2(id);
}
}
Add fields freely, remove them slowly, and never break a caller without a dated warning.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Microservices course, and every lesson in it is listed on the Microservices contents page.