Files
claude-skills-reference/engineering/api-design-reviewer/references/api_antipatterns.md
Leo 612f2a63fc merge: resolve conflict with dev, keep POWERFUL-tier SKILL.md
Kept our SKILL.md (POWERFUL-tier, 669 lines) over the codex-synced version.
Accepted all new files from dev (additional scripts, references, assets).
2026-02-16 13:13:28 +00:00

680 lines
13 KiB
Markdown

# Common API Anti-Patterns and How to Avoid Them
## Introduction
This document outlines common anti-patterns in REST API design that can lead to poor developer experience, maintenance nightmares, and scalability issues. Each anti-pattern is accompanied by examples and recommended solutions.
## 1. Verb-Based URLs (The RPC Trap)
### Anti-Pattern
Using verbs in URLs instead of treating endpoints as resources.
```
❌ Bad Examples:
POST /api/getUsers
POST /api/createUser
GET /api/deleteUser/123
POST /api/updateUserPassword
GET /api/calculateOrderTotal/456
```
### Why It's Bad
- Violates REST principles
- Makes the API feel like RPC instead of REST
- HTTP methods lose their semantic meaning
- Reduces cacheability
- Harder to understand resource relationships
### Solution
```
✅ Good Examples:
GET /api/users # Get users
POST /api/users # Create user
DELETE /api/users/123 # Delete user
PATCH /api/users/123/password # Update password
GET /api/orders/456/total # Get order total
```
## 2. Inconsistent Naming Conventions
### Anti-Pattern
Mixed naming conventions across the API.
```json
Bad Examples:
{
"user_id": 123, // snake_case
"firstName": "John", // camelCase
"last-name": "Doe", // kebab-case
"EMAIL": "john@example.com", // UPPER_CASE
"IsActive": true // PascalCase
}
```
### Why It's Bad
- Confuses developers
- Increases cognitive load
- Makes code generation difficult
- Reduces API adoption
### Solution
```json
Choose one convention and stick to it (camelCase recommended):
{
"userId": 123,
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"isActive": true
}
```
## 3. Ignoring HTTP Status Codes
### Anti-Pattern
Always returning HTTP 200 regardless of the actual result.
```json
Bad Example:
HTTP/1.1 200 OK
{
"status": "error",
"code": 404,
"message": "User not found"
}
```
### Why It's Bad
- Breaks HTTP semantics
- Prevents proper error handling by clients
- Breaks caching and proxies
- Makes monitoring and debugging harder
### Solution
```json
Good Example:
HTTP/1.1 404 Not Found
{
"error": {
"code": "USER_NOT_FOUND",
"message": "User with ID 123 not found",
"requestId": "req-abc123"
}
}
```
## 4. Overly Complex Nested Resources
### Anti-Pattern
Creating deeply nested URL structures that are hard to navigate.
```
❌ Bad Example:
/companies/123/departments/456/teams/789/members/012/projects/345/tasks/678/comments/901
```
### Why It's Bad
- URLs become unwieldy
- Creates tight coupling between resources
- Makes independent resource access difficult
- Complicates authorization logic
### Solution
```
✅ Good Examples:
/tasks/678 # Direct access to task
/tasks/678/comments # Task comments
/users/012/tasks # User's tasks
/projects/345?team=789 # Project filtering
```
## 5. Inconsistent Error Response Formats
### Anti-Pattern
Different error response structures across endpoints.
```json
Bad Examples:
# Endpoint 1
{"error": "Invalid email"}
# Endpoint 2
{"success": false, "msg": "User not found", "code": 404}
# Endpoint 3
{"errors": [{"field": "name", "message": "Required"}]}
```
### Why It's Bad
- Makes error handling complex for clients
- Reduces code reusability
- Poor developer experience
### Solution
```json
Standardized Error Format:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Email address is not valid"
}
],
"requestId": "req-123456",
"timestamp": "2024-02-16T13:00:00Z"
}
}
```
## 6. Missing or Poor Pagination
### Anti-Pattern
Returning all results in a single response or inconsistent pagination.
```json
Bad Examples:
# No pagination (returns 10,000 records)
GET /api/users
# Inconsistent pagination parameters
GET /api/users?page=1&size=10
GET /api/orders?offset=0&limit=20
GET /api/products?start=0&count=50
```
### Why It's Bad
- Can cause performance issues
- May overwhelm clients
- Inconsistent pagination parameters confuse developers
- No way to estimate total results
### Solution
```json
Good Example:
GET /api/users?page=1&pageSize=10
{
"data": [...],
"pagination": {
"page": 1,
"pageSize": 10,
"total": 150,
"totalPages": 15,
"hasNext": true,
"hasPrev": false
}
}
```
## 7. Exposing Internal Implementation Details
### Anti-Pattern
URLs and field names that reflect database structure or internal architecture.
```
❌ Bad Examples:
/api/user_table/123
/api/db_orders
/api/legacy_customer_data
/api/temp_migration_users
Response fields:
{
"user_id_pk": 123,
"internal_ref_code": "usr_abc",
"db_created_timestamp": 1645123456
}
```
### Why It's Bad
- Couples API to internal implementation
- Makes refactoring difficult
- Exposes unnecessary technical details
- Reduces API longevity
### Solution
```
✅ Good Examples:
/api/users/123
/api/orders
/api/customers
Response fields:
{
"id": 123,
"referenceCode": "usr_abc",
"createdAt": "2024-02-16T13:00:00Z"
}
```
## 8. Overloading Single Endpoint
### Anti-Pattern
Using one endpoint for multiple unrelated operations based on request parameters.
```
❌ Bad Example:
POST /api/user-actions
{
"action": "create_user",
"userData": {...}
}
POST /api/user-actions
{
"action": "delete_user",
"userId": 123
}
POST /api/user-actions
{
"action": "send_email",
"userId": 123,
"emailType": "welcome"
}
```
### Why It's Bad
- Breaks REST principles
- Makes documentation complex
- Complicates client implementation
- Reduces discoverability
### Solution
```
✅ Good Examples:
POST /api/users # Create user
DELETE /api/users/123 # Delete user
POST /api/users/123/emails # Send email to user
```
## 9. Lack of Versioning Strategy
### Anti-Pattern
Making breaking changes without version management.
```
❌ Bad Examples:
# Original API
{
"name": "John Doe",
"age": 30
}
# Later (breaking change with no versioning)
{
"firstName": "John",
"lastName": "Doe",
"birthDate": "1994-02-16"
}
```
### Why It's Bad
- Breaks existing clients
- Forces all clients to update simultaneously
- No graceful migration path
- Reduces API stability
### Solution
```
✅ Good Examples:
# Version 1
GET /api/v1/users/123
{
"name": "John Doe",
"age": 30
}
# Version 2 (with both versions supported)
GET /api/v2/users/123
{
"firstName": "John",
"lastName": "Doe",
"birthDate": "1994-02-16",
"age": 30 // Backwards compatibility
}
```
## 10. Poor Error Messages
### Anti-Pattern
Vague, unhelpful, or technical error messages.
```json
Bad Examples:
{"error": "Something went wrong"}
{"error": "Invalid input"}
{"error": "SQL constraint violation: FK_user_profile_id"}
{"error": "NullPointerException at line 247"}
```
### Why It's Bad
- Doesn't help developers fix issues
- Increases support burden
- Poor developer experience
- May expose sensitive information
### Solution
```json
Good Examples:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The email address is required and must be in a valid format",
"details": [
{
"field": "email",
"code": "REQUIRED",
"message": "Email address is required"
}
]
}
}
```
## 11. Ignoring Content Negotiation
### Anti-Pattern
Hard-coding response format without considering client preferences.
```
❌ Bad Example:
# Always returns JSON regardless of Accept header
GET /api/users/123
Accept: application/xml
# Returns JSON anyway
```
### Why It's Bad
- Reduces API flexibility
- Ignores HTTP standards
- Makes integration harder for diverse clients
### Solution
```
✅ Good Example:
GET /api/users/123
Accept: application/xml
HTTP/1.1 200 OK
Content-Type: application/xml
<?xml version="1.0"?>
<user>
<id>123</id>
<name>John Doe</name>
</user>
```
## 12. Stateful API Design
### Anti-Pattern
Maintaining session state on the server between requests.
```
❌ Bad Example:
# Step 1: Initialize session
POST /api/session/init
# Step 2: Set context (requires step 1)
POST /api/session/set-user/123
# Step 3: Get data (requires steps 1 & 2)
GET /api/session/user-data
```
### Why It's Bad
- Breaks REST statelessness principle
- Reduces scalability
- Makes caching difficult
- Complicates error recovery
### Solution
```
✅ Good Example:
# Self-contained requests
GET /api/users/123/data
Authorization: Bearer jwt-token-with-context
```
## 13. Inconsistent HTTP Method Usage
### Anti-Pattern
Using HTTP methods inappropriately or inconsistently.
```
❌ Bad Examples:
GET /api/users/123/delete # DELETE operation with GET
POST /api/users/123/get # GET operation with POST
PUT /api/users # Creating with PUT on collection
GET /api/users/search # Search with side effects
```
### Why It's Bad
- Violates HTTP semantics
- Breaks caching and idempotency expectations
- Confuses developers and tools
### Solution
```
✅ Good Examples:
DELETE /api/users/123 # Delete with DELETE
GET /api/users/123 # Get with GET
POST /api/users # Create on collection
GET /api/users?q=search # Safe search with GET
```
## 14. Missing Rate Limiting Information
### Anti-Pattern
Not providing rate limiting information to clients.
```
❌ Bad Example:
HTTP/1.1 429 Too Many Requests
{
"error": "Rate limit exceeded"
}
```
### Why It's Bad
- Clients don't know when to retry
- No information about current limits
- Difficult to implement proper backoff strategies
### Solution
```
✅ Good Example:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640995200
Retry-After: 3600
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "API rate limit exceeded",
"retryAfter": 3600
}
}
```
## 15. Chatty API Design
### Anti-Pattern
Requiring multiple API calls to accomplish common tasks.
```
❌ Bad Example:
# Get user profile requires 4 API calls
GET /api/users/123 # Basic info
GET /api/users/123/profile # Profile details
GET /api/users/123/settings # User settings
GET /api/users/123/stats # User statistics
```
### Why It's Bad
- Increases latency
- Creates network overhead
- Makes mobile apps inefficient
- Complicates client implementation
### Solution
```
✅ Good Examples:
# Single call with expansion
GET /api/users/123?include=profile,settings,stats
# Or provide composite endpoints
GET /api/users/123/dashboard
# Or batch operations
POST /api/batch
{
"requests": [
{"method": "GET", "url": "/users/123"},
{"method": "GET", "url": "/users/123/profile"}
]
}
```
## 16. No Input Validation
### Anti-Pattern
Accepting and processing invalid input without proper validation.
```json
Bad Example:
POST /api/users
{
"email": "not-an-email",
"age": -5,
"name": ""
}
# API processes this and fails later or stores invalid data
```
### Why It's Bad
- Leads to data corruption
- Security vulnerabilities
- Difficult to debug issues
- Poor user experience
### Solution
```json
Good Example:
POST /api/users
{
"email": "not-an-email",
"age": -5,
"name": ""
}
HTTP/1.1 400 Bad Request
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Email must be a valid email address"
},
{
"field": "age",
"code": "INVALID_RANGE",
"message": "Age must be between 0 and 150"
},
{
"field": "name",
"code": "REQUIRED",
"message": "Name is required and cannot be empty"
}
]
}
}
```
## 17. Synchronous Long-Running Operations
### Anti-Pattern
Blocking the client with long-running operations in synchronous endpoints.
```
❌ Bad Example:
POST /api/reports/generate
# Client waits 30 seconds for response
```
### Why It's Bad
- Poor user experience
- Timeouts and connection issues
- Resource waste on client and server
- Doesn't scale well
### Solution
```
✅ Good Example:
# Async pattern
POST /api/reports
HTTP/1.1 202 Accepted
Location: /api/reports/job-123
{
"jobId": "job-123",
"status": "processing",
"estimatedCompletion": "2024-02-16T13:05:00Z"
}
# Check status
GET /api/reports/job-123
{
"jobId": "job-123",
"status": "completed",
"result": "/api/reports/download/report-456"
}
```
## Prevention Strategies
### 1. API Design Reviews
- Implement mandatory design reviews
- Use checklists based on these anti-patterns
- Include multiple stakeholders
### 2. API Style Guides
- Create and enforce API style guides
- Use linting tools for consistency
- Regular training for development teams
### 3. Automated Testing
- Test for common anti-patterns
- Include contract testing
- Monitor API usage patterns
### 4. Documentation Standards
- Require comprehensive API documentation
- Include examples and error scenarios
- Keep documentation up-to-date
### 5. Client Feedback
- Regularly collect feedback from API consumers
- Monitor API usage analytics
- Conduct developer experience surveys
## Conclusion
Avoiding these anti-patterns requires:
- Understanding REST principles
- Consistent design standards
- Regular review and refactoring
- Focus on developer experience
- Proper tooling and automation
Remember: A well-designed API is an asset that grows in value over time, while a poorly designed API becomes a liability that hampers development and adoption.