Loading learning content...
When Amazon launched AWS S3 in 2006, they made design decisions that persist to this day—nearly two decades later. The API paths, authentication model, and XML response format chosen by engineers in 2006 still serve millions of applications. This isn't legacy debt; it's intentional permanence. The S3 API has evolved through backward-compatible additions while maintaining unwavering support for original operations.
This is the reality of API design: decisions made today will constrain or empower your system for years or decades. Unlike internal code that can be refactored at will, APIs create external dependencies that cannot be easily unwound. The integration code your consumers write becomes technical debt that they bear—and they will resist changes that force them to modify working code.
Understanding the API lifecycle—from birth through evolution to eventual sunset—is essential for making decisions today that won't become regrets tomorrow.
You will understand the complete API lifecycle: the design phase and its long-term implications, the launch process and what 'GA' really means, the evolution strategies that maintain backward compatibility, the deprecation process and its organizational choreography, and the eventual sunset that gracefully ends API support. This knowledge enables you to make decisions that stand the test of time.
APIs progress through well-defined lifecycle stages, each with distinct characteristics, objectives, and constraints.
Stage 1: Design
Stage 2: Preview (Alpha/Beta)
Stage 3: General Availability (GA)
Stage 4: Deprecation
Stage 5: Sunset/Retirement
Lifecycle stages have dramatically different durations:
| Stage | Typical Duration | Key Milestone | Breaking Changes? |
|---|---|---|---|
| Design | 2-6 months | Contract approval | N/A (not public) |
| Alpha | 1-3 months | Internal testing complete | Yes, expected |
| Beta (Private) | 2-6 months | Partner validation | Yes, with notice |
| Beta (Public) | 1-3 months | Community feedback | Reluctantly, with notice |
| GA | 5-20+ years | Full production launch | No (use versioning) |
| Deprecation | 12-24+ months | Zero new integrations | No |
| Sunset | Single date | API shutdown | Total (API removed) |
Design takes months; GA lasts for years or decades. The 3 months you spend in design determine the 10 years you spend in GA. This asymmetry means design investments have outsized returns—and design shortcuts have outsized costs.
The design phase is where you make decisions that will persist for the lifetime of the API. Getting these right—or at least not catastrophically wrong—is critical.
These decisions are nearly impossible to change after GA:
API Style and Protocol:
Naming Conventions:
/users/123 vs /users/user_123)Authentication Model:
Error Format:
Identifier Format:
Robust design review prevents regrets:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
# API Design Review Checklist ## Pre-Review Requirements- [ ] OpenAPI/GraphQL specification complete- [ ] Example requests and responses for all operations- [ ] Error cases documented- [ ] Security model defined- [ ] Rate limiting strategy defined ## Consistency Review- [ ] Naming follows organizational conventions- [ ] HTTP methods match operation semantics- [ ] Response structures consistent with existing APIs- [ ] Error format matches organizational standard- [ ] Authentication consistent with other APIs ## Consumer Perspective Review- [ ] Use cases clearly addressed- [ ] Minimum viable operations included- [ ] No leaky abstractions (internal details exposed)- [ ] Sensible defaults for optional parameters- [ ] Clear documentation for all fields ## Evolvability Review- [ ] Room for future enhancements- [ ] No unnecessary coupling to current implementation- [ ] Pagination built in where lists may grow- [ ] Versioning strategy defined- [ ] Expansion patterns for response fields ## Security Review- [ ] Authentication requirements appropriate- [ ] Authorization model scalable- [ ] Rate limiting prevents abuse- [ ] No sensitive data in URLs or logs- [ ] Input validation specified ## Operational Review- [ ] Monitoring hooks defined- [ ] SLA targets achievable- [ ] Capacity planning complete- [ ] Incident response plan ## Approval Gates- [ ] API Product Manager approval- [ ] Security team approval- [ ] Documentation team review- [ ] Consumer representative review (if known)The best design validation is implementation. Before finalizing your design, have 2-3 potential consumers (internal or partners) build against a mock version. Their implementation struggles reveal design flaws that appear minor in specification but catastrophic in code.
The preview phase bridges design and GA. It's your last opportunity to make breaking changes without major coordination costs.
Alpha:
Beta:
Clear communication prevents frustration:
1234567891011121314151617
HTTP/1.1 200 OKContent-Type: application/jsonX-API-Status: betaX-API-Stability: experimentalX-API-Breaking-Change-Notice: This API is in beta. Breaking changes may occur with 2 weeks notice. Subscribe to changelog for updates. { "data": { ... }, "_meta": { "apiStatus": "beta", "stabilityLevel": "experimental", "breakingChangePolicy": "2 weeks notice via changelog", "changelogUrl": "https://changelog.example.com/beta-api", "feedbackEmail": "api-beta@example.com" }}The preview phase's primary value is learning. Active feedback collection is essential:
Quantitative feedback:
Qualitative feedback:
Premature GA is extremely costly. Once you commit to stability, every design flaw becomes permanent technical debt. It's far better to extend beta by 2 months than to live with a bad decision for 10 years. Resist pressure to launch before you're ready.
General Availability (GA) is a commitment. When you declare an API GA, you're promising:
GA is NOT:
GA IS:
The core GA promise is backward compatibility: existing integrations continue to work. This means:
| Change Type | Example | Compatible? |
|---|---|---|
| Add optional field to request | Add metadata object to POST body | ✅ Yes |
| Add field to response | Add createdAt to user object | ✅ Yes |
| Add new endpoint | Add GET /users/{id}/preferences | ✅ Yes |
| Add new HTTP method | Add PATCH /users/{id} | ✅ Yes |
| Widen accepted values | Accept both 'USD' and 'usd' | ✅ Yes |
| Remove field from request | Remove name requirement | ⚠️ Usually (test) |
| Remove field from response | Remove legacyStatus | ❌ Breaking |
| Rename field | Rename user_id to userId | ❌ Breaking |
| Change field type | Change age from string to int | ❌ Breaking |
| Add required field | Require email on all requests | ❌ Breaking |
| Remove endpoint | Remove DELETE /deprecated | ❌ Breaking |
| Change error format | Change error structure | ❌ Breaking |
| Stricter validation | Reject previously accepted values | ❌ Breaking |
1234567
// Original GA response (2020){ "id": "usr_123", "name": "Jane Doe", "email": "jane@example.com", "status": "active"}Also known as Postel's Law: 'Be conservative in what you send, liberal in what you accept.' Clients should ignore unknown response fields. Servers should be lenient with optional inputs. This mutual tolerance enables evolution on both sides.
When breaking changes are unavoidable, versioning allows them without disrupting existing consumers.
URL Path Versioning:
https://api.example.com/v1/users
https://api.example.com/v2/users
Header Versioning:
GET /users
API-Version: 2024-01-15
Query Parameter Versioning:
https://api.example.com/users?version=2
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
// Supporting multiple API versions simultaneously interface VersionConfig { version: string; status: 'current' | 'supported' | 'deprecated' | 'sunset'; launchDate: Date; deprecationDate?: Date; sunsetDate?: Date; supportLevel: 'full' | 'security-only' | 'none';} const API_VERSIONS: VersionConfig[] = [ { version: 'v3', status: 'current', launchDate: new Date('2024-01-15'), supportLevel: 'full', }, { version: 'v2', status: 'supported', launchDate: new Date('2022-06-01'), deprecationDate: new Date('2024-06-15'), sunsetDate: new Date('2025-06-15'), supportLevel: 'full', }, { version: 'v1', status: 'deprecated', launchDate: new Date('2020-01-01'), deprecationDate: new Date('2022-01-01'), sunsetDate: new Date('2024-06-01'), supportLevel: 'security-only', },]; // Version-aware routingasync function routeByVersion(req: Request): Promise<Response> { const version = extractVersion(req); const config = API_VERSIONS.find(v => v.version === version); if (!config) { return new Response(JSON.stringify({ error: 'UNKNOWN_API_VERSION', message: `Version '${version}' is not recognized`, availableVersions: API_VERSIONS .filter(v => v.status !== 'sunset') .map(v => v.version), }), { status: 400 }); } if (config.status === 'sunset') { return new Response(JSON.stringify({ error: 'API_VERSION_SUNSET', message: `Version '${version}' was sunset on ${config.sunsetDate}`, migrationGuide: `https://docs.example.com/migrate-${version}`, currentVersion: 'v3', }), { status: 410 }); // 410 Gone } // Add deprecation headers for deprecated versions const headers = new Headers(); if (config.status === 'deprecated') { headers.set('Deprecation', config.deprecationDate.toISOString()); headers.set('Sunset', config.sunsetDate.toISOString()); headers.set('Link', `<https://docs.example.com/migrate-${version}>; rel="successor-version"`); } // Route to version-specific handler const handler = versionHandlers[version]; const response = await handler(req); // Add version info to response headers.set('API-Version', version); headers.set('API-Version-Status', config.status); return new Response(response.body, { status: response.status, headers: { ...Object.fromEntries(response.headers), ...Object.fromEntries(headers) }, });}Maintaining multiple versions simultaneously is expensive but necessary:
Code organization options:
Separate codebases — Different services for each version
Version adapters — Single core with version-specific adapters
Feature flags — Single codebase with version-conditional behavior
Each API version you maintain is effectively a separate product requiring support, testing, monitoring, and documentation. Limit the number of concurrent versions—ideally no more than 2-3. The maintenance burden grows faster than it appears.
Deprecation is the formal process of winding down API support while giving consumers time to migrate.
Good reasons to deprecate:
Bad reasons to deprecate:
Minimum deprecation period for external APIs: 12 months Recommended: 18-24 months
This timeline allows enterprise consumers—who may have annual budget cycles—to plan, budget, and execute migration projects.
| Timeframe | Action | Communication Channel |
|---|---|---|
| T-24 months | Announce new version availability | Blog, email, changelog |
| T-18 months | Announce deprecation date | Blog, email, in-API headers, changelog |
| T-12 months | Begin deprecation warnings in responses | API headers, dashboard notifications |
| T-6 months | Increase warning urgency | Direct email to high-volume users |
| T-3 months | Disable new API key creation for deprecated version | Dashboard, signup flow |
| T-1 month | Final migration reminder | Direct outreach to remaining users |
| T-1 week | Last call notification | All channels |
| T-0 | Sunset: API returns 410 Gone | Status page, blog |
123456789101112131415161718192021222324252627282930313233
# Deprecation headers following IETF RFC 8594 # Standard deprecation headerDeprecation: Sat, 15 Jun 2024 00:00:00 GMT # Sunset header with date API will be removedSunset: Sat, 15 Jun 2025 00:00:00 GMT # Link header pointing to successor and documentationLink: <https://api.example.com/v3/users>; rel="successor-version", <https://docs.example.com/deprecation-v2>; rel="deprecation" # Example complete deprecated response: HTTP/1.1 200 OKContent-Type: application/jsonAPI-Version: v2API-Version-Status: deprecatedDeprecation: Sat, 15 Jun 2024 00:00:00 GMTSunset: Sat, 15 Jun 2025 00:00:00 GMTLink: <https://api.example.com/v3/users>; rel="successor-version" { "data": { ... }, "warnings": [ { "code": "API_VERSION_DEPRECATED", "message": "API version v2 is deprecated and will be sunset on 2025-06-15", "migrationGuide": "https://docs.example.com/migrate-v2-to-v3", "newVersionUrl": "https://api.example.com/v3" } ]}Deprecation is only ethical if you provide a migration path:
Essential migration resources:
Migration assistance for large consumers:
Monitor usage of deprecated versions/endpoints weekly. Identify consumers who aren't migrating and reach out proactively. Large consumers may not know about the deprecation—direct outreach often accelerates migration more than documentation.
The sunset phase is the final removal of API support. Done well, it's anticlimactic—everyone has migrated. Done poorly, it breaks production systems.
When a sunset API is called, return a helpful response:
123456789101112131415161718192021
HTTP/1.1 410 GoneContent-Type: application/jsonSunset: Sat, 15 Jun 2024 00:00:00 GMT { "error": { "code": "API_SUNSET", "message": "This API version (v1) was sunset on June 15, 2024", "details": "API version v1 is no longer available. Please migrate to v3.", "resources": { "migrationGuide": "https://docs.example.com/migrate-v1-to-v3", "currentApiDocs": "https://docs.example.com/v3", "support": "https://support.example.com/api-migration" }, "timeline": { "deprecationAnnounced": "2022-06-15", "deprecationDate": "2023-01-15", "sunsetDate": "2024-06-15" } }}After sunset, monitor for:
Sometimes, sunset must be delayed:
Consider extension if:
Do not extend for:
Extensions should be exception, not norm. Frequent extensions signal your deprecation timeline is too aggressive.
After sunset stabilizes, remove the dead code. Sunset endpoints still running in prod create confusion and maintenance burden. Delete the code, remove the documentation, and close the chapter completely.
Effective API lifecycle management requires organizational governance—clear policies, assigned ownership, and systematic processes.
Maintain a central registry of all APIs with lifecycle status:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
interface ApiRegistryEntry { // Identification id: string; name: string; description: string; category: 'internal' | 'partner' | 'public'; // Ownership owningTeam: string; technicalOwner: string; productOwner: string; // Lifecycle status status: 'design' | 'alpha' | 'beta' | 'ga' | 'deprecated' | 'sunset'; statusChangedAt: Date; // Key dates designApprovedAt?: Date; alphaLaunchAt?: Date; betaLaunchAt?: Date; gaLaunchAt?: Date; deprecationAnnouncedAt?: Date; sunsetScheduledAt?: Date; sunsetCompletedAt?: Date; // Documentation specUrl: string; // OpenAPI/GraphQL spec docsUrl: string; // Human-readable docs changelogUrl: string; // Metrics lastWeekRequests: number; activeConsumers: number; errorRate: number; // Dependencies dependsOn: string[]; // APIs this API calls dependedOnBy: string[]; // APIs that call this API // Versioning currentVersion: string; supportedVersions: string[]; deprecatedVersions: string[];} // Example registry entry:const userApiEntry: ApiRegistryEntry = { id: 'api-user-service', name: 'User Service API', description: 'Core user management and authentication', category: 'public', owningTeam: 'identity-team', technicalOwner: 'jane.smith@company.com', productOwner: 'bob.jones@company.com', status: 'ga', statusChangedAt: new Date('2022-01-15'), gaLaunchAt: new Date('2022-01-15'), specUrl: 'https://specs.internal/user-api.yaml', docsUrl: 'https://docs.example.com/api/users', changelogUrl: 'https://docs.example.com/api/users/changelog', lastWeekRequests: 45_000_000, activeConsumers: 1_247, errorRate: 0.02, dependsOn: ['api-audit-service', 'api-notification-service'], dependedOnBy: ['api-order-service', 'api-billing-service', 'api-analytics-service'], currentVersion: 'v3', supportedVersions: ['v2', 'v3'], deprecatedVersions: ['v1'],};Quarterly reviews for each API:
Every API needs clear ownership:
Technical Owner: Engineer responsible for implementation, performance, reliability
Product Owner: PM responsible for roadmap, consumer needs, deprecation decisions
Executive Sponsor: Leader accountable for strategic alignment, resource allocation, cross-team coordination
Without clear ownership, APIs drift into neglect, documentation rots, and deprecations never complete.
Treat your API portfolio like a garden requiring continuous attention. Regular weeding (deprecation), fertilizing (documentation updates), and pruning (removing unused endpoints) keeps the garden healthy. Neglected gardens become overgrown and unusable.
Understanding the API lifecycle transforms how you approach API design and maintenance. Let's consolidate the key insights:
Module Complete
You've now completed Module 1: What Is an API? You understand APIs as contracts, the internal/external distinction, API product thinking, and the full API lifecycle. This foundational knowledge prepares you for the next modules on specific API technologies: REST, GraphQL, gRPC, and beyond.
Congratulations on completing 'What Is an API?' You now have the conceptual foundation for API design—understanding APIs as contracts, products, and lifecycle-managed assets. The next module explores REST API design in depth, applying these foundational concepts to the most common API style.