Prompt and suitable context
This is a system-design question. The core is not choosing URL or header versioning; it is designing an API lifecycle control plane. A contract change should flow into impact analysis, real-consumer discovery, old/new behavior checks, staged migration, and evidence-based shutdown. Microsoft recommends preserving backward compatibility when possible, and Google Cloud similarly advises trying compatible evolution first. The interview asks you to turn those principles into a system that works across teams.
What the interviewer evaluates
- Whether you distinguish format compatibility, entity-semantic changes, and genuinely breaking changes.
- Whether you connect API contracts, a consumer inventory, runtime traffic, and migration work.
- Whether you design compatibility tests, gradual rollout, alerts, notices, and rollback rather than only version labels.
- Whether you can explain the operational cost of multiple versions, data-conversion risk, and organizational boundaries.
Clarifications to ask first
Confirm whether the API is internal, partner-facing, or public; whether consumers can be fully identified; and the traffic, latency, and availability targets. Clarify whether the field is optional, whether its meaning changes, and whether reads, writes, or persisted data are involved. Ask about an OpenAPI or other contract source, client SDKs, notice channels, support windows, compliance requirements, and rollback duration. If scale is unspecified, state your assumptions.
A 30-second answer framework
I would design five stages: contract registry, impact discovery, compatibility validation, migration coordination, and shutdown evidence. Every API contract enters a registry; a rule engine classifies changes, then combines static dependencies and runtime calls to identify consumers. Old and new versions run in parallel while consumers receive notices with a deadline and migration guide. During gradual rollout, record success, errors, and residual traffic by version. Shut down only after critical consumers migrate, migration evidence is complete, rollback is rehearsed, and an owner approves the change.
Step-by-step deep answer
1. Build a contract registry and change rules
The registry stores each API, version, owner, field semantics, authentication scope, support state, deprecation date, and migration documentation. A change request includes a contract diff, and the rule engine flags removed fields, narrowed enums, changed requiredness, altered error semantics, and entity relationship changes. Adding an ignorable field is often compatible, but clients cannot be assumed to ignore unknown fields correctly; teams need an evidence-backed exception path.
2. Discover consumers and build an impact graph
Combine repository dependencies, gateway logs, service-mesh telemetry, SDK registrations, and declarations into an API-version-consumer graph. Static discovery can miss dynamically constructed requests, while runtime discovery can miss rare jobs, so mark evidence source, last-seen time, and confidence. For external clients, retain only necessary tenant and application identifiers rather than treating logs as indefinite personal data.
3. Validate compatibility with safety gates
For each change, run contract tests, consumer replays, and sampled-traffic comparisons. Check response semantics on reads and verify that old-client writes do not lose data or create unintended side effects. Start breaking changes in shadow traffic or a small tenant cohort; version routing and feature flags must be reversible. Preserve the old version on failure instead of treating a passing test set as proof about untested consumers.
4. Coordinate notices, migration, and multi-version operation
A notice service sends migration guides, deadlines, example requests, and contacts based on consumer, severity, and support contract; external customers need a queryable status page or console. Migration work records an owner, blockers, validation evidence, and the last successful call. Multiple versions add testing, deployment, and monitoring cost, so set a version cap, deprecation stages, and a migration path instead of supporting every version indefinitely.
5. Decide shutdown, observe, and recover
Before shutdown, verify critical consumers upgraded, residual calls below threshold, no error regression, reversible data conversion, and support readiness. Use cohorts and time windows: stop new onboarding, return a clear deprecation error and migration link to old calls, then disable routing. Track compatibility failures, old-version traffic, migration completion, notice delivery, rollback count, and consumer-segmented errors. Kubernetes shows how stability levels, minimum support periods, conversion, and rollback constraints can be explicit policy; configure such constraints for the organization rather than copying its dates universally.
High-quality sample answer
I would clarify consumer types, the contract source, read/write semantics of the field being removed, external notice obligations, and rollback duration. A contract registry feeds a change-rule engine that identifies breaking differences and links them to an impact graph built from code dependencies, gateway logs, and service-mesh telemetry, with evidence freshness. Compatibility tests, consumer replays, and gradual traffic validate old and new behavior; a notice service sends each consumer a migration guide and deadline. Versions run in parallel, and migration work stores validation evidence. Only after critical consumers migrate, residual traffic and errors meet thresholds, rollback is rehearsed, and an owner approves, do we retire the old route. Audit logs cover changes, notices, observations, and rollback so unknown clients are not cut off at once.
Common mistakes
- Debating URL, header, or semantic-version labels without consumer discovery and shutdown evidence.
- Relying only on static code search or one access log while missing dynamic and rare calls.
- Assuming removing an optional field is automatically safe without testing real client parsing.
- Launching the new version and immediately closing the old one without gradual rollout or rollback.
- Supporting every version forever without pricing testing, monitoring, and conversion cost.
- Sending one email without a deadline, owner, escalation path, or segmented metrics.
Follow-up questions and responses
How do you decide to shut down when an external client cannot be found?
Treat unknown usage as a risk state, extend the observation window, increase telemetry, and contact the contractual owner or offer a migration diagnostic. Zero logs does not prove zero use; use a recoverable rejection policy and emergency fallback before shutdown.
Is adding a response field always compatible?
No. The rules may classify it as usually compatible, but real consumer replays, SDK matrices, and error samples still need to validate it. Strict schema validators may require a separate migration or version boundary.
How do you migrate data written by old clients safely?
Define semantic mappings and no-loss conditions first, then use dual reads, dual writes, or offline conversion with version records. Conversion must be verifiable and reversible; do not silently change business meaning at read time.
What if a customer cannot upgrade by the deadline?
Offer a limited compatibility window, adapter, or assisted migration according to contract and risk, recording cost, owner, and a new exit date. Exceptions should reduce unknown traffic rather than make the old version permanent.
How do you avoid making the control plane a single point of failure?
Data-plane routing should not require a live control-plane write. Cache approved version policy and keep the last safe configuration during control-plane failure. Registry, notice, and metric services can recover asynchronously, while shutdown requires dual approval and explicit rollback.