FoundationsGENERALSCALE-SPECIFIC

The Backend Reasoning Loop

Eleven questions to ask of any feature, in an order that surfaces the expensive decisions early.

What actually happensHow to build it

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.

The question

Is there a repeatable way to reason about a backend feature instead of discovering its problems in production?

The requirement

A ticket says "let users export their data". You have to turn that into a design that survives contact with real traffic.

The obvious build

Write the endpoint, then handle problems as they appear in review or in incidents.

Why it breaks

Transaction boundaries and idempotency are structural. Retrofitting them means rewriting the feature, not patching it.

How it breaks in production
  • 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.
RequirementAPI ContractApplication LogicData AccessExternal DepsConcurrencyFailureSecurityObservabilityDeploymentScale

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.

Eleven questions for any feature
  1. 1
    Requirement

    What does the user actually need, in their words?

    fails by Designing for the implementation someone already imagined.

  2. 2
    API Contract

    What do clients call, and what are they promised?

    fails by A contract that leaks your schema (Schema Leakage).

  3. 3
    Application Logic

    What are the business rules, and where do they live?

    fails by Rules scattered across handlers and ORM hooks.

  4. 4
    Data Access

    What is read and written, how many round trips?

    fails by N+1 discovered in production (The N+1 Query Problem).

  5. 5
    External Dependencies

    What leaves the process, and what if it hangs?

    fails by A call with no timeout (Timeouts).

  6. 6
    Concurrency

    What happens if this runs twice at once?

    fails by Lost updates on concurrent writes (Backend Races).

  7. 7
    Failure Handling

    What can fail, and what does the caller see?

    fails by Everything becoming a 500 (An Error Taxonomy That Maps Cause to Response).

  8. 8
    Security

    Who may call this, on which objects?

    fails by Authorization at the route instead of the object.

  9. 9
    Observability

    How will I know it works, and debug it when it does not?

    fails by Discovering there is no signal during the incident.

  10. 10
    Deployment

    How does this ship without breaking the running version?

    fails by A migration that assumes one version is live (Expand and Contract Migrations).

  11. 11
    Scale

    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.

StepAnswer for this feature
RequirementA user wants all their records, occasionally, as a file
API ContractNot GET /export returning the file — it cannot finish in a request. POST /exports returns 202 with a job id
Application LogicCollect the tenant's records, serialize, write to storage, notify
Data AccessA large scan; must stream and paginate rather than load everything (Pagination That Survives a Large Table)
External DependenciesObject storage and an email provider — both need timeouts and retries
ConcurrencyTwo clicks must not produce two exports (Idempotency Keys)
Failure HandlingA partial export must never be delivered as complete; write to a temp key, then move
SecurityExport only the caller's tenant; the download link must be scoped and short-lived (Presigned URLs)
ObservabilityJob duration, rows exported, failure count by cause, per tenant
DeploymentWorkers and API deploy separately, so the job format must be readable by both versions
ScaleOne 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

Failure modes
  • 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.
Security
  • 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?
Misreads
  • "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

How you see it in production
  • The observability step should produce concrete artefacts: this metric, this log field, this span. "We will add logging" is not an answer.
What changes at 10x and 100x
  • 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.
What this costs
  • 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.