Sobes.tech
Middle

How to safely evolve contracts between services?

sobes.tech AI

Answer from AI

Secure evolution of contracts between services is achieved through principles of backward compatibility and gradual implementation of changes. Main approaches:

  • Versioning of contracts: adding a new version of the API or schema, while old versions continue to work.
  • Adding new fields as optional: not deleting or changing existing fields, but expanding the contract with new optional parameters.
  • Using schemas supporting evolution (e.g., Avro, Protobuf), which allow adding new fields without breaking old clients.
  • Agreements on behavior when new fields are absent: clients should handle the absence of new data correctly.
  • Compatibility testing: automated tests to ensure that new versions do not break old clients.

Example using JSON Schema, where a new optional field is added:

from jsonschema import validate, ValidationError

schema_v1 = {
    "type": "object",
    "properties": {
        "id": {"type": "integer"},
        "name": {"type": "string"}
    },
    "required": ["id", "name"]
}

schema_v2 = {
    "type": "object",
    "properties": {
        "id": {"type": "integer"},
        "name": {"type": "string"},
        "email": {"type": "string"}  # new optional field
    },
    "required": ["id", "name"]
}

# Clients supporting v1 will be able to ignore the email field if it exists

try:
    validate(instance={"id": 1, "name": "Alice"}, schema=schema_v2)  # valid
    validate(instance={"id": 2, "name": "Bob", "email": "bob@example.com"}, schema=schema_v2)  # valid
except ValidationError as e:
    print(f"Validation error: {e}")