An API Deprecation Needs Evidence That Consumers Have Moved

Published · Programming

Changing an API is easy when every caller ships in the same deployment. It gets harder when clients update on their own schedules, an integration belongs to another company, or an old mobile app can keep making requests for months. An announcement and a new endpoint do not make those consumers disappear.

An API deprecation is a promise about a transition. The replacement must be usable, the old interface must remain supported for the agreed window, and the eventual removal decision needs evidence that the important consumers have moved. That evidence is more than a graph with a downward slope.

This is narrower than retiring a developer tool. An API may have callers you cannot search, patch, or schedule. The work is to make their dependency visible enough to act on without pretending you control their release process.

Define the interface and replacement precisely

Name what is actually going away: a versioned endpoint, request field, response field, event schema, authentication method, or behavior. “The old API” is too vague to support a migration plan. A client may use a supported endpoint but depend on a default value that changes in the replacement.

For each affected surface, write down the old contract, the replacement, and the compatibility differences. A useful migration note includes sample requests and responses, changed errors, pagination and ordering behavior, rate limits, authentication scopes, and any data that must be transformed. Identify whether the old and new versions can run side by side for one consumer.

Do not announce a retirement clock before the replacement is operational for the consumers you expect to move. If a new endpoint lacks a required field or imposes a different consistency guarantee, those clients have a product gap, not merely migration reluctance.

Build a consumer inventory with honest coverage

Start with API gateway logs and authentication identities, but do not stop there. A token may represent several applications, and a proxy may hide the real caller. Conversely, a source search can find dead code that never makes a request.

Combine signals that answer different questions:

Signal What it establishes What it misses
Versioned request telemetry Calls observed during the retention window Rare workflows and calls outside that window
Credential or tenant mapping A contact or account associated with requests Multiple applications behind one identity
SDK and repository search References in code you can inspect Deployed versions and external clients
Partner or owner confirmation Intended release schedule and critical workflows Forgotten scripts and untested paths
Support and incident records Consumers that previously depended on behavior Consumers that have never filed a ticket

Record the observation period, environments, log retention, and known blind spots. A zero in a dashboard is meaningful only if the dashboard includes the traffic you care about. Include background jobs, disaster-recovery procedures, seasonal integrations, and mobile releases whose adoption lags the server rollout.

For internal consumers, name an owning team and deployment. For external consumers, map requests to a tenant or integration owner when possible and give that party a contact path. Avoid putting user identifiers or request payloads into an editorial-style migration spreadsheet; the inventory needs ownership and status, not unnecessary private data.

Measure use of the deprecated surface

Instrument the old version at the boundary where requests arrive. Count requests by version and operation, success or error outcome, client identity where permitted, and enough environment context to distinguish tests from production. Watch both absolute calls and the set of distinct active consumers. A high-volume client that migrated can make request volume collapse while several low-volume clients remain.

The Kubernetes API deprecation policy is a concrete public example. Deprecated REST API requests receive warnings, audit annotations, and a metric that can be paired with request counts. Its migration guide also describes locating use through warnings, metrics, and audit information. The transferable practice is to expose deprecation at the point of use and give operators a way to find real callers; your API does not need to copy Kubernetes' exact version policy.

Warnings help only if the consumer sees them. Server logs, response headers, SDK warnings, and direct owner notices reach different people. Put the planned removal date or decision window, replacement documentation, and support route where the affected team can find them. Keep the message specific enough that a consumer can tell whether its code is affected.

Verify the replacement, not just the absence of old traffic

An old request can vanish because the client migrated, stopped running, lost credentials, or broke before reaching the server. Pair falling old-version usage with evidence that the intended workflow succeeds on the replacement.

For an internal client, that may mean a deployed version identifier, a contract test, and an observed production request through the new path. For a partner integration, ask the owner to run a representative transaction and confirm the result. For a mobile client, inspect adoption of the new app version and retain the support window for older installed versions under the product's policy.

Test semantic changes, not merely HTTP status codes. Suppose /v1/orders returned records in creation order and /v2/orders requires an explicit sort key. A client that receives 200 OK but silently picks the wrong “latest” order has not completed its migration. Compare representative data, failure handling, permissions, and rollback behavior. Keep any test fixtures free of customer secrets.

Track each consumer in a small ledger: owner, old surface used, replacement version, verification evidence, last observed old request, blocker, and next action. The ledger should make the final exceptions visible; it should not become another permanent service with nobody responsible for maintaining it.

Set removal criteria before the deadline

A calendar date is a coordination tool, not proof of safety. Agree on the conditions that must be true to stop serving the old version:

  1. The replacement covers documented use cases and its changed behavior is published.
  2. Known production consumers have an owner and a verified migration or explicit exception.
  3. The observation window covers the relevant release and business cycles.
  4. New clients and generated SDKs no longer choose the deprecated version by default.
  5. Remaining old traffic is understood rather than dismissed as noise.
  6. Removal, monitoring, escalation, and recovery have named operators.

Some APIs have a contractual minimum support period, published version policy, or third-party obligations. Apply those before selecting a date. A quiet month cannot justify removing an interface used only for a quarterly export. Conversely, an obsolete test client should not indefinitely hold a production version open once it has an owner and a removal action.

If telemetry is incomplete, write down what substitutes for it: owner attestations, synthetic exercises, release records, or a longer observation window. Be explicit about the remaining uncertainty. “No calls observed” is a finding; “all consumers migrated” is a stronger claim.

Remove in a controlled sequence

Before shutdown, stop creating new dependencies through examples, SDK defaults, templates, and onboarding docs. Exercise the removal in a staging environment with affected clients. If possible, reject a known test identity or disable the old route during a staffed window before making the change permanent.

Monitor both unexpected requests to the old version and failure signals on the replacement. Decide in advance what would justify restoring the old path, who can authorize that, and how long restoration is allowed. Rollback may be impossible if the new version writes data the old version cannot read; test that boundary rather than assuming a router toggle solves it. This is the same distinction explored in rollback plans that account for changed production state.

After the window, remove obsolete credentials, routes, SDK options, and documentation that still advertise the old interface. Preserve only the records and compatibility artifacts that have a real owner and retention reason.

The useful question is not “Did we announce deprecation?” It is “Which consumers still depend on this contract, and what proves they can perform their work without it?” Answer that, and removal becomes an engineering decision instead of a bet on silence.

Slaptijack's Koding Kraken