The Backend Reasoning Loop
Eleven questions to ask of any feature, in an order that surfaces the expensive decisions early.
The requirement, the obvious build, and why it breaks
Every lesson starts where the work starts: someone asked for something, and the first implementation that comes to mind has a problem.
Is there a repeatable way to reason about a backend feature instead of discovering its problems in production?
A ticket says "let users export their data". You have to turn that into a design that survives contact with real traffic.
Write the endpoint, then handle problems as they appear in review or in incidents.
Transaction boundaries and idempotency are structural. Retrofitting them means rewriting the feature, not patching it.
- Transaction boundaries and idempotency are structural. Retrofitting them means rewriting the feature, not patching it.
- Whether work belongs in the request path decides your API shape. Discovering it later changes the contract clients already depend on.
- Observability added after an incident is added to the wrong place — you instrument what broke, not what will.
What is actually happening
- The loop runs top to bottom because each answer constrains the next. What the contract promises decides what must be synchronous; what is synchronous decides what must be transactional; what is transactional decides what can be retried.
- It is a checklist for *questions*, not answers. "Nothing races here" is a valid, valuable answer — recorded, not assumed.
The loop
Each step is a question whose answer constrains everything below it. Read top to bottom; when an answer changes, everything under it is back in play.
- 1Requirement
What does the user actually need, in their words?
fails by Designing for the implementation someone already imagined.
- 2API Contract
What do clients call, and what are they promised?
fails by A contract that leaks your schema (Schema Leakage).
- 3Application Logic
What are the business rules, and where do they live?
fails by Rules scattered across handlers and ORM hooks.
- 4Data Access
What is read and written, how many round trips?
fails by N+1 discovered in production (The N+1 Query Problem).
- 5External Dependencies
What leaves the process, and what if it hangs?
fails by A call with no timeout (Timeouts).
- 6Concurrency
What happens if this runs twice at once?
fails by Lost updates on concurrent writes (Backend Races).
- 7Failure Handling
What can fail, and what does the caller see?
fails by Everything becoming a 500 (An Error Taxonomy That Maps Cause to Response).
- 8Security
Who may call this, on which objects?
fails by Authorization at the route instead of the object.
- 9Observability
How will I know it works, and debug it when it does not?
fails by Discovering there is no signal during the incident.
- 10Deployment
How does this ship without breaking the running version?
fails by A migration that assumes one version is live (Expand and Contract Migrations).
- 11Scale
What changes at 10x, and what becomes finite first?
fails by Working perfectly until it does not, with no warning signal.
Worked example: "let users export their data"
The loop turns a one-line ticket into a design. Note how the answer at step five — the export takes minutes — invalidates the obvious synchronous contract, and everything below adjusts.
| Step | Answer for this feature |
|---|---|
| Requirement | A user wants all their records, occasionally, as a file |
| API Contract | Not GET /export returning the file — it cannot finish in a request. POST /exports returns 202 with a job id |
| Application Logic | Collect the tenant's records, serialize, write to storage, notify |
| Data Access | A large scan; must stream and paginate rather than load everything (Pagination That Survives a Large Table) |
| External Dependencies | Object storage and an email provider — both need timeouts and retries |
| Concurrency | Two clicks must not produce two exports (Idempotency Keys) |
| Failure Handling | A partial export must never be delivered as complete; write to a temp key, then move |
| Security | Export only the caller's tenant; the download link must be scoped and short-lived (Presigned URLs) |
| Observability | Job duration, rows exported, failure count by cause, per tenant |
| Deployment | Workers and API deploy separately, so the job format must be readable by both versions |
| Scale | One tenant with 50M rows must not starve every other tenant's export (Bulkheads) |
How to build it
Most important first.
- Run the loop before writing code, at whatever depth the feature deserves. A read-only endpoint takes two minutes.
- Write the answers down in the PR description. Most of them are one line, and reviewers can then disagree with a decision rather than guess at one.
- Revisit at the two steps that most often change the design: failure handling and scale.
What can go wrong
- Running the loop as ceremony — answering every step "standard" — which is worse than not running it, because it looks like diligence.
- Stopping at Deployment and never asking the scale question, which is where the cheap decisions become expensive.
- The security step is not "did we sanitize inputs". It is: who may call this, on which objects, and what does an attacker get by calling it with someone else's id?
- "This is a design-doc process." It is eleven questions; most features answer them in a paragraph.
- "Steps I answer 'none' for are wasted." Recording that nothing races is exactly the value — it is a claim someone can challenge.
Operating it
- The observability step should produce concrete artefacts: this metric, this log field, this span. "We will add logging" is not an answer.
- The scale step asks what changes at 10x and 100x, and specifically what becomes finite first — pool, loop, worker set, memory or third-party quota.
- The loop costs time before code exists, which feels slow on a small feature and is genuinely unnecessary on some.
- Applied dogmatically it over-engineers. Depth should scale with blast radius.
Where this applies
Backend advice is context-sensitive. These labels say what each claim is specific to, and where a different stack or scale would differ.
- GENERALA reasoning process, not a technology; independent of stack.
- SCALE-SPECIFICThe last two steps matter far more above roughly one instance and one database. A single-instance internal tool can legitimately stop at Observability.
Where the depth lives
This domain teaches the application-side mechanism and hands the rest off.