Loading learning content...
Versioning isn't complete without a plan for what happens to old versions. Deprecation is the formal process of signaling that a version (or feature) is no longer recommended and will eventually be removed.
Done poorly, deprecation causes panic, frustration, and broken integrations. Done well, it enables graceful transitions where consumers migrate on their own timeline within reasonable bounds.
This page covers the complete deprecation lifecycle:
The goal is to give consumers every opportunity to migrate while maintaining a sustainable burden on your team.
At its core, deprecation is a communication challenge. You're telling consumers: 'This is going away. Here's when, here's why, and here's how to move forward.' Every aspect of deprecation should be evaluated through this lens: does this help consumers understand and act on the transition?
A well-managed deprecation follows a predictable lifecycle with clear phases:
Phase 1: Announcement
Publicly communicate that a version or feature is deprecated:
Phase 2: Active Deprecation Period
The version continues to work but is discouraged:
Phase 3: Sunset Warning
As the end approaches, increase urgency:
Phase 4: Sunset (Removal)
The version stops working:
Phase 5: Post-Sunset
After removal:
| Phase | Timeline | Key Actions |
|---|---|---|
| Announcement | Day 0 | Blog post, email blast, documentation update, API version page marked deprecated |
| Active Deprecation | Months 1-6 | Warning headers in responses, migration guides published, new signups blocked |
| Sunset Warning | Month 6-9 | Sunset date published, direct outreach to top 100 consumers, weekly reminder emails |
| Final Warning | Month 9-12 | Critical severity warnings, daily emails, dashboard alerts, sunset countdown |
| Sunset | Month 12 | API returns 410 Gone with migration message |
| Post-Sunset | Months 13-18 | Maintain informative error responses, archived docs, traffic monitoring |
Industry best practice suggests a minimum 12-month deprecation period for major versions, though this varies by API type. Enterprise APIs may require 24+ months. Fast-moving platforms might use 6 months. Whatever you choose, communicate it clearly and honor it consistently.
Effective deprecation requires multi-channel, repeated communication. Consumers miss messages, priorities shift, and migration work gets delayed. Your communication strategy must account for this reality.
Channels to Utilize:
Documentation:
In-API Signals:
Direct Outreach:
Public Channels:
The Rule of Seven:
Marketing wisdom suggests people need to encounter a message seven times before acting. For deprecation, expect consumers to need multiple reminders across multiple channels before they prioritize migration work.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
# Example: Deprecation Blog Post ## API v1 Deprecation Announcement **Effective immediately, API v1 is deprecated.** ### Timeline- **Today**: v1 is marked deprecated; v2 is the recommended version- **September 1, 2024**: v1 will enter sunset warning phase- **December 31, 2024**: v1 will be permanently retired ### What This Means for YouIf you're currently using API v1:1. Your integration will continue to work until December 31, 20242. You'll see deprecation headers in responses starting today3. We recommend beginning migration to v2 as soon as possible ### Why We're Deprecating v1- v2 offers 40% better performance- Improved security model with OAuth 2.0- Consistent error formats aligned with industry standards- Foundation for upcoming features only available in v2 ### Migration Resources- [v1 to v2 Migration Guide](/docs/migration/v1-to-v2)- [Breaking Changes Summary](/docs/v2/breaking-changes)- [Code Examples in 10 Languages](/docs/migration/examples)- [Migration Office Hours](/events/migration-support) - Weekly Q&A ### Need Help?- Email: api-migration@example.com- Slack: #api-migration-support- Enterprise customers: Contact your account manager --- # Example: Email to Developers Subject: Action Required: API v1 Sunset December 31, 2024 Dear Developer, Our records indicate you're using API v1, which will be retired on December 31, 2024. **What you need to do:**Migrate to API v2 before December 31, 2024 to avoid service interruption. **Resources to help:**• Migration Guide: [link]• Office Hours: Every Tuesday 2pm PT• Support: api-migration@example.com **Your current v1 usage:**• Endpoints: /users, /orders, /products• Approximate daily calls: 45,000• First v1 request: January 15, 2022 We're here to help make this transition smooth. Reply to this email with any questions. Best regards,The API TeamGeneric deprecation notices get ignored. Where possible, personalize: 'You're using endpoints X, Y, Z in v1. Here's specifically what changes for those endpoints in v2.' High-volume consumers may warrant individual meetings.
Beyond documentation and email, your API itself should signal deprecation programmatically. This reaches developers where they're most likely to notice—in their logs, monitoring, and integration code.
HTTP Deprecation Headers:
The Deprecation HTTP header is a proposed standard (RFC draft) specifically for this purpose:
Deprecation: true
Deprecation: Sun, 31 Dec 2024 23:59:59 GMT
The Sunset header indicates when the resource will become unavailable:
Sunset: Sun, 31 Dec 2024 23:59:59 GMT
Link Headers for Migration:
Use Link headers to point to migration resources:
Link: <https://docs.example.com/deprecation/v1>; rel="deprecation"
Link: <https://api.example.com/v2/users>; rel="successor-version"
Warning Header:
The standard Warning header can also convey deprecation:
Warning: 299 - "API v1 is deprecated. Migrate to v2 by 2024-12-31."
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
// Express.js Deprecation Middlewareimport express from 'express'; interface DeprecationConfig { version: string; deprecationDate: Date; sunsetDate: Date; successorVersion: string; migrationGuideUrl: string;} const deprecationConfigs: Record<string, DeprecationConfig> = { 'v1': { version: 'v1', deprecationDate: new Date('2024-01-01'), sunsetDate: new Date('2024-12-31'), successorVersion: 'v2', migrationGuideUrl: 'https://docs.example.com/migration/v1-to-v2', },}; function deprecationMiddleware(apiVersion: string) { return (req: express.Request, res: express.Response, next: express.NextFunction) => { const config = deprecationConfigs[apiVersion]; if (!config) { return next(); // Not deprecated } const now = new Date(); // Check if already past sunset if (now > config.sunsetDate) { return res.status(410).json({ error: 'GONE', message: `API ${config.version} was retired on ${config.sunsetDate.toISOString().split('T')[0]}. Please migrate to ${config.successorVersion}.`, migrationGuide: config.migrationGuideUrl, successorVersion: config.successorVersion, }); } // Add deprecation headers res.set({ 'Deprecation': config.deprecationDate.toUTCString(), 'Sunset': config.sunsetDate.toUTCString(), 'Link': [ `<${config.migrationGuideUrl}>; rel="deprecation"`, `</${config.successorVersion}${req.path}>; rel="successor-version"`, ].join(', '), }); // Calculate days until sunset for warning severity const daysUntilSunset = Math.ceil( (config.sunsetDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24) ); let warningSeverity: string; if (daysUntilSunset <= 7) { warningSeverity = 'CRITICAL'; } else if (daysUntilSunset <= 30) { warningSeverity = 'HIGH'; } else if (daysUntilSunset <= 90) { warningSeverity = 'MEDIUM'; } else { warningSeverity = 'LOW'; } res.set('Warning', `299 - "${warningSeverity}: API ${config.version} sunset in ${daysUntilSunset} days. Migrate to ${config.successorVersion}."` ); // Add deprecation notice to response body for JSON responses res.locals.deprecationNotice = { deprecated: true, message: `API ${config.version} is deprecated and will be retired on ${config.sunsetDate.toISOString().split('T')[0]}`, sunsetDate: config.sunsetDate.toISOString(), daysRemaining: daysUntilSunset, severity: warningSeverity, successorVersion: config.successorVersion, migrationGuide: config.migrationGuideUrl, }; next(); };} // Apply to v1 routesconst v1Router = express.Router();v1Router.use(deprecationMiddleware('v1')); v1Router.get('/users/:id', (req, res) => { const userData = { id: req.params.id, name: 'John Doe' }; // Include deprecation notice in response res.json({ data: userData, _deprecation: res.locals.deprecationNotice, });}); // Example response:// {// "data": { "id": "123", "name": "John Doe" },// "_deprecation": {// "deprecated": true,// "message": "API v1 is deprecated and will be retired on 2024-12-31",// "sunsetDate": "2024-12-31T00:00:00.000Z",// "daysRemaining": 180,// "severity": "MEDIUM",// "successorVersion": "v2",// "migrationGuide": "https://docs.example.com/migration/v1-to-v2"// }// }Notice how the warning severity escalates as sunset approaches. This graduated urgency helps consumers prioritize: LOW means 'plan for this', CRITICAL means 'this is your final warning'. Your monitoring dashboards should alert on HIGH and CRITICAL severity deprecation warnings.
Telling consumers to migrate isn't enough—you need to help them succeed. The easier you make migration, the faster consumers will complete it, and the sooner you can retire the old version.
Essential Migration Resources:
1. Comprehensive Migration Guide
A detailed document covering:
2. Change Detection Tooling
Help consumers understand their exposure:
3. Compatibility Layers (Optional)
For complex migrations, consider temporary compatibility:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
# API v1 to v2 Migration Guide ## OverviewThis guide covers migrating from API v1 to v2. Estimated effort: 2-8 hours depending on integration complexity. ## Breaking Changes Summary ### 1. User Object Restructuring **Before (v1):**```json{ "id": 123, "name": "John Doe", "email": "john@example.com"}``` **After (v2):**```json{ "id": "user_a1b2c3", "profile": { "displayName": "John Doe" }, "contact": { "email": "john@example.com" }}``` **Migration Steps:**1. Update ID handling: v2 uses string UUIDs instead of integers2. Change `user.name` references to `user.profile.displayName`3. Change `user.email` references to `user.contact.email` ### 2. Authentication Change **Before (v1):** Basic Authentication or API Key**After (v2):** OAuth 2.0 Bearer Tokens only **Migration Steps:**1. Register OAuth application at developer portal2. Implement OAuth 2.0 client credentials flow3. Replace `Authorization: Basic ...` with `Authorization: Bearer ...` ### 3. Error Format Change **Before (v1):**```json{ "error": "Not found", "code": 404 }``` **After (v2):**```json{ "error": { "type": "NOT_FOUND", "message": "User with ID 'xyz' not found", "details": { "resource": "user", "id": "xyz" } }}``` **Migration Steps:**1. Update error parsing to handle nested `error` object2. Change error code checking from numeric to string type ## Step-by-Step Migration ### Phase 1: Prepare (Do First)- [ ] Read this guide completely- [ ] Audit your v1 usage to identify affected endpoints- [ ] Set up v2 API credentials ### Phase 2: Update Authentication- [ ] Implement OAuth 2.0 flow- [ ] Test authentication separately before other changes ### Phase 3: Update API Calls- [ ] Update base URL from /v1 to /v2- [ ] Update request payloads per breaking changes- [ ] Update response parsing per breaking changes ### Phase 4: Test- [ ] Run full integration test suite- [ ] Verify all edge cases- [ ] Test error handling ### Phase 5: Deploy- [ ] Deploy to staging- [ ] Monitor for issues- [ ] Deploy to production ## Common Pitfalls 1. **Integer vs String IDs**: Don't cast string IDs to integers; they contain letters2. **Null handling**: v2 returns `null` for missing optional fields; v1 omitted them3. **Rate limits**: v2 has different rate limits; check your volume ## Need Help? - Migration Office Hours: Tuesdays 2pm PT- Email: migration-support@example.com- Slack: #api-migrationWhen the sunset date arrives, the deprecated version must actually stop working. This moment requires careful execution to minimize chaos while firmly enforcing the deadline.
Pre-Sunset Checklist (1 Week Before):
Sunset Day Execution:
Sunset Response Design:
When a sunset endpoint is called, return a helpful response:
12345678910111213141516171819202122
// HTTP 410 Gone Response for Sunset APIs { "error": { "type": "API_VERSION_SUNSET", "message": "API v1 was retired on December 31, 2024 and is no longer available.", "migrationInfo": { "successorVersion": "v2", "successorEndpoint": "https://api.example.com/v2/users/123", "migrationGuide": "https://docs.example.com/migration/v1-to-v2" }, "support": { "email": "api-support@example.com", "documentation": "https://docs.example.com/v2" }, "sunsetDate": "2024-12-31T23:59:59Z", "deprecationNoticeDate": "2024-01-01T00:00:00Z" }}Some organizations provide a brief grace period after sunset (e.g., 24-48 hours) where the API returns errors but a quick revert is possible for true emergencies. Others enforce hard cutoffs. Choose based on your consumer relationships and operational capabilities, but be consistent.
Sunset isn't the end—there's important work after the deadline:
Continued Monitoring:
Keep monitoring traffic to sunset endpoints for weeks or months:
Helpful Error Responses:
Maintain informative 410 Gone responses for at least 6-12 months:
Documentation Archival:
Don't delete old documentation—archive it:
Post-Mortem Analysis:
After every deprecation cycle, analyze:
Infrastructure Cleanup:
Once traffic has definitively stopped:
| Timeframe | Actions |
|---|---|
| Day 1-7 | Intensive monitoring, support for stragglers, immediate incident response capability |
| Week 2-4 | Analyze migration completion rates, follow up with remaining consumers, adjust 410 responses |
| Month 2-6 | Maintain 410 responses, periodic traffic review, documentation remains archived |
| Month 6-12 | Reduce monitoring intensity, consider removing from active codebase, post-mortem completion |
| After 12 months | Remove sunset handlers, archive closes, learnings incorporated into next deprecation |
Not all deprecation is version-level. Sometimes individual features, endpoints, or fields need deprecation within a version. This requires more surgical approaches.
Endpoint Deprecation:
Mark specific endpoints as deprecated while the overall version remains active:
GET /v2/users/123/legacy-profile HTTP/1.1
HTTP/1.1 200 OK
Deprecation: true
Sunset: Sun, 30 Jun 2025 23:59:59 GMT
Link: </v2/users/123/profile>; rel="successor-version"
{ "data": { ... }, "_deprecated": "Use /v2/users/{id}/profile instead" }
Field Deprecation:
Mark specific response fields as deprecated:
{
"id": "user_123",
"name": "John Doe",
"email": "john@example.com",
"legacy_id": 123,
"_fieldDeprecations": {
"legacy_id": {
"deprecated": true,
"sunset": "2025-06-30",
"replacement": "Use 'id' field instead"
}
}
}
Parameter Deprecation:
Signal deprecated parameters in responses:
Warning: 299 - "Parameter 'user_id' is deprecated; use 'userId' instead"
SDK/Client Library Deprecation:
If you provide SDKs, use language-level deprecation:
/** @deprecated Use getUserById() instead */
function getUser(id: number): User { ... }
// TypeScript/IDE will warn consumers
Feature-level deprecation is powerful for gradual API evolution. Instead of big-bang version bumps, you can deprecate individual elements over time, giving consumers focused, manageable migration work. This reduces major version frequency and eases consumer burden.
Learning from how major platforms handle deprecation provides valuable patterns:
Stripe: Continuous Evolution Model
Stripe uses date-based versions (e.g., 2024-01-15) rather than traditional major versions. Their approach:
Key Insight: Pin versions by default, upgrade explicitly. This prevents surprise breakage.
Google API Deprecation Policy
Google's Cloud APIs follow a structured policy:
Key Insight: Different stability levels allow faster iteration on new features while protecting production-critical APIs.
Twitter API v1.1 → v2 Migration
Twitter's major version migration challenges:
Key Insight: Major version migrations are multi-year endeavors. Plan accordingly.
Deprecation is where versioning becomes real. Let's consolidate the key insights from this page and the entire module:
Module Complete: API Versioning
You've now completed a comprehensive journey through API versioning:
With this knowledge, you can design APIs that evolve sustainably over years while maintaining the trust and satisfaction of your consumers.
You now possess world-class knowledge of API versioning—from strategic importance through tactical implementation to graceful deprecation. This foundation enables you to build APIs that serve consumers reliably for years while continuously improving. Apply these principles from your next API's first design, and versioning will be a source of strength rather than struggle.