Returning 200 for every response — including errors — forces consumers to parse the body to determine success, destroys the value of HTTP-level monitoring (uptime checks, load balancer health probes, APM error rate dashboards all become blind), and breaks any tooling that relies on status codes for routing decisions. Returning 500 for validation failures (which are client errors, not server errors) skews error rate metrics and triggers false alerts. iso-25010:2011 compatibility.interoperability requires semantically correct status codes because the entire HTTP ecosystem — proxies, CDNs, clients — makes decisions based on them.
High because incorrect status codes break HTTP-level monitoring, load balancer health checks, and any tooling that distinguishes client errors from server errors by status code.
Use HTTP status codes to convey the outcome — not just a 200 with an error flag in the body:
// Standard status code mapping:
// GET /api/users -> 200
// GET /api/users/:id -> 200 (found) or 404 (not found)
// POST /api/users -> 201 (created)
// PUT /api/users/:id -> 200 (updated) or 404 (not found)
// DELETE /api/users/:id -> 204 (deleted) or 404 (not found)
// Client errors (4xx):
// 400 -> validation error, malformed request
// 401 -> not authenticated
// 403 -> authenticated but not permitted
// 404 -> resource not found
// 409 -> conflict (duplicate, concurrent edit)
// 422 -> valid JSON but semantic error
// Server errors (5xx):
// 500 -> unhandled exception, not validation failure
See the error-schema check for structuring error response bodies consistently alongside these status codes.
ID: api-design.developer-ergonomics.status-codes
Severity: high
What to look for: Examine HTTP status codes returned across all endpoints. Check for correct and consistent usage: 200 for successful GET/PUT/PATCH, 201 for successful POST (resource created), 204 for successful DELETE (no content), 400 for validation errors, 401 for unauthenticated, 403 for unauthorized, 404 for not found, 409 for conflicts, 422 for unprocessable entity, 500 for server errors. Look for anti-patterns: returning 200 for errors with { success: false } in the body, returning 500 for all errors, returning 200 for resource creation instead of 201.
Pass criteria: Count the distinct HTTP status codes used across all handlers. Status codes are used correctly and consistently. Success operations use appropriate codes (200, 201, 204). Error codes distinguish between client errors (4xx) and server errors (5xx). The same type of error returns the same status code across all endpoints. At least 3 distinct status codes should be in use for a well-designed API.
Fail criteria: Status codes are incorrect (200 returned for errors, 500 returned for validation failures) or inconsistent (404 for missing user on one endpoint, 400 for missing order on another). Or all responses return 200 with error state in the body.
Skip (N/A) when: Fewer than 3 endpoints exist. Also skip for GraphQL (uses 200 for all responses by convention, with errors in the errors array) and gRPC (uses its own status code system).
Detail on fail: Identify the specific misuse (e.g., "All endpoints return 200 including errors -- error state conveyed via { success: false, error: '...' }. DELETE /api/users/:id returns 200 instead of 204. POST /api/orders returns 200 instead of 201 on creation."). Max 500 chars.
Cross-reference: For error response structure consistency, see the error-schema check in the Contract & Schema Quality category above.
Remediation: Use HTTP status codes to convey the outcome:
// Standard status code usage:
GET /api/users -> 200 (list)
GET /api/users/:id -> 200 (found) or 404 (not found)
POST /api/users -> 201 (created)
PUT /api/users/:id -> 200 (updated) or 404 (not found)
DELETE /api/users/:id -> 204 (deleted) or 404 (not found)
// Client errors:
400 -> validation error, malformed request
401 -> not authenticated (no valid token/session)
403 -> not authorized (authenticated but not permitted)
404 -> resource not found
409 -> conflict (duplicate resource, concurrent edit)
422 -> unprocessable entity (valid JSON but semantic error)