APIs don't stay the same. Requirements change. You learn better ways to design things. Your users need new features.
Your API will change, so the priority is handling change without breaking users.
Plan for evolution from day one. Use versioning. Deprecate carefully. Communicate changes clearly.
This chapter is about growing your API without turning it into a mess.
Versioning Strategy
/api/v1/domains ← Initial release
/api/v2/domains ← Breaking change (new response format)
/api/v3/domains ← Major refactor
Deprecation Notice
Warn clients before breaking changes:
// V1 endpoint being phased out
class DeprecationHeadersMiddleware
{
public function handle(Request $request, Closure $next): Response
{
// For API v1
if ($request->is('api/v1/*')) {
$response = $next($request);
$response->header('Deprecation', 'true');
$response->header('Sunset', '2024-12-31T23:59:59Z'); // When v1 dies
$response->header('Link', '</api/v2/docs>; rel="successor-version"');
return $response;
}
return $next($request);
}
}
Schema Evolution
Add new fields without breaking existing clients:
// v1 response
{
"key": "lic_123",
"name": "License",
"domain": "example.com"
}
// v2 response (backward compatible)
{
"key": "lic_123",
"name": "License",
"domain": "example.com",
"status": "active", // NEW
"expiresAt": "2025-12-31" // NEW
}
// v3 response (with additional metadata)
{
"key": "lic_123",
"name": "License",
"domain": "example.com",
"status": "active",
"expiresAt": "2025-12-31",
"metadata": { // NEW
"createdAt": "2024-01-01",
"lastModified": "2024-12-01"
}
}
"expires_at": "2025-01-05"
}
Handling Multiple Versions
// routes/api.php
Route::prefix('v1')->group(function () {
Route::get('/domains', LicenseControllerV1::class);
});
Route::prefix('v2')->group(function () {
Route::get('/domains', LicenseControllerV2::class);
});
// Or use content negotiation
Route::get('/domains', function (Request $request) {
$version = $request->header('API-Version', 'v1');
return match($version) {
'v1' => new CollectionResponse(LicenseResourceV1::collection(...)),
'v2' => new CollectionResponse(LicenseResourceV2::collection(...)),
default => response()->json(['error' => 'Unsupported version'], 400),
};
});
Changelog Tracking
Keep a detailed changelog:
# Changelog
# [2.0.0] - 2024-12-01
# Added
- New `/api/v2/domains` endpoint with improved response format
- Support for bulk domain operations
- WebSocket support for real-time updates
# Changed
- Response format redesigned for clarity
- Rate limit headers now more granular
# Deprecated
- `/api/v1/domains` - use `/api/v2/domains` instead
- Bearer token format changed - old format still supported until 2024-03-01
# Removed
- Legacy SOAP API endpoint
# Security
- Fixed authentication bypass vulnerability
# [1.2.1] - 2024-10-15
# Fixed
- Rate limiting incorrectly counted requests from different IPs
Chapter 10 Summary
Versioning is essential. Plan for change from day one.
Best practices:
- Use URL versioning:
/api/v1,/api/v2 - Deprecate old versions with warning period
- Support 2-3 versions simultaneously
- Publish deprecation headers and dates
- Changelog every change
- Make migration guides for breaking changes
Evolution is inevitable. Plan for it.