Warming up the neural circuits...
By the end of this chapter you will:
Most backend outages are not caused by missing endpoints. They are caused by data-layer mistakes:
The difference between demo code and production-grade persistence is intentionality:
This chapter is the transition from "ORM user" to "data-access engineer."
from sqlalchemy import select
def list_task_summaries(db):
stmt = (
select(Task.id, Task.title, Task.status, User.name.label("assignee_name"))
.join(User, Task.assignee_id == User.id, isouter=True)
.order_by(Task.created_at.desc())
.limit(50)
)
rows = db.execute(stmt).all()
return [
{
"id": r.id,
"title": r.title,
"status": r.status,
"assignee_name": r.assignee_name,
}
for r in rows
]Returning whole ORM objects for list endpoints usually over-fetches and triggers accidental lazy loads.
from sqlalchemy.exc import SQLAlchemyError
def move_task(db, task_id: int, new_project_id: int) -> None:
try:
task = db.get(Task, task_id)
if not task
Common strategies:
SELECT ... FOR UPDATE,from sqlalchemy import select
def reserve_ticket(db, ticket_id: int, user_id: int) -> None:
stmt = select(Ticket).where(Ticket.id ==
For PostgreSQL, explicit upsert can be clearer than forcing generic ORM patterns.
from sqlalchemy.dialects.postgresql import insert
def upsert_metric(db, key: str, value: int) -> None:
stmt = insert(Metric).values(key=key,
Debug loop:
EXPLAIN (ANALYZE, BUFFERS).Optimization without plan visibility often makes systems slower, not faster.
Useful boundaries:
This keeps high-read endpoints from inheriting write-path complexity.
The fastest query is the one you never execute. The second fastest is the one that returns exactly the columns and rows you need.
| Situation | Better default |
|---|---|
| Endpoint list with related fields | Projection query plus explicit join |
| Single-row update with conflict risk | Short transaction plus lock or version check |
| High-volume idempotent writes | Postgres upsert statement |
| Latency regression after growth | Query-plan first investigation |
| Complex data access rules | Repository/query-object boundaries |
| Mistake | Why it hurts | Better move |
|---|---|---|
| Returning full ORM entities for list APIs | Over-fetching and hidden lazy queries | Use shaped projections |
| Long transaction blocks around network calls | Lock contention and deadlocks | Keep transactions pure DB scope |
| Missing rollback on write failures | Session stuck in failed | Always rollback before reuse |
| Blindly adding indexes | Write overhead with weak read gains | Index from measured query patterns |
| Guessing optimization fixes | Regressions and false confidence | Use EXPLAIN and measured baselines |
Task list endpoint returning full ORM modelsShaped response with minimal selected fieldsCreate project then create first taskAll-or-nothing behaviorShared ticket rowSingle successful reservationMetric key and current valueInsert on first write, update on conflictSlow query log sampleRoot cause and optimization recommendationBeginner:
"Why is one session per request a common backend pattern?"
It creates clean transaction boundaries and avoids cross-request state leakage.
"What is N+1 and why does it matter?"
It is repeated child-query execution per parent row, which can explode query counts and latency.
Senior:
"How do you choose between optimistic and pessimistic concurrency control?"
Choose optimistic when conflicts are rare and latency matters; choose pessimistic when correctness under contention is critical.
"How do you prevent ORM abstractions from hiding performance issues in production?"
Enforce query-shape reviews, instrument query counts, and validate generated SQL with explain plans in critical paths.
Shape query intentionally
Keep transactions short
Protect concurrent writes
Measure query plans before optimizingWhy are projection queries often better than returning full ORM entities for list endpoints?
Keep transactions narrow in time and scope to reduce lock contention.
Locking can protect correctness but increases contention if held too long.
Bulk operations need careful tradeoff between speed, , and side effects.