The interviewer hands you a diff for a service that moves money and asks “what's missing?” The senior answer is a checklist you run, not a vibe you have.
Student edition · understand, apply, verify
Review threats, object-level authorization, input boundaries, retries, and rollout before trusting a transaction path.
What you should be able to do: Prioritize material security/privacy/correctness gaps with evidence and a safe concrete repair.
A context-dependent practice guide, not a universal policy or verified personal history. Examples and numbers are illustrative. Use genuine, authorized evidence; personnel/privacy/legal decisions follow applicable organization policies and qualified review.
Concepts explained in this guide
Read these definitions first, or use the linked terms as you go. The examples and limitations are local to this guide; companion pages are optional.
Authentication, authorization, and IDOR
Authentication establishes who is calling. Authorization determines what that principal may do to a particular object. IDOR/BOLA occurs when a caller can use an object ID to access or mutate another principal's data.
Example. A parameterized query safely treats an input as data, but still needs an owner/permission predicate from trusted authentication context.
Boundary. Never accept caller identity solely from request fields. Input validation, authentication, per-object checks, least privilege, and race-safe policy enforcement are separate controls.
Privacy, minimization, and pseudonymity
Personal data can identify or link a person directly or with other information. Minimize data to an approved purpose. Pseudonymity replaces an identifier but leaves possible linkage; anonymity requires a much stronger contextual assessment.
Example. Hashing an email preserves a stable value and may be dictionary-reversed. HMAC can reduce that attack for parties without the key, but remains linkable pseudonymous data.
Boundary. Legal basis, retention, rights/exceptions, audit access, and deletion of derived/backup copies require qualified context-specific review. Encryption or hashing alone is not compliance.
Idempotency keys
A client-generated identity for one logical operation. The server binds it to caller scope, endpoint, request fingerprint, state, and result. A retry uses the same key and reuses the original outcome rather than repeating the effect.
Example. Payment p42 times out after the provider charged it. Retrying with p42 lets the provider return the same charge instead of creating a new one.
Boundary. A unique local claim is insufficient if the business effect and saved result can diverge. Keep unknown outcomes pending, use downstream idempotency, and state the retention/retry horizon.
Optimistic concurrency and compare-and-set
Update state only if the expected version or status still matches, in the same atomic operation that changes it. A failed comparison means a conflicting operation won; reread or return a conflict.
Example. UPDATE seats SET status='held', version=version+1 WHERE id=? AND status='available' allows only one concurrent claimant to update the row.
Boundary. Application-level GET then unconditional SET is not equivalent. Include acquisition identity to reject stale callbacks, and couple related invariants in a transaction.
Preconditions and postconditions
A precondition states required inputs/state before an operation. A postcondition states the result promised when those requirements hold and execution terminates normally. Together they make requirements testable.
Example. For sum_first(b,n), require integer 0≤n≤len(b) and numeric elements; promise the sum of b[0:n].
Boundary. State exceptions, mutation, numeric domains, and empty cases. A mathematical array range does not automatically validate a runtime input.
This is the depth question in the code-review track. You're handed a pull request for a microservice that handles secure transactions — a payments path, a consent write, a credential exchange — and asked what a senior reviewer would flag. The trap is to comment on style and a missing null check and stop. The signal is that you run the change through a repeatable security-and-correctness checklist, name the classes of risk that would cause a SEV or a breach, and then describe how you'd deliver that feedback so it lands and the diff still ships. What follows is the checklist a senior reviewer runs, plus a wrong-versus-right snippet, plus how to give the feedback.
The checklist a senior reviewer runs on a security-critical change: missing tests on the failure paths, unhandled errors, concurrency on shared state, secrets handling, authn/authz, input validation, idempotency for money movement, encryption, PII in logs plus an audit trail, rate limiting, supply-chain risk, and a safe rollout.
What this question is really testing
Whether you have an internalized threat model for code that touches money, secrets, or user data — and the discipline to apply it the same way every time rather than relying on catching something clever in the moment. The interviewer wants to hear that you review for the classes of failure that hurt (a breach, a double-charge, a leaked secret, a data-corruption SEV), that you weight the failure and error paths as heavily as the happy path, and that you can prioritize: what actually blocks the land versus what's a follow-up. They're also listening for whether you'd deliver a security block without shaming the author, because a reviewer who's feared gets routed around.
How to answer
Start with the threat model, not the code. Say out loud what this service protects — money movement, credentials, PII — and who the adversary is. That framing is what makes the rest of the checklist purposeful rather than a laundry list.
Run the checklist and name the classes. Missing tests on the failure paths; unhandled exceptions and error paths; races and concurrency on shared state; secrets handling; authn/authz; input validation and injection; idempotency; encryption; PII in logs and an audit trail; rate limiting and abuse; dependency and supply-chain risk; and a safe rollout. Group them — don't just recite.
Weight the failure paths. Most bugs in a transaction service hide in the error branch: the retry that double-charges, the exception that leaves a half-written record, the timeout with no compensation. Say explicitly that you review the unhappy paths hardest.
Separate optional instrumentation from detection/recovery controls required for safe operation. Whether it blocks follows material risk and the agreed release bar, not a blanket category rule.
Show how you'd give the feedback. Lead with the risk and the reasoning, link the standard or a past SEV, and offer the fix — “this reads the key from an env var; route it through the secrets manager, here's the pattern” — so the block is a path forward, not a wall.
What the interviewer is looking for
A threat model stated first, so the review is purposeful, not a checklist recital.
The high-severity classes named — secrets, authz, injection, idempotency — not just tests and null checks.
The failure and error paths reviewed as hard as the happy path.
Clear blocking-versus-follow-up prioritization.
Feedback framed to teach and unblock, not to shame — a security reviewer people want, not avoid.
The checklist a senior reviewer runs
Group your findings the way you'd group them in the review so the author can act on them. Everything above the fold blocks a land; the operational hardening is where you separate must-fix from fast-follow.
Area
What you look for
Why it’s here
Tests & failure paths
Unit and integration tests that exercise the error branches, retries, and partial failures — not just the happy path
The bugs that hurt live in the paths no one tested
Error handling
No unhandled exceptions; no bare except; errors surfaced, not swallowed; resources released on the failure path
A swallowed error in a money path is a silent data bug
Concurrency
Races on shared mutable state; check-then-act on balances; correct locking or an atomic compare-and-set
Two concurrent requests must not both spend the same funds
Secrets & credentials
No hardcoded secrets or keys; pull from a secrets manager; least-privilege scopes; rotation possible
A committed secret is a breach waiting in git history
AuthN / AuthZ
Caller is authenticated and authorized for this resource; no missing object-level checks (IDOR)
Authn without per-object authz is the classic escalation
Input validation & injection
Validate and bound all external input; parameterized queries; no shell string-building; no unsafe deserialization
SQL, command, and deserialization injection all start here
Idempotency
Money movement carries an idempotency key; a retry or duplicate delivery cannot double-apply
At-least-once delivery means retries will happen
Encryption
TLS in transit; sensitive fields encrypted at rest; correct algorithms, no home-rolled crypto
Transactions and PII must be protected on the wire and on disk
PII in logs & audit
No card numbers, tokens, or PII in logs or error messages; a tamper-evident audit trail of who did what
Logs leak, and money movement needs a forensic record
Rate limiting & abuse
Per-caller limits, bounded retries with backoff, and abuse/fraud guards on the endpoint
An unbounded money endpoint is a fraud and DoS surface
Dependencies
New libraries vetted, pinned, and scanned; no unmaintained or typo-squatted packages
Supply-chain risk enters through a one-line import
Safe rollout
Behind a gate/flag; canary or staged ramp; a fast, tested rollback
You want to turn a bad change off in minutes, not redeploy
Wrong versus right
The runnable lab uses synthetic data and no credentials. A complete production payment handler additionally needs trusted authentication, per-object authorization, amount/currency validation, a transaction/provider idempotency contract, durable unknown-outcome reconciliation, classified audit policy, and recovery tests.
# Architecture contract, not a production handler:
principal = trusted_authentication_context()
validate_amount_currency_and_operation_identity()
authorize(principal, requested_account)
execute_local_transaction_or_provider_keyed_operation()
# Timeout: persist unknown/reconciling; do not claim the effect failed.
# Diagnostics omit sensitive data; authorized audit is separately governed.
Common follow-ups
You have limited time — what do you check first?
How to answer
Triage by blast radius. Secrets, authz, and injection first — each is a breach or escalation on its own.
Then correctness of money movement. Idempotency and the race on shared balance state, because those cause silent financial loss.
Severity depends on the actual gap. Logging secrets or unjustified personal data can be blocking; missing critical detection/recovery controls can also block. Defer only low-risk improvements under an owner/date.
How do you review the parts you're not a security expert in?
How to answer
Review what you can judge. Tests, error paths, input handling, and clear intent don't need a specialist.
Pull in a security reviewer. Route crypto and threat-model questions to the security team or a code-owner rather than rubber-stamping.
Turn gaps into questions. “What stops a replay here?” often surfaces a real hole and teaches you the area.
The author says the missing tests will come in a follow-up diff. Do you block?
How to answer
Block tests on the failure paths. For a money path, the error-branch tests are part of the change, not a follow-up.
Be pragmatic on the rest. A non-critical happy-path test can land as a fast-follow with a tracked task.
Anchor on the standard. “Our bar is tested failure paths for transaction code” makes it the policy talking, not you.
How do you deliver a hard security block without alienating the author?
How to answer
Lead with the risk and the why. “This can double-charge on retry” beats “this is wrong.”
Offer the fix. Link the secrets-manager pattern or the idempotency helper so the block is a path forward.
Separate person from code. Praise the good parts; make clear the bar is the arbiter, not your taste.
Evidence to collect with your approved tools
A decision/review record with the problem, constraints, options, and your contribution.
Relevant tests, dashboards, or measurements with definitions, denominators, dates, and uncertainty.
Incident or delivery records showing actual outcomes and unresolved limitations.
Stakeholder agreements and follow-up actions with owners and review dates.
Only authorized, minimized, safe-to-share evidence. Do not copy confidential personnel notes into a public story.
Worked example: authenticated is not authorized
This is an illustrative situation. The point is the reasoning and evidence, not copying the numbers or first-person story.
Sequence for this example. The table explains the evidence and decision at each step.
Step
Evidence, decision, and expected result
Authenticate the principal
Trusted middleware establishes caller identity; do not accept caller/owner identity from an arbitrary payload.
Authorize the object
The lookup/update includes the caller's permitted object scope. SQL parameters prevent injection but do not themselves provide authorization.
Check atomic money/retry semantics
A conditional debit and durable idempotency outcome share their boundary; remote timeout remains unknown until reconciled.
Inspect secrets/logging/rollout
No embedded credential examples; diagnostic identifiers can still be classified personal data. Missing material log/privacy controls are not automatically harmless fast-follows.
Check your understanding: Does a parameterized query fix IDOR?
No. IDOR is unauthorized object access; include trusted-principal authorization and object scope in addition to parameterization.
Run and change the example
Download guide_labs.py. Python 3.10+, standard library only. In its directory, run:
python3 guide_labs.py security
python3 guide_labs.py test
The synthetic SQLite lab checks parameterized values and caller-scoped object lookup, including an injection-like string and a different owner.
The program tests its stated finite contracts. It is an educational model, not an implementation of the named production product or a proof of unmodeled distributed faults.