---
title: "Reviewing AI-Generated Backends: Boundaries, Data, and Failure Modes"
seo_title: "Reviewing AI-Generated Backends: Boundaries, Data, and Failure Modes — Dan Podina"
description: "A practical architecture review for AI-built backends, following one request through authorization, tenant data, retries, background jobs, and recovery."
url: https://danpodina.com/log/reviewing-ai-generated-backends/
markdown: https://danpodina.com/log/reviewing-ai-generated-backends/index.md
type: log
date: 2026-09-10
lastmod: 2026-09-10
---

# Reviewing AI-Generated Backends: Boundaries, Data, and Failure Modes

> A practical architecture review for AI-built backends, following one request through authorization, tenant data, retries, background jobs, and recovery.

*The export service below is an illustrative example, not a client case study.*

An AI-built backend can look convincing before anyone has checked how its pieces behave together. Routes exist. Queries return data. A job runs in the background. The useful review question is what happens when a request crosses an ownership boundary or stops halfway through.

Consider a small SaaS product that exports customer records. A user starts an export, a worker creates a file, and the dashboard offers a download. That one feature crosses authentication, tenant authorization, database access, queue delivery, object storage, and retention.

## Follow a request from identity to data

Start by writing down the identities involved: the signed-in user, their organization, the export owner, and the worker's service identity. They are not interchangeable.

A valid login does not prove permission to read a particular export. OWASP describes [broken object-level authorization](https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/) as a distinct API risk: authorization must apply to the requested object, not merely to the endpoint.

For our example, the lookup needs both an export identifier and an authorized tenant scope. The tenant scope comes from a verified membership decision; accepting it from a request header is not sufficient.

```text
request
  → authenticate the user
  → establish permitted tenant membership and action
  → fetch the export within that tenant
  → authorize access to its result
  → issue a short-lived download link
```

Apply the same reasoning to list endpoints, status polling, retries, and downloads. Protecting the initial “start export” route while leaving the status route global still exposes information.

Useful review evidence is a small table of requests: same tenant with permission, same tenant without permission, another tenant, revoked membership, and an expired session. For each, record the expected result and verify that denied requests produce no file or background job.

## Ask whether tenant boundaries survive the worker

A background worker often runs with more privilege than the user. Its job payload should identify the operation and tenant, while the worker loads authoritative state before acting. A queue message copied from the original request should not become a permanent authorization grant.

Decide how membership changes affect queued work. In this example, the chosen policy is to check permission again before execution and before download. A user removed from a tenant should not regain access by waiting for an old export to finish.

Database protections can add another boundary. PostgreSQL [row security policies](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) can restrict visible rows, but table owners and privileged roles may bypass them. Test with the actual application and worker roles. A passing test run under a different database role proves a different configuration.

Also inspect object-storage paths and cache keys. If the database is tenant-scoped but the result cache uses only `export:latest`, the boundary is lost one layer later.

## Treat retries as part of the normal path

The user clicks twice. A request times out after the database commits. The worker finishes a file and crashes before acknowledging its message. None of these requires an extraordinary outage.

Give each accepted export operation an identity, and define how repeated requests refer to that operation. For an idempotency key, scope it to the tenant and operation type, store a hash of the meaningful request, and reject reuse with different parameters. The retention period must cover the intended retry window.

If creating a job means both writing a database record and publishing a message, there is a dual-write gap. A [transactional outbox](https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/transactional-outbox.html) records the intended event in the same transaction as the job, then publishes it separately. The relay can still publish twice, so workers need idempotent state transitions. This is a pattern to evaluate, not a reason to add a queue to every feature.

## Make partial completion legible

For this example, use states with observable meaning: queued, running, completed, failed, and expired. Store the result reference only when the file is ready. A worker that loses its lease must not overwrite the result from a later attempt.

Now write the recovery rules. How is a stuck running job detected? Can an operator retry it? Are temporary objects removed? Does expiry delete the actual object or only hide the link? Can an error response include customer data or a signed download URL?

The dashboard should show enough to explain a failure without exposing internal credentials, storage locations, or raw stack traces. Operational logs need a job ID and tenant-safe context, not a copy of the exported records.

## Finish with a review that another engineer can act on

A useful review ends with evidence and priorities:

| Finding | Evidence to collect | Acceptance condition |
|---|---|---|
| Cross-tenant access | Authorization matrix against every export endpoint | Denied requests cannot read, create, or download another tenant's export |
| Duplicate work | Retry after a committed request times out | One accepted operation and a stable status response |
| Lost background job | Failure between database commit and message publication | Recovery republishes pending work without duplicating the result |
| Stale access | Membership revoked while work is queued | Execution and download follow the stated revocation policy |
| Incomplete cleanup | Expiry and worker-crash scenarios | Temporary and expired objects are removed within the chosen retention window |

The code's origin does not change these acceptance conditions. AI assistance changes how quickly a plausible implementation appears. Architecture review connects that implementation to the boundaries and promises the product needs to keep.


Read the log: https://danpodina.com/log/ | Contact card: https://danpodina.com/card/ | What I do now: https://systemtrails.com/

