Slopapalooza

What it is

A working API made entirely of accidents. Every endpoint is a local decision that doesn’t know the other endpoints exist. Holding it together since forever.

How it happens

Each endpoint was prompted independently. “Add a route for X.” “Now one for Y.” The AI happily generates each route to match whatever conventions it can infer from the existing file — which is often nothing, because the existing file is also a duct-tape collection.

Warning signs

  • The auth pattern differs between endpoints.
  • Error responses are sometimes JSON, sometimes strings, sometimes a status code with no body.
  • Verbs and nouns don’t agree: GET /getUser next to POST /users/create next to DELETE /user_delete.
  • Pagination is implemented three different ways.
  • New endpoints don’t reuse existing helpers — each one re-implements parsing, validation, and error handling.

Why it’s dangerous

The cost lands on the people integrating with it — later, and louder. Documentation is hard because the API isn’t designed; it’s the union of accidents. SDKs are tedious to write. Every new endpoint takes longer than the last because every decision has to be re-litigated against the noise.

How to prevent it

Decide the conventions before the first endpoint. Naming, auth, errors, pagination, status codes — pick one of each. Write them down. New endpoints follow the conventions, or they get rewritten before merge.

For an API that’s already a mess, don’t try to retroactively make it consistent in place — you’ll break consumers and never finish. Freeze the existing surface, version it, and build the new shape behind v2 with the conventions enforced from the start. Deprecate the old version on a real schedule. AI is dramatically better at generating endpoints that match a written spec than at retrofitting coherence onto a sprawling existing one.

The serious team fix

The interface is the contract. The code is the implementation. Review the interface — the URL, the payload, the response shape, the error contract — before AI generates a single line of code. Every time.

Naming matters disproportionately on an API, and AI is not great at it. The model picks the median pattern, which is fine in isolation and incoherent across an API. Whether the endpoint is /users/:id/orders or /orders?user_id=.... Whether the field is customer_id or customerId. Whether the response is wrapped in a data key or not. Those are design decisions that reflect how you understand the domain. Naming is thinking. If you let AI do the thinking, you get its thinking, not yours.

Three reinforcing levels of rigor, depending on how much the API matters:

  1. A two-page style guide in the repo, literally the first thing in the prompt context for any new endpoint. One canonical pattern for naming, auth, errors, pagination, status codes. AI is great at following conventions when you’ve actually written them down — and it stops inventing them when you have.
  2. Contract-first with OpenAPI (formerly known as Swagger — same thing, renamed in 2016) when the API is a real product surface or has external consumers. Write the spec — URL, payload, response shape, error contract — then generate the server stubs from it and the client SDKs from it. AI fills in handlers against an unambiguous contract instead of inventing the contract endpoint by endpoint. This is the AI-era best case: the spec eliminates the ambiguity that produces duct tape, and AI does the implementation work the spec implies.
  3. Lint and CI enforcement so conventions can’t silently drift. Spectral (or similar) catches OpenAPI violations. Custom lint rules catch naming and response-shape inconsistencies. The next AI-generated endpoint that breaks the pattern fails CI, not code review — which means the reviewer is free to focus on the design, not the formatting.

You don’t need all three on day one. But for any API other people will consume, level 1 is non-negotiable, and level 2 pays for itself the first time you regenerate clients.

Related anti-patterns

Back to all anti-patterns