API Versioning

15 questions found

How does GraphQL's approach to API evolution differ from REST's typical versioning strategies?

Intermediate
GraphQL generally avoids traditional versioning entirely, favoring a single, continuously evolving schema where new fields are added (additive, non-breaking) and old fields are marked @deprecated with a reason rather than removed immediately, since clients only request the specific fields they need -- unused deprecated fields simply don't affect clients that never query them, unlike REST where an entire response payload shape typically changes at once.
type Product {
  id: ID!
  name: String!
  price: Float! @deprecated(reason: "Use priceWithCurrency instead")
  priceWithCurrency: Money!
}

# Old clients querying 'price' keep working;
# new clients migrate to priceWithCurrency at their own pace
Real-world example A GraphQL API deprecates a poorly-designed field for over a year, monitoring query analytics to confirm zero remaining clients still request it before finally removing it from the schema entirely, without ever needing a 'v2' schema.

Common follow-ups: How do you track which clients still use a deprecated GraphQL field?;What are GraphQL's own versioning challenges (breaking changes to required fields)?

RESTful Web APIs & Controllers;gRPC Services

How do you handle database schema changes that need to support multiple concurrently deployed API versions during a migration window?

Advanced
Common patterns include: additive-only migrations (add new columns/tables without removing old ones until all versions are retired), expand-contract migrations (first 'expand' the schema to support both old and new shapes, deploy code that writes to both, then 'contract' by removing the old shape only after all consumers migrate), and maintaining a translation/mapping layer in application code so different API version handlers read/write the same underlying schema through different DTOs.
-- Expand-contract pattern:
-- Step 1 (expand): ALTER TABLE Products ADD COLUMN currency_code VARCHAR(3);
-- Step 2: deploy code where v2 writes currency_code, v1 continues ignoring it
-- Step 3 (contract, after v1 retirement): safely remove any v1-only compatibility columns
Real-world example A team migrating a pricing field from a single decimal to a currency-aware structure uses expand-contract: adding new columns first, running both API versions against the same expanded schema, then cleaning up only after v1 is fully retired.

Common follow-ups: What's the risk of skipping the expand-contract pattern and doing a direct schema change?;How long should the 'expand' phase typically last?

Entity Framework Core & Data Access;CI/CD Publishing & Deployment

What HTTP status code and response should an API return when a client requests a version that no longer exists or was never supported?

Intermediate
A commonly recommended approach returns 400 Bad Request (for a malformed/unsupported version format) or 410 Gone (for a version that existed but was intentionally retired), along with a clear, structured error body indicating supported version options -- this is more actionable for API consumers than a generic 404 Not Found, which could be confused with a missing resource rather than an unsupported version.
// Requesting a retired version
// GET /api/v1/products  (v1 fully removed)
// HTTP 410 Gone
{
  "error": "api_version_retired",
  "message": "API v1 was retired on 2025-06-01. Please migrate to v2.",
  "supportedVersions": ["2.0", "3.0"]
}
Real-world example A payments API returns a structured 410 Gone response with a link to migration documentation whenever a client hits a fully retired version, reducing confused support tickets compared to a bare 404.

Common follow-ups: Why is 410 Gone more informative than 404 for this case?;Should the error body format itself be versioned?

Global Exception Handling & Middleware;RESTful Web APIs & Controllers

How does API versioning strategy differ for internal microservice-to-microservice APIs versus public-facing external APIs?

Advanced
Internal APIs, where the team controls both producer and consumer and can coordinate deployments, often favor looser versioning (or none at all) relying on contract testing and coordinated rollouts, sometimes accepting brief compatibility windows during deploys. Public external APIs, where consumers are unknown, numerous, and can't be coordinated with directly, require much stricter, longer-lived versioning with generous deprecation windows and strong backward-compatibility guarantees, since a breaking change can silently fail for consumers you don't even know about.
// Internal service-to-service: looser, coordinated deploys
// Both OrderService and InventoryService deploy together, contract tests catch mismatches in CI

// Public external API: strict versioning, long deprecation windows
// /api/v1 supported for 12+ months after v2 launches, with proactive client communication
Real-world example A company's internal order-processing microservices skip formal API versioning entirely, relying on consumer-driven contract tests in CI to catch breaking changes before deployment, while their public partner API maintains three concurrent versions with a strict 18-month deprecation policy.

Common follow-ups: What is consumer-driven contract testing (e.g., Pact)?;How do service meshes help manage internal API compatibility during rollouts?

Microservices & Distributed Architecture Patterns;CI/CD Publishing & Deployment

How can feature flags be used as an alternative or complement to formal API versioning for rolling out changes gradually?

Intermediate
Feature flags let you toggle new behavior on or off for specific clients, percentages of traffic, or environments without deploying a formally versioned endpoint, enabling gradual rollout, A/B testing, and instant rollback of a change -- useful for behavior changes that don't need a permanent parallel version, though for changes to the wire contract itself, formal versioning is usually still needed since flags control behavior, not necessarily backward-incompatible schema shape.
if (featureManager.IsEnabled("NewPricingCalculation", context: request.UserId)) {
    return CalculateWithNewLogic(order);
} else {
    return CalculateWithLegacyLogic(order);
}
// No new API version needed -- same endpoint, gradually shifting behavior
Real-world example A team rolls out a new tax calculation algorithm to 5% of API traffic via a feature flag, monitoring for discrepancies before gradually increasing to 100%, entirely without introducing a new API version.

Common follow-ups: When does a behavior change actually require a new API version versus just a feature flag?;How do you clean up feature flags after a rollout completes?

CI/CD Publishing & Deployment;RESTful Web APIs & Controllers

Showing 11–15 of 15