API Design 3 min read

API Versioning Without Breaking Enterprise Clients

Table of contents

    Consumer apps update weekly. Enterprise integrations are written once, by a contractor whose engagement ended in 2021, and are expected to run untouched for a decade. If you sell APIs to enterprises, your versioning policy is not a technical detail - it is part of the product contract.

    After maintaining public APIs for KID and FormOK through years of evolution, this is the policy we converged on.

    First, classify the change

    Most versioning debates conflate three very different kinds of change. We classify every API change before deciding how to ship it:

    Additive changes - new endpoints, new optional request fields, new response fields. These ship to all versions immediately, no announcement required. The contract this imposes on clients is documented loudly: you must ignore response fields you do not recognize. We test this expectation in our client SDKs and state it in the integration guide's first page.

    Behavioral changes - same schema, different semantics. A validation that tightens, a default that changes, an error code that becomes more specific. These are the dangerous ones, because no schema diff catches them. They get a version gate.

    Breaking changes - removed fields, changed types, restructured resources. Version gate, migration guide, and the full deprecation clock.

    The discipline that matters most: behavioral changes are treated as breaking. Every painful integration incident we have seen in consulting engagements came from a change the provider considered "minor cleanup" and the client's decade-old integration considered load-bearing.

    Pin versions per account, not per request

    We put the version in the URL path for visibility, but the effective version is pinned per API credential at creation time. A request without explicit version intent gets the credential's pinned version - forever, until the account owner changes it.

    This has a consequence we consider a feature: upgrades are a deliberate act by the customer, performed in a dashboard, by a person who can read the changelog. Nobody gets upgraded by accident because a request header was dropped by a proxy.

    The deprecation clock

    Our published policy:

    • Deprecated versions keep working for 12 months after the deprecation announcement
    • Announcements go out by email to registered technical contacts, in the dashboard, and as Deprecation and Sunset HTTP response headers on every affected call
    • Three months before sunset, we send each affected account a usage report: which endpoints, which credentials, how many calls

    The response headers matter more than the emails. Emails reach whoever manages the account; headers reach the monitoring systems of the team that owns the integration. Enterprise contacts change, log pipelines are forever.

    HTTP/1.1 200 OK
    Deprecation: true
    Sunset: Sat, 14 Nov 2026 00:00:00 GMT
    Link: <https://kodama.com/docs/api/migrations/v2>; rel="deprecation"

    Contract tests are the enforcement mechanism

    Policy without enforcement drifts. Every response schema we have ever shipped is captured as a versioned contract fixture, and CI replays current handlers against old fixtures. A pull request that changes a v1 response shape fails the build - the engineer finds out at review time, not from a customer escalation.

    What we tell clients who are building on us

    1. Ignore unknown fields. This is the whole additive-change bargain.
    2. Alert on the Sunset header in your API client, today, before you need it.
    3. Put the API version in your credential inventory, so "what will break next November" is a query, not an archaeology project.

    Versioning policy is trust infrastructure. Enterprises do not expect you to never change; they expect to never be surprised. Those are different promises, and only the second one is keepable.

    Copied