Programming with AI
ENRU
7.8

Access, AI mistakes, and a checklist

how a backend differs from other code

What it is

The chapter's closer. Two topics: who is allowed to do what — and how to check backend code.

A backend differs from everything else in this book in one way: other people appear here. A mistake in a script costs you an evening; a mistake in access control on a server can cost other people's data.

Access

Two different questions that get confused constantly:

QuestionTermResponse code
Who are you?authentication401 if you didn't identify yourself
What are you allowed?authorisation403 if you did, but it isn't allowed

The typical hole looks like this: the token is checked, the right is not.

@app.get("/subs/{user_id}")
def get_subs(user_id: int, current_user = Depends(get_current_user)):
    return SUBS[user_id]          # anyone can read anyone's subscriptions

The user signed in honestly, the token is real — and they request /subs/999. The identity check passed, the permission check does not exist.

    if current_user.id != user_id and not current_user.is_admin:
        raise HTTPException(status_code=403, detail="not your data")

What must not be handed out

  • Tokens and keys — not into the code, not into a chat, not into logs (article 5.7).
  • A dump of the live database — it holds other people's personal data. For debugging, make an anonymised extract.
  • A traceback to the client — it shows the project's structure (article 7.2).
  • Internal fields in a response — hashes, service identifiers (article 7.3).

AI mistakes in a backend

  1. Authentication without authorisation — covered above.
  2. 500 instead of 400 — no validation, the server dies on malformed data.
  3. The N+1 query — article 7.6.
  4. An f-string in SQL — injection, article 7.5.
  5. No transaction where several changes must happen together.
  6. No response_model — everything travels out in the response.
  7. A synchronous driver in asynchronous code — sqlite3 instead of aiosqlite, which blocks the whole server (article 6.4).
  8. No pagination. GET /subs returns every record. At a thousand it still works; at a million it takes down both the server and the client.

A checklist for backend code

Five steps on top of the general acceptance checklist (article 4.7).

1. Response codes

Check all four cases with real requests:

curl -o /dev/null -w "%{http_code}\n" .../subs/7      # 200
curl -o /dev/null -w "%{http_code}\n" .../subs/999    # 404
curl -X POST ... -d '{"city":"X"}'  -w " %{http_code}\n"   # 422
curl -X POST ... -d '{"city":"Oslo"}' -w " %{http_code}\n" # 409 on a repeat

2. Other people's data

Sign in as one user, request another's data. It must be a 403.

3. Database queries

How many per HTTP request? More than two or three — look for N+1.

4. The irreversible

Does the diff contain UPDATE, DELETE, DROP, migrations? Each one separately and carefully (articles 7.5, 7.7).

5. What travels out to the client

Look at the real response in full, not at the schema. Any extra fields?

What to tell the AI

Prompt
Check this backend code against the list:
- is there a permission check on the specific record, not only
  on the token
- which codes are returned on malformed data and on a missing record
- how many database queries per HTTP request
- are there f-strings in SQL
- what travels out to the client in the response

Which of these are problems? If none, say so.
Prompt
Can user 7 get user 8's data? Show me the request that does it,
or explain what prevents it.

Cheat sheet

  • Authentication is who you are (401), authorisation is what you may (403).

  • The AI adds the first and forgets the second. Ask about permissions separately.

  • Eight common mistakes: permissions, 500, N+1, injection, transactions, response_model, synchronous driver, pagination.

  • Checklist: codes · other people's data · query count · the irreversible · what reaches the client.

  • Check the codes with real curl, not by reading the code.

  • A dump of the live database does not go into a chat.