When making a change to an API or Database Schema, by default most changes aren’t backwards compatible (safe) with previous client versions.

For example:

change safe for API Responses? safe for API Requests? safe for Database Schema?
add required field
prev clients ignore when deserializing

prev clients won’t send it

prev client won’t create with it
add optional field
prev clients ignore when deserializing

prev clients won’t send it

prev clients won’t create with it
rename field
prev clients expect prev name

prev clients send prev name

prev clients fetch/create prev name
delete field
prev clients expect field

prev clients send field

prev clients fetch/create prev field
change field type
prev clients expect prev field type

prev clients send prev field type

prev clients fetch/create prev field type
change null field to non-null
prev clients still deserialize

prev clients send null value

prev clients create with null
add variant to enum/union
prev clients get error deserializing

prev clients send subset

prev clients fetch not expecting variant
remove variant from enum/union
prev clients deserialize subset

prev clients send removed variant

prev clients send removed variant

Making Changes Compatible

add required field

  1. Make field nullable
  2. Update all clients to send value
  3. Make field required

rename field

  1. Create new field (synced to prev field, dual writes?)
  2. Update all clients to use new field
  3. Remove prev field

delete field

  1. Delete from all clients
  2. Remove field

change field type

  1. Create new field with new type (and synced, assuming field type change is compatible, i.e., varchar -> text)
  2. Update all clients to use new field
  3. Remove prev field

change null field to non-null

Add default for the field (for null case)

or

  1. Update all clients to send value
  2. Change field nullability

add variant to enum/union

  • Serialze enum/union as wider type, like a string instead of a union of string literals ("ok" | "error" | "pending")

or

remove variant from enum/union

  1. Create new field (synced to prev field but w/o new variant)
  2. Update all clients to use new field
  3. Remove prev field
  • openapi-diff check for breaking changes in Open API spec
  • apollo schema checks check for breaking changes in graphql schema
  • planetscale check for breaking MySQL schema changes using usage data
  • squawk check for breaking changes in Postgres schema
  • buf check for breaking changes in Protobuf

Conclusion

Be careful changing your API, there are some tools to make it easier!

In general, most API changes are multi-step (and can’t be made in a single PR).

  1. make setup change
  2. update all clients (takes a while)
  3. make desired change