Last hour — read this, then stop
On this page11
- The three rules
- One line per answer
- Status codes that get confused
- Methods
- Postgres isolation
- Migrations on a busy table
- Index rules
- Retry or not
- Facts with dates
- Healthcare vocabulary
- Your numbers
Last hour — read this, then stop
One page for the hour before the call. Nothing here is new — it is the
compressed form of Answers — say these out loud and
5 October — Senior Python Backend Engineer — prep map. After this, no new material.
The three rules
- 60 to 90 seconds, reasoned aloud. Context → options → decision →
trade-off → result. A correct fifteen-second answer reads as mid-level.
- Never stop at “I don’t know”. “I haven’t used X, but I’ve done the same
thing with Y, where the trade-off was…”
- Slow down. Twenty percent slower than feels natural. Short sentences.
Close every answer: “So that’s how I’d approach it.”
One line per answer
The sentence each answer in Answers — say these out loud is built around.
- Concurrency — asyncio for many waiting connections, threads for sync I/O libraries, processes for CPU-bound Python. Free-threading is official in 3.14, but opt-in.
- Blocked loop — one blocking call stalls every request in the worker: p99 up everywhere, CPU idle. A
def route, a thread, or an async driver.
- Memory — refcounting frees at zero, the cycle collector handles cycles, RSS rarely shrinks — so peak usage sizes the process.
- What’s new — 3.14: free-threading, subinterpreters, t-strings, lazy annotations. 3.15 on 9 October: lazy imports,
frozendict, UTF-8 by default.
- FastAPI or Django — FastAPI for I/O-heavy API services, Django when the ORM, admin and batteries carry the product. The framework stays at the edge.
- REST design — nouns plus methods, meaningful status codes, one error shape, cursor pagination, additive changes, idempotency keys on POST.
- Retrying a POST — a client-generated key;
INSERT ... ON CONFLICT DO NOTHING decides the race; same key with a different body is a 422.
- Slow query —
pg_stat_statements to find, EXPLAIN (ANALYZE, BUFFERS) to understand, one fix, measure again.
- Concurrent updates — Read Committed loses read-modify-write updates. A version column when rare,
FOR UPDATE when hot, a constraint when possible.
- Migrations — old and new code run together: expand/contract,
CONCURRENTLY, NOT VALID then VALIDATE, batched backfills, always lock_timeout.
- Flaky third party — adapter at the boundary; timeouts first; retry only transient errors, with backoff and jitter; circuit breaker; a queue if nobody waits.
- Webhooks — HMAC over the raw body, acknowledge fast, dedupe on the event id with a unique constraint, reconcile by polling.
- Service to service — sync only when the caller needs the answer; events for side effects; an outbox for the dual write; idempotent consumers.
- Background jobs — idempotent tasks,
acks_late, backoff, a DLQ someone reads, separate queues, scale on queue depth.
- Mocking — stub what you do not own at the transport; never mock the database — real Postgres, rolled back per test.
- Monolith or microservices — modular monolith first, split on a concrete pain, strangler fig, data last.
- Scale 10× — the stateless tier scales out; the database is the ceiling: queries, pooling, cache, replicas, then partitioning.
- Code review — correctness, design, operability, tests; tools own style; label blocking vs nit; small PRs.
- Architecture decision — effort scales with the cost of reversal; options and trade-offs in an ADR; what we gave up, written down.
- Incident — mitigate first, then diagnose; split coordination from investigation; blameless postmortem; detection time is the lever.
- Patient data — encryption, per-request authorisation, an append-only audit log, minimisation, never in logs; a BAA or DPA with every vendor.
- FHIR and HL7 — FHIR R4 over REST for anything new, HL7 v2 over MLLP in hospitals; conformance is to a profile; idempotent on the message id.
- An LLM with patient data — a BAA with no retention, de-identification, or in-boundary inference — decided first; then an unreliable dependency like any other.
Status codes that get confused
| Code |
Use it for |
| 400 vs 422 |
unparseable request vs well-formed but invalid (FastAPI’s validation default) |
| 401 vs 403 |
not authenticated vs authenticated but not allowed |
| 404 instead of 403 |
a resource in another tenant — a 403 would confirm it exists |
| 409 |
conflicts with current state: a stale version, a duplicate, a key still in flight |
| 412 |
an If-Match precondition failed — the lost-update guard |
| 429 |
rate limited; send Retry-After |
| 202 |
accepted for async processing; return a status URL |
| 502 / 503 / 504 |
bad upstream / unavailable / upstream timeout — retryable |
Methods
| Method |
Safe |
Idempotent |
| GET, HEAD |
yes |
yes |
| PUT, DELETE |
no |
yes |
| PATCH |
no |
depends on the body |
| POST |
no |
no — give it a key |
Postgres isolation
| Level |
What it means in Postgres |
| Read Committed (default) |
each statement sees what was committed before it started; read-modify-write in the app can lose updates |
| Repeatable Read |
one snapshot per transaction; a concurrent update to the same row raises a serialization error — retry |
| Serializable |
also detects write skew; retry on serialization failure (SQLSTATE 40001) |
MySQL InnoDB defaults to Repeatable Read — the other half of the classic
question.
Migrations on a busy table
| Operation |
Safe? |
| add a nullable column, or one with a constant default |
yes — metadata only (constant defaults since PG 11) |
add a column with a volatile default, such as gen_random_uuid() |
no — rewrites the table |
SET NOT NULL directly |
scans under an exclusive lock — CHECK ... NOT VALID, VALIDATE, then SET NOT NULL |
CREATE INDEX |
blocks writes — use CONCURRENTLY, outside a transaction |
| add a foreign key |
NOT VALID first, then VALIDATE CONSTRAINT |
| change a column’s type |
usually a rewrite — a new column and a backfill instead |
| rename or drop a column |
fast, but breaks running code — expand/contract |
| any DDL |
SET lock_timeout first, and retry on timeout |
Index rules
- Equality columns first, the range column last — a composite index is sorted by the tuple.
- A function, a cast or a leading
% on the column defeats the index; an expression index fixes the function case.
- Foreign keys are not indexed automatically — an unindexed FK makes deleting a parent scan the child table.
- When a query matches more than roughly 5-10% of the table, a sequential scan is the right plan.
- A low-cardinality filter wants a partial index;
INCLUDE columns enable index-only scans.
- Every index taxes every write;
pg_stat_user_indexes with idx_scan = 0 finds the dead ones.
Retry or not
| Retry |
Do not retry |
| connection errors and timeouts, on idempotent calls |
4xx other than 429 |
| 502, 503, 504 |
500 without thought — often a deterministic bug |
429, honouring Retry-After |
a write without an idempotency key |
Exponential backoff with full jitter, a low attempt cap, retries owned by one
layer, deadlines propagated. A retry is load.
Facts with dates
| Fact |
As of 2026-10 |
| Python |
3.14 current; 3.15.0 final due 9 October 2026 (rc3 shipped 2 October) |
| Free-threading |
officially supported in 3.14 (PEP 779); an opt-in python3.14t build |
| FastAPI threadpool |
40 threads by default, shared by sync routes and sync dependencies |
| pymalloc |
objects up to 512 bytes; anything larger goes to the system allocator |
| Django |
6.1 current, 5.2 the LTS |
| PostgreSQL |
18 current — async I/O, uuidv7(), virtual generated columns |
| Problem Details |
RFC 9457, which replaced RFC 7807 in 2023 |
| FHIR |
R4 is what is deployed; R5 has limited adoption; R6 is in ballot |
| EU AI Act |
transparency obligations and GPAI enforcement apply from 2 August 2026; high-risk obligations delayed to Dec 2027 / Aug 2028 |
Healthcare vocabulary
| Term |
One line |
| PHI |
protected health information — health data tied to an identifiable person |
| HIPAA |
US rules for covered entities and their business associates: privacy, security, breach notification |
| BAA |
business associate agreement — required with every vendor that touches PHI: cloud, LLM API, error tracker |
| GDPR Art. 9 |
EU: health data is a special category — it needs an Art. 9 condition on top of a lawful basis, plus minimisation, DPIAs, erasure rights |
| DPA |
GDPR’s controller-processor contract (Art. 28) — the BAA’s European counterpart |
| Audit log |
who accessed which record, and when — append-only, kept for years, a functional requirement |
| De-identification |
Safe Harbor (remove the 18 identifiers) or Expert Determination |
| FHIR |
REST and JSON resources — Patient, Observation, Encounter; profiles make them strict |
| HL7 v2 |
pipe-delimited hospital messages over MLLP, with ACK/NAK; resent on timeout |
| LOINC / SNOMED CT / ICD-10 / RxNorm |
lab tests / clinical terms / diagnoses / medications |
Your numbers
Fill these in before the call. An answer with one of them in it lands; an
answer without one sounds read from a page.
- Years: “over five years” — the resume says 5+, so say exactly that
- Your best query fix: [before] → [after], and the cause: [ ]
- Your busiest service: [requests per second, or users]
- Your biggest table: [rows]
- An incident: [what broke, how long, how you found out, what changed]
- A mentoring result: [who, what changed, how you could see it]
- A decision you drove: [the options, the choice, what it cost]