All 24 checks with why-it-matters prose, severity, and cross-references to related audits.
Verb-in-URL anti-patterns like `/api/getUsers` or `/api/createPost` conflate the resource with the action, which belongs to the HTTP method. This makes APIs harder to understand, breaks REST tooling that assumes noun-based paths, and forces every new developer to learn an idiosyncratic convention rather than standard REST. iso-25010:2011 maintainability.modifiability degrades measurably: adding a new operation on a resource requires inventing a new verb path rather than adding an HTTP method to an existing noun endpoint.
Why this severity: High because inconsistent resource naming breaks REST tooling assumptions, makes APIs unintuitive for every new consumer, and compounds with each added endpoint.
api-design.naming-conventions.resource-namingSee full patternMixed casing in API response fields — `userId` alongside `created_at` in the same response object — forces every consumer to handle two naming conventions simultaneously. This is the classic sign of database column names leaking directly into the API layer without a serialization pass. It creates client-side bugs when consumers assume one convention and get the other, and it makes the API feel unfinished. iso-25010:2011 maintainability.modifiability is the direct impact: every client integration must special-case the inconsistency.
Why this severity: High because mixed field naming forces every API consumer to write defensive handling code, and the inconsistency compounds across every new endpoint added.
api-design.naming-conventions.field-namingSee full patternMagic numbers for status or type fields — `status: 1`, `role: 3` — are opaque to every consumer reading the response. Mixed enum formats across endpoints (SCREAMING_SNAKE on one, snake_case on another) mean consumers must maintain a lookup table per endpoint just to decode the same semantic concept. This directly violates iso-25010:2011 maintainability.modifiability: adding a new status value requires updating documentation, clients, and any code that pattern-matches on string values.
Why this severity: Medium because inconsistent enum formats create consumer confusion and maintenance overhead without enabling direct security exploitation.
api-design.naming-conventions.enum-namingSee full patternWhen one list endpoint paginates with `page`/`limit` and another with `offset`/`count`, every consumer building a generic list component must branch on the endpoint. Sorting inconsistency — `sort` on one, `orderBy` on another — means documentation is never sufficient; developers must check each endpoint individually. This is a direct iso-25010:2011 maintainability.modifiability failure: the API surface area effectively doubles for consumers who need to handle multiple list endpoints.
Why this severity: Medium because inconsistent query parameter naming imposes per-endpoint special-casing on every client without enabling security vulnerabilities.
api-design.naming-conventions.query-param-namingSee full patternMixed URL casing — `/api/user-profiles` alongside `/api/order_items` — creates ambiguity that breaks DNS case-sensitivity on some servers, confuses API clients that normalize URLs differently, and forces documentation to call out each route individually instead of stating a predictable rule. The iso-25010:2011 maintainability.modifiability impact is direct: developers adding new routes have no clear template to follow and will continue adding to the inconsistency.
Why this severity: High because URL segment casing inconsistencies can cause routing failures on case-sensitive servers and force every consumer to memorize per-route conventions.
api-design.naming-conventions.url-casingSee full patternA field named `active` could be a boolean, a verb, or a role descriptor — consumers can't tell from the name alone. `admin` is worse: it reads as a role name but is sometimes a boolean flag. When boolean fields lack `is_`/`has_`/`can_` prefixes, client code is forced to check documentation or source code before using the value, and TypeScript consumers may assign wrong types. iso-25010:2011 maintainability.readability is the direct impact: any developer reading the response must stop and verify semantics rather than inferring from the name.
Why this severity: Low because ambiguous boolean naming causes developer confusion and potential type bugs without enabling security exploits or data loss.
api-design.naming-conventions.boolean-namingSee full patternAn API with no machine-readable schema has no contract. Every consumer must read the implementation source to understand request/response shapes — and when the implementation changes, there is no diff to review, no tooling to catch regressions, and no artifact to share with third-party integrators. iso-25010:2011 compatibility.interoperability and maintainability.analysability both fail: the API cannot be consumed reliably by any party without deep implementation access. SDK generation, mock servers, and contract testing are all impossible without a schema.
Why this severity: Critical because without a machine-readable schema, the API has no enforceable contract, making all consumer integrations fragile and all tooling (SDK gen, mock servers, contract tests) inaccessible.
api-design.contract-quality.schema-existsSee full patternA stale schema is worse than no schema: it actively misleads consumers and downstream tooling. When `openapi.yaml` documents 8 endpoints but 12 exist in code, SDK generators produce incomplete clients, contract tests pass against a fiction, and third-party integrators build on fields that have already changed. iso-25010:2011 compatibility.interoperability fails in the most damaging way — the schema signals reliability it does not deliver. Drift compounds silently with every schema-less code change.
Why this severity: Critical because a drifted schema gives consumers false confidence, causes SDK generation failures, and makes contract tests useless as a regression guard.
api-design.contract-quality.schema-accuracySee full patternUnvalidated request bodies are the direct path to CWE-20 (Improper Input Validation) and OWASP A03:2021 (Injection). A handler that destructures `req.body` without parsing trusts whatever the client sends — malformed types, missing required fields, oversized strings, or injected payloads. Even without injection intent, unvalidated inputs cause runtime exceptions that leak stack traces, corrupt database records with wrong types, and produce 500 errors that hide the real cause from developers. iso-25010:2011 security.integrity requires that data entering the system be validated at the boundary.
Why this severity: High because unvalidated request bodies directly enable injection attacks (CWE-20, OWASP A03) and cause data corruption from type mismatches.
api-design.contract-quality.schema-validationSee full patternHandlers that return ad-hoc inline objects — `{ id: user.id, name: user.name, role: user.role }` — with no shared type definition silently expose database columns that were never intended for the API surface. Without explicit response types, a refactor that renames a database column silently changes the API contract, and TypeScript's type system provides zero protection. iso-25010:2011 maintainability.modifiability and compatibility.interoperability both degrade: the API shape can change with any database migration and no tooling catches it.
Why this severity: High because untyped responses expose accidental data leakage paths and remove TypeScript's protection against API shape changes caused by database refactors.
api-design.contract-quality.response-typesSee full patternWhen a field typed as `string` returns `null` at runtime, consumers get a type error or a silent coercion depending on their language. When some endpoints omit missing fields and others include them as `null`, every consumer must guard against both `undefined` and `null` for every optional field. This is the most common source of unexpected `Cannot read properties of null` errors in client applications. iso-25010:2011 compatibility.interoperability requires that the schema truthfully represent what the API sends — nullability is a core part of that contract.
Why this severity: Medium because undeclared nullability causes runtime type errors in consumers and forces defensive null-guarding throughout client code without enabling direct security exploits.
api-design.contract-quality.nullable-explicitSee full patternAn API where `/api/users` uses cursor-based pagination and `/api/orders` uses offset/limit forces every consumer to write different pagination logic per endpoint. Worse, an endpoint with no pagination at all returns unbounded result sets that will eventually OOM the server or time out for large collections. iso-25010:2011 compatibility.interoperability is directly impacted: a generic list component cannot be built once and reused, and any SDK wrapping the API must special-case every list endpoint.
Why this severity: High because inconsistent pagination patterns prevent generic client-side list handling and unbounded collections create reliability failures at scale.
api-design.contract-quality.pagination-contractSee full patternWhen auth errors return `{ message: '...' }`, validation errors return `{ errors: [...] }`, and not-found errors return raw strings, consumers must write a different parser for each error type. This is not just inconvenient — it makes automated error handling (retry logic, error monitoring, user-facing messages) impossible to build generically. iso-25010:2011 compatibility.interoperability and maintainability.modifiability both require a stable, machine-readable error contract. Inconsistent errors are also a debugging liability: different shapes in different production logs make tracing failures significantly harder.
Why this severity: Critical because inconsistent error shapes make automated error handling and observability impossible, and every new error type added to the API risks introducing yet another shape.
api-design.contract-quality.error-schemaSee full patternAn unversioned API has no mechanism to introduce breaking changes without breaking every existing consumer simultaneously. The first time a field is renamed, an endpoint is removed, or a response type changes, all integrators are affected with no warning or migration path. iso-25010:2011 compatibility.interoperability and maintainability.modifiability both require that the API surface area be stable across releases — and versioning is the mechanism that makes stability promises credible. Adding `/v1/` retroactively after you have external consumers is far more disruptive than doing it before launch.
Why this severity: Critical because an API with no versioning cannot introduce breaking changes without immediately breaking all consumers, making the API contract permanently fragile.
api-design.versioning-evolution.versioning-strategySee full patternWithout a documented breaking change policy, consumers have no way to know which parts of the API are stable and which might change without notice. This matters most at integration time: a consumer building a production integration on an undocumented API is making a bet that the author's implicit stability expectations match their own. When they don't, the consumer's code breaks silently on the next deploy. iso-25010:2011 compatibility.interoperability requires that the API's stability guarantees be explicitly stated — not assumed from silence.
Why this severity: High because undocumented breaking change policies mean consumers cannot make informed integration decisions and have no recourse when changes break their code.
api-design.versioning-evolution.breaking-change-policySee full patternDeprecated endpoints marked only in code comments — "legacy, use the new path" — are invisible to consumers reading the API schema or watching response headers. Tooling that scans for deprecated fields (OpenAPI linters, SDK generators, custom contract tests) cannot find what is not formally marked. iso-25010:2011 compatibility.interoperability is the direct casualty: consumers have no automated way to discover they are using a path scheduled for removal, and the migration window effectively starts the day removal happens rather than the day deprecation was intended.
Why this severity: Medium because unmarked deprecations leave consumers unaware of scheduled removals until breakage occurs, removing the entire benefit of a deprecation period.
api-design.versioning-evolution.deprecation-markersSee full patternWithout an API-specific changelog, consumers upgrading between versions have no way to know which endpoints changed, which fields were added, or which response shapes shifted. A general project changelog that says "various API improvements" is not a substitute. iso-25010:2011 maintainability.modifiability is directly impacted: the cost of each API change is borne by consumers who must diff the implementation themselves rather than reading a curated list of what changed and what they need to update.
Why this severity: Low because a missing changelog does not cause immediate failure but significantly increases the cost of every consumer upgrade and creates friction for third-party SDK maintainers.
api-design.versioning-evolution.changelogSee full patternAdding a required field without a default to an existing request schema is an immediate breaking change: every existing consumer who does not send the new field starts receiving 400 errors without any version bump or deprecation notice. iso-25010:2011 compatibility.interoperability and maintainability.modifiability are both violated — the API's stability promise is broken the moment a required field lands in production. The failure mode is insidious because it appears as a client error (400), making it look like the consumer sent a bad request when the real cause is a server-side contract change.
Why this severity: High because adding a required field without a default immediately breaks all existing consumers without a version bump, causing silent client-side failures.
api-design.versioning-evolution.additive-changesSee full patternReturning 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.
Why this severity: 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.
api-design.developer-ergonomics.status-codesSee full patternWhen sort and filter fields are undocumented, consumers discover valid values by trial and error or by reading the source. When invalid values are silently ignored — returning results in an unspecified order — consumers write broken code that appears to work until a data change reveals the sort was never applied. iso-25010:2011 maintainability.analysability is the direct impact: the API cannot be understood from its own interface and requires source code access to use correctly.
Why this severity: Medium because undocumented sort/filter fields force consumers into trial-and-error integration and silent failures when invalid values are ignored without error.
api-design.developer-ergonomics.sort-filter-docsSee full patternA POST endpoint for payments or orders with no idempotency protection turns a network retry into a duplicate charge or duplicate resource creation. This is not a theoretical risk — mobile networks, load balancers, and client libraries all retry on connection failure. CWE-770 (Allocation of Resources Without Limits or Throttling) applies when duplicate submissions trigger duplicate downstream effects. PUT endpoints that increment rather than set are silently non-idempotent: calling them twice produces different state than calling them once, making any retry logic dangerous.
Why this severity: High because non-idempotent POST operations on payments or orders create duplicate charges or records on network retries, a real production failure mode on unreliable connections.
api-design.developer-ergonomics.idempotencySee full patternAn API with no batch endpoints forces consumers to implement N+1 request loops for common multi-item operations — deleting 50 selected items requires 50 sequential DELETE calls. Each request adds latency, each adds load, and on high-latency connections the cumulative cost is severe. iso-25010:2011 performance-efficiency.time-behaviour is the direct impact: an operation that could complete in one round-trip takes N round-trips because the API surface was not designed for the real usage pattern the client requires.
Why this severity: Low because missing batch endpoints degrade performance for multi-item operations without causing correctness failures, and the impact scales with collection size.
api-design.developer-ergonomics.batch-operationsSee full patternA response that includes `userId` but no information about how to fetch the user forces every consumer to hardcode the URL pattern `/api/v1/users/{userId}`. When that pattern changes, every consumer breaks — and there is no machine-readable artifact that could have warned them. iso-25010:2011 compatibility.interoperability is the direct gap: the API returns foreign keys but not the means to navigate them, making the API partially self-describing at best and requiring consumers to maintain their own URL construction knowledge.
Why this severity: Info because missing resource links are a navigability improvement rather than a functional defect — consumers can work around the gap by hardcoding URL patterns, but it makes the API less self-documenting.
api-design.developer-ergonomics.resource-linkingSee full patternAn endpoint that returns 404 when a collection is empty conflates two distinct states: "this endpoint does not exist" and "this collection has no items." Consumers must add special-case 404 handling to every list call, and any generic list component breaks for new users who have no data yet. Returning `null` instead of `[]` causes `response.data.map is not a function` in every consumer that did not add a null guard. iso-25010:2011 compatibility.interoperability requires a consistent response shape regardless of result count — consumers must be able to write one code path for all list responses.
Why this severity: Low because inconsistent empty collection handling causes consumer-side null reference errors and forces special-case code without enabling security exploits.
api-design.developer-ergonomics.empty-collectionsSee full patternRun this audit in your AI coding tool (Claude Code, Cursor, Bolt, etc.) and submit results here for scoring and benchmarks.
Open API Design Audit