Blog · 2026-01-28 · 14 min read
Amazon Backend Interview Questions
What to expect in an Amazon backend loop, coding and system design, the Leadership Principles and what an interviewer probes for each, failure handling, consistency tradeoffs and a worked STAR answer.
Amazon's loop is unusual in how heavily it weights behavioural evidence. Every interviewer is calibrated against the Leadership Principles, so a technically correct answer can still fail if the reasoning behind it is not visible. This guide is based on Amazon's published Leadership Principles, public engineering writing, and publicly documented job requirements. Where candidates report on the shape of the loop, that is flagged as reported rather than official, because the loop genuinely varies by role, level, team and interviewer.
What the loop looks like
Publicly documented and widely reported for backend roles, you can expect some combination of:
- Coding rounds, usually data structures and algorithms, graded on clarity and edge-case handling rather than cleverness.
- System design, more of it at senior level and above, where the discussion goes to scale and failure rather than a single component.
- Behavioural and Leadership Principle questions woven into the technical rounds, not separated into their own block.
- A bar raiser, a senior engineer from outside the hiring team, whose explicit job is to compare against the level bar.
Treat that as a shape, not a schedule. Reported experiences differ on the number of rounds, whether design comes before or after coding, and how long the loop takes to schedule. Nobody publishes an official per-stage question list. What is consistent is that the technical and behavioural assessments are both pass/fail and both are graded against a written rubric, so an outstanding coding round does not offset a failed behavioural one.
Prep for the intersection, not for one half. If you only practise algorithms you can lose on Ownership. If you only have stories you can lose on the code.
The Leadership Principles are the spine
The most-quoted framing is the "sixteen principles" version, and you will meet that number from interviewers who learned it years ago. Amazon has revised the published list over time, and some names have shifted slightly, so treat the current published set on Amazon's own site as authoritative and treat any remembered numbering as approximate. What has not changed is the substance, and that is what is graded.
These are the ones that come up most, with what an interviewer is actually listening for.
Customer Obsession. Start from the customer and work backwards. The probe is whether you can name the specific customer pain you were solving, and whether you would have cut scope differently if you could not name it. A strong signal is saying which customers you decided not to optimise for.
Ownership. Leaders own the whole outcome, not their ticket, and never say "that is not my job". The probe is what you did after the feature shipped, and whether you touched something outside your team without being asked. The classic weak answer is a story that stops at the code review.
Dive Deep. They will push several layers into one technical story: why this design, what the alternatives were, what the numbers are, what you would change. The probe is depth of reasoning under questioning, not how big the project was. A small project you understand completely beats a large one you describe from the outside.
Earn Trust. Write and speak the truth, and say the uncomfortable thing early. The probe is whether you raised a risk before it was convenient, and how you reacted when you were wrong.
Bias for Action. In the absence of complete information, act quickly, while gathering more information. The probe is how you set a reversible versus irreversible decision. A good answer distinguishes the two explicitly.
Deliver Results. Focus on the few most important things and deliver them, over doing a lot of mediocre work. The probe is whether you scoped aggressively and what you consciously did not build.
Insist on the Highest Standards. Quality matters because internal quality translates to external quality, even in code nobody reads. The probe is a time you refused to lower the bar, and how you handled pushback about it.
Frugality. Be resourceful, deliver results with the least possible resource. The probe is a concrete cost or effort saving you drove, with a number. This one catches people out because engineers rarely volunteer it.
Invent and Simplify. Invent new approaches when old ones no longer work, and simplify anything faster than you simplify code. The probe is a simplification you made and what you deleted.
Build a story bank and tag each story to two or three principles. The point of tagging is that when a behavioural question arrives, you are not composing an answer under time pressure, you are selecting one you already know. Prepare roughly eight to ten stories so that no single principle depends on a single story, and so you can reuse a story from a different angle rather than repeating it verbatim.
STAR, and why Situation should be the shortest part
Situation, Task, Action, Result. The failure mode is spending forty percent of the answer setting the scene. The listener does not need your org chart; they need enough to judge your decisions.
Keep Situation to a couple of sentences: what the system or business was, what was broken or at stake, and your role. Spend the bulk in Action, because that is the part only you can be graded on and the part Dive Deep will probe. End in Result with a number and, where honest, a thing that remained unsolved.
A worked answer
Situation: our order service published a payment-captured event to the fulfilment service. The consumer had just been rewritten by another team to retry aggressively on any 5xx. The producer predated the concept of idempotency keys, so there was no way for the consumer to recognise a redelivery. Task: I owned the fix after we found roughly one in every two hundred and fifty orders had been placed twice over a two-week window, and support was manually refunding them. Action: I started from the incident data rather than the code. Querying the consumer's table for duplicates and joining back to the event log showed that a single burst of 5xx responses during a deploy had produced, for one order, eleven deliveries. That reframed the problem: this was not a consumer bug that happened to duplicate rows, it was unbounded fan-out that would eventually do the same for any event type. So I fixed it at both ends. On the producer I added an idempotency key equal to the event ID and made retries bounded, with exponential backoff and jitter. On the consumer I added a unique constraint on that key and made a duplicate insert a success rather than an error, because retrying a completed unit of work is exactly what caused this. I also added a dashboard on duplicates per thousand orders and an alert, because the reason it went unnoticed for two weeks was that we had no signal for it. Finally I wrote up the pattern and got it added to the team's service checklist. Result: duplicate orders went to zero within eleven days, manual refunds dropped to nothing, and the alerting caught two new instances the following quarter before either reached a customer. Dive Deep follow-up I was ready for: I can explain why I used a database unique constraint rather than a Redis set for de-duplication, since Redis as the system of record would have lost the guarantee on eviction and restart.
System design at Amazon scale
Two features of the Amazon framing are worth naming, because interviewers notice whether you use them.
Working backwards. You are given a customer need and a very large scale assumption, and you are expected to derive the architecture from the need rather than from a component wish list. The widely reported practice of designing for a hundred million users is a forcing device: the number exists to stop you choosing a single-server design by accident. The useful habit is estimating before designing. Take a user count, derive daily active users, derive requests per user per day, divide by seconds to get peak QPS with a factor for skew, then derive storage from bytes per entity and retention. Say your assumptions out loud as you go, because the estimation method is what is being graded, not the arithmetic.
Cost and operational simplicity as first-class constraints. If a design adds a new datastore, that is a new on-call burden, a new backup story and a new failure mode. Frugality and Deliver Results both push you to reuse what exists unless you can state why a new component is cheaper than the operational tax.
The shape of the discussion is often longer and shallower than a Google-style design round, with more time spent on one component's failure modes, backpressure, and how you would operate it. Expect to be interrupted from the diagram and asked "what happens when this dependency is slow". Have a cost estimate for your proposal, because Frugality gets asked.
Data modelling and schema design
Denormalisation is a normal answer, not a wrong one. Pre-join the data your primary read path needs into a wide table or a document, accept redundant copies of denormalised fields, and update them asynchronously where the read path tolerates it. The cost is write amplification and a second code path for repairs, and you should say that you know the cost. For a read-heavy workload at scale, paying it deliberately usually beats paying it accidentally in N+1 queries.
Sharding strategy. Pick the shard key from the dominant access pattern, not from whatever makes the data look tidy. A shard key that makes every query fan out to every shard has not saved you anything, because you have traded one database problem for a scatter-gather problem. You should be able to state your key, the one query it optimises, and honestly whether it introduces a hot shard for your busiest tenant.
Hot keys are the question that separates a candidate from a memoriser. A shard key of customer_id is fine until one enterprise customer is a large fraction of traffic, at which point that shard saturates while the rest idle. Mitigations to be able to name: add a sub-key suffix to spread a single hot entity, cache or precompute the hot path, replicate reads to a follower with a staleness bound, and detect skew with a per-shard metric rather than discovering it in an incident.
Failure handling
Retries must back off and jitter. Naive fixed-interval retries from many clients at once produce a thundering herd, and the retry storm it creates is usually worse than the original outage.
import random
import time
from typing import Callable, TypeVar
T = TypeVar("T")
def retry_call(
fn: Callable[[], T],
*,
attempts: int = 5,
base_delay: float = 0.1,
max_delay: float = 5.0,
) -> T:
"""Retry a transient failure with exponential backoff and full jitter.
Only pass idempotent operations to this. Catch narrowly: an
`except Exception` here will happily retry your validation errors.
"""
if attempts < 1:
raise ValueError("attempts must be >= 1")
for attempt in range(attempts):
try:
return fn()
except Exception:
if attempt == attempts - 1:
raise
ceiling = min(max_delay, base_delay * 2 ** attempt)
time.sleep(random.uniform(0, ceiling))
The key line is random.uniform(0, ceiling), which is what AWS's architecture blog calls full jitter. Randomising across the whole interval rather than sleeping a fixed amount means clients that failed together do not retry together. Note what the function does not do: retry forever. Every retry system needs a bound, because unbounded retries convert a dependency degradation into your own resource exhaustion.
Idempotency is what makes retries safe, so it has to be designed in before the retry exists. Carry an idempotency key, enforce uniqueness where it cannot be raced, and treat a duplicate as success rather than as an error.
def create_charge(request, idempotency_key: str):
# The unique constraint on idempotency_key in the database is the real
# guarantee; the read below is only an optimisation to avoid the write.
existing = store.get(idempotency_key)
if existing is not None:
return existing
try:
charge = payments.charge(request)
except TimeoutError:
# Unknown outcome: do not guess, reconcile by key later.
schedule_reconciliation(idempotency_key)
raise
store.put(idempotency_key, charge) # unique index rejects a racing duplicate
return charge
The TimeoutError branch is the part people skip. A timeout does not mean the charge failed; it means you do not know. The correct response is to record the key and reconcile, not to retry blindly.
Circuit breakers stop calling a dependency that is failing, after a threshold of failures, so that failures are fast instead of slow and you stop consuming the thread pool waiting on a dead service. Three states: closed and calling through, open and failing immediately without calling, half-open and letting a single probe through to test recovery. Open it on a failure ratio over a window rather than a single failure, and put a cap on the open duration.
Dead-letter queues take messages that have exhausted their retries. The design questions are what you actually do with them: alert on depth, because a quiet DLQ is a data-loss incident nobody noticed; retain them long enough to debug; and make replay idempotent, since you will replay them. A DLQ with no alerting is a place where work goes to be lost.
Consistency versus availability, and what a ledger must choose
Name the trade explicitly and then commit to a side with a reason. Availability for reads and strong consistency for writes is a common and defensible split: you show a slightly stale balance immediately and reconcile, rather than refusing to show a balance at all.
A payments ledger should choose consistency, and the reason is that the ledger is the authoritative record and its whole value depends on being right. Specifics to be ready for:
- Append-only entries. Balances are derived, never updated in place, so there is an audit trail and a repair path.
- Double-entry. Every movement is a set of balanced postings, so the sum is a constant you can assert on, and a bug shows up as an invariant violation rather than a wrong number nobody notices.
- No distributed transaction across services. Hold a single-writer partition, and reconcile across partitions asynchronously with an idempotent process.
- Every write carries an idempotency key with a unique constraint, so a retry cannot create a second posting.
- Read your own writes, and if you cannot, accept and document the staleness bound rather than hoping.
Interviewers at Amazon specifically probe whether you will sacrifice availability for correctness here, and the answer that lands is: yes, for the ledger, and here is exactly what I give up and how I compensate.
Optimisation questions
"What would you optimise" is usually a trap, because the expected answer is a measurement plan. Say what you would measure first, in this order: the saturation metric that tells you which resource is actually the constraint, latency percentiles rather than averages, error rate, and then the saturation point itself. Then say how you would know if the change helped, which means a defined before-and-after comparison on the same traffic mix, and what you would do if it did not.
Specific to the Amazon loop: name the cost. A change that reduces p99 latency by a large percentage but increases monthly spend meaningfully needs to be justified, and Frugality is the principle under which it gets questioned. Being able to say "this trades a percent of spend for a reliability improvement, here is the number" is a strong answer.
Behavioural: ambiguity, disagreement, and failure
Ambiguous problem. Strong answers establish what was actually ambiguous before acting, name a way to resolve part of it cheaply and quickly, and set an explicit revisit point rather than guessing indefinitely. Weak answers say "I just made a reasonable assumption" with no mechanism.
Disagreement with a stakeholder. Strong answers separate the disagreement about outcomes from the disagreement about evidence, show that you actually listened and that their constraint was real, and end with either a decision or an explicit agreed experiment with a date. Watch for answers where the stakeholder was simply wrong; that reads as not having listened. Also watch for "I convinced them" with no account of what you learned.
A project that failed. Ownership, so this one is not optional. Strong answers own the failure in the first person, were specific about their own contribution to it rather than the team's, say what the technical or judgement error was, and describe the change that was made afterwards with evidence it stuck. The strongest version volunteers a failure that was their decision, not a failure that happened to them.
Questions to ask at the end
Ask about the thing that determines whether you would be good at the job. What does the team consider its hardest problem right now, and what happens to whoever owns it. How the team decides what to build, since that reveals whether your opinions are wanted. What the failure mode was in the last incident and what changed afterwards, which tells you whether the organisation is honest about problems. Whether on-call is shared, and what the pages actually look like. Whether the bar for this level was described concretely anywhere.
Do not ask about the process in a way that signals you are not interested in the problem, and do not skip the questions entirely, which reads as disengagement.
Common mistakes
Vague answers with no specifics. "I improved reliability" tells an interviewer nothing; "I cut p99 checkout latency in half by batching writes and added a circuit breaker around a dependency that was taking out the service monthly" is gradable.
No metrics anywhere, including in the Result. Some candidates are embarrassed by numbers they do not have. Use a relative figure honestly hedged, and if you genuinely cannot recall the number, say what you remember and why it matters.
Blaming former managers or colleagues. You will be assessed on how you handle a difficult person, and attributing a failure upward is the answer that fails.
Over-claiming. Saying "I designed the system" for something you built one service of is easy to catch, and Dive Deep will catch it. Say exactly what you owned.
Team stories with no individual contribution. "We decided" and "we built" throughout means the interviewer cannot tell what they are hiring you for.
Finally: treating the behavioural round as a formality. On an Amazon loop it is a pass/fail gate against a rubric, and it is the one most candidates under-prepare for.
Related reading: the system design cheat sheet covers the architecture mechanics behind the design rounds, the Go post covers the concurrency and error-handling detail a systems answer may be probed on, and the HR round post covers the non-technical side.
Want the full set?
- 360 questions, every one with a model answer
- Leadership Principles guide
- Loop format
- Story-mapping worksheet
- Prep plan
Amazon Interview FAQ Pack
How to map your stories to the Leadership Principles and prepare for the bar raiser.
Get this pack Free Frontend pack