Production engineering guide for api design principles covering patterns, implementation strategies, and operational best practices.
This page describes foundational principles for designing APIs that are reliable, maintainable, and predictable. These principles apply when creating new HTTP-based services or extending existing ones, particularly when working with REST-style interfaces. They guide decisions about resource structure, method usage, response format, error handling, and data representation.
Resource names should be nouns in plural form and consistently use lowercase with hyphens for compound terms. For example: /users, /payment-methods, /notification-settings.
Use standard HTTP methods for their intended purposes:
Example request using curl:
curl -X PUT \
-H "Content-Type: application/json" \
-d '{"name": "Updated Name"}' \
http://api.example.com/users/123
Collections should support pagination via query parameters page and size. Default page size is 20, maximum allowed is 100.
Example request:
curl "http://api.example.com/users?page=2&size=50"
Response includes meta object with pagination information:
{
"data": [...],
"meta": {
"page": 2,
"size": 50,
"total": 1234,
"pages": 25
}
}
All successful responses use HTTP 200 OK with a consistent JSON structure. For collections, include data array and metadata.
Example:
{
"data": {
"id": "123",
"name": "John Doe",
"email": "[email protected]"
}
}
Errors must return HTTP status codes with structured JSON bodies containing error, message, and optional details:
{
"error": "ValidationFailed",
"message": "The request body is invalid",
"details": [
{
"field": "email",
"message": "Email is required"
}
]
}
Use appropriate status codes:
Version information must be part of the URL path, not headers or query parameters. Example: /v1/users, /v2/payment-methods.
Changes to existing endpoints must not break clients relying on previous behavior. New features should be additive.
All date fields use RFC 3339 format: 2023-12-01T15:30:00Z
Example field:
{
"created_at": "2023-12-01T15:30:00Z"
}
All numeric fields that are not floating-point use 64-bit integers. For large numbers, return as strings to prevent overflow in clients.
Example:
{
"account_balance": "1234567890123456789"
}
All responses must be valid against a defined schema. Clients should validate responses against the documented structure.
Authentication uses standard HTTP Authorization header with Bearer scheme:
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
http://api.example.com/users
Each endpoint requires specific authorization scopes. These are documented in the API specification.
Multiple identical requests must produce the same result as a single request. This enables safe retries.
When creating resources, clients can provide their own identifiers. The system should store these values and return them in responses.
Example:
curl -X POST \
-H "Content-Type: application/json" \
-d '{"id": "client-generated-123", "name": "New User"}' \
http://api.example.com/users
Response:
{
"data": {
"id": "client-generated-123",
"name": "New User",
"created_at": "2023-12-01T15:30:00Z"
}
}
Every resource must support ETag-based optimistic concurrency control. When updating, clients must send the current ETag in If-Match header:
curl -X PUT \
-H "If-Match: \"abc123def456\"" \
-H "Content-Type: application/json" \
-d '{"name": "Updated Name"}' \
http://api.example.com/users/123
When ETag doesn't match, return HTTP 412 with error details:
{
"error": "ResourceModified",
"message": "The resource was modified by another process"
}
Collections support filtering via query parameters that match field names:
curl "http://api.example.com/users?status=active&role=admin"
Sorting uses sort parameter with - prefix for descending order:
curl "http://api.example.com/users?sort=-created_at"
Batch operations should be available when multiple related resources need to be modified. Use POST with array of operations:
{
"operations": [
{
"method": "PUT",
"path": "/users/123",
"body": {"name": "Updated User"}
},
{
"method": "DELETE",
"path": "/users/456"
}
]
}
API responses must include headers indicating current limits:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1672531200
Responses should include Cache-Control headers when appropriate:
Cache-Control: public, max-age=3600
All error responses must provide clear, actionable messages that help clients understand and resolve the problem.
Use consistent error codes across all endpoints. These codes should be documented in API specification.
When parts of the system are unavailable, return partial results or appropriate fallback responses rather than failing completely.
Accept unknown fields in request bodies without validation errors. This allows future extensions without breaking existing clients.
All new fields should be optional in existing API versions to allow gradual adoption.
Without Content-Type: application/json, requests may be rejected or parsed inconsistently.
Mixing error response structures leads to client-side parsing failures.
Using 200 OK for errors or failing to return appropriate codes makes clients unable to distinguish success from failure.
Making PUT requests non-idempotent prevents safe retries and leads to data inconsistency.
Returning inconsistent page sizes or missing metadata breaks client-side pagination logic.
Not requiring authentication for protected endpoints creates security vulnerabilities.
Using POST for updates instead of PUT or PATCH results in incorrect semantics and prevents idempotency.
Failing to implement ETag-based concurrency control leads to data conflicts during concurrent updates.
All request parameters must be validated, including presence, type, and range checks.
Invalid input should return HTTP 400 with detailed error information in the response body.
PATCH operations should support partial updates to prevent overwriting valid fields.
Only include necessary fields in responses. Avoid returning full objects when partial data is sufficient.
Allow clients to request only specific fields using a fields query parameter:
curl "http://api.example.com/users?fields=id,name,email"
Ensure that API endpoints translate to efficient database operations without unnecessary joins or redundant queries.
This page was rewritten on 15 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy