Something about the Amazon SDE-2 loop surprises almost every engineer who goes through it for the first time: the behavioral questions feel like half the interview. They are, by design. Amazon's official SDE-II interview prep page says each of your four or five onsite rounds will evaluate Leadership Principles alongside the technical work, and at least one round, the Bar Raiser, is run by someone from a completely different team whose job is to raise the quality bar, not fill a headcount slot. Getting the coding right is necessary. It is not sufficient.
This page covers what the Amazon SDE-2 loop actually looks like in 2026, based on verified candidate reports from Glassdoor, LeetCode discuss threads, Medium write-ups, Onsites.fyi, and Amazon's own published interview prep guidance. Where I know a question is from a specific round, I've said so. Where something varies by team, I've said that too. The 16 Leadership Principles section is long on purpose. It needs to be.
Easy questions
9The details matter more than the topic. "I read about Rust" is not an answer. "I spent three weekends building a small service in Rust to understand the ownership model before we decided whether to migrate our low-level data serialization layer to it, and the outcome was that I concluded the borrow checker friction wasn't worth the memory safety gain for our use case, so I wrote a short doc explaining why we were keeping C++" is an answer.
The Bar Raiser version follows up: "What specifically surprised you? What was the hardest concept to get right?" If you can answer those questions with technical specificity, the story is credible. If you can't, the interviewer will assume you padded the answer.
Most candidates report four to eight weeks from application to verbal offer. The OA is typically sent within a week of the recruiter call and given a one-week completion window. Onsite loops are scheduled one to three weeks after the OA clears. Amazon's official guidance is a decision within five business days of the loop; in practice, candidates report one to two weeks, particularly when the hiring team needs to confirm headcount or when the Bar Raiser needs additional time to complete their debrief.
The Bar Raiser is a specially trained Amazon interviewer from a different team than the one you're applying to. They are not evaluating fit for the specific role; they are evaluating whether you would raise the overall quality of Amazon's engineering workforce. Their no-hire vote is a veto that cannot be overridden by the hiring manager without VP-level escalation, which almost never happens. The Bar Raiser's round can look like any other round, coding, design, or LP-focused, but with deeper follow-up probing. They are typically more thorough on LP stories than the other interviewers and more likely to surface inconsistencies between your claims and the details you provide.
Each interviewer is assigned two to three LPs to evaluate in their round. They'll typically ask two behavioral questions per round, each targeting one or two LPs. Over a five-round loop, you'll face roughly ten to fifteen LP-focused questions in total. The same LP can appear in multiple rounds if the interviewers have overlapping assignments, so prepare stories flexible enough to vary based on the follow-up direction rather than memorizing a single answer per LP.
Amazon accepts most mainstream languages: Java, Python, C++, JavaScript, Go. Java is the most common choice among candidates and what most interviewers are fluent in for code review. Python is also well-accepted. The OA requires syntactically correct code (the HackerRank environment compiles it), so you need to be fluent enough to write error-free code under time pressure, not just pseudocode. Some interviewers may ask you to avoid certain built-in library functions and implement core logic yourself, especially for data structure questions.
SDE-2 is Amazon's L5 level, the mid-level software engineer tier. It maps roughly to L4 at Google, E4 at Meta, and SDE-II at Microsoft. Levels.fyi data from 2025 shows median total compensation for Amazon SDE-2 at approximately $274,000 per year, with the bulk of that in RSUs. SDE-2 is the most active hiring level at Amazon and the bar where the LP evaluation is most consequential, because L5 engineers are expected to operate with significant ownership and cross-team scope, not just execute on assigned tasks.
System design is a standard part of the SDE-2 (L5) loop. Most loops include one LLD round and one HLD round, though some teams combine them into a single 90-minute session or include a hybrid design component in the hiring manager round. If your loop only has four rounds, one of them is almost certainly a design round. For context, the L4 (SDE-1) loop typically does not include a dedicated HLD round; that's one of the key differentiators between the two levels.
Situation and Task together should take no more than 20-30 seconds, just enough context for the interviewer to follow the stakes. Action is where most of your time goes, and it needs to be specific to what you personally did, not what your team did, including the technical or interpersonal choices you made and why you made them. Result needs a number or a concrete outcome, not "it went well." The mistake candidates make under Bar Raiser pressure is spending too long on Situation because it feels safer to talk about, then rushing Action, which is exactly the part the interviewer is scoring.
The eight-minute Work Style Survey presents short behavioral scenarios and asks you to rank a set of responses from most to least like how you'd actually act, closer in format to a personality assessment than an open-ended written question. It maps your rankings against the 16 Leadership Principles. Candidates report there's no obvious single correct answer key, the scoring appears to look for consistency with the LPs across scenarios rather than one ideal response. It doesn't gate you out on its own the way failing the coding section can, but recruiters have noted that a wildly inconsistent survey, answers that contradict each other on similar scenarios, can raise a flag during the debrief.
Medium questions
29Build a frequency map for each list, then iterate through both maps. A key present in map A but absent from map B is a removal. A key present in map B but absent from map A is an addition. A key present in both but at a different index position is a relocation. For large-scale batch processing follow-ups, this extends naturally to streaming input with delta updates rather than full recomputes.
This exact question was reported from a 2025 Amazon SDE-2 onsite round, Round 3 in the candidate's loop, with Customer Obsession and Ownership as the LPs evaluated. The interviewer emphasized the scalability angle: how does your solution behave when the product catalog is 50 million entries and the diff needs to run continuously? The expected answer introduces a streaming approach with event-based updates rather than full recomputation.
Standard coin change DP (bottom-up, dp[i] = minimum coins to make amount i) extended with an additional constraint on how many coins of each denomination can be used. The 1D DP table needs an extra dimension or a count-tracking pass to enforce per-denomination limits. Without the constraint, it's a classic unbounded knapsack. With it, the state becomes dp[amount][denomination_index], treated as a 0-1 knapsack per denomination.
Reported from a Bar Raiser round in 2025. The interviewer introduced the per-denomination count limit as a follow-up mid-problem, after the candidate had already implemented the basic version. Being able to extend a working DP without rewriting it from scratch is what the interviewer was testing, not whether you could recite the standard coin change solution.
def coin_change_limited(coins, counts, amount):
# coins[i] can be used at most counts[i] times
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for i, coin in enumerate(coins):
# iterate in reverse to enforce use-at-most-once per pass
for _ in range(counts[i]):
for a in range(amount, coin - 1, -1):
if dp[a - coin] != float('inf'):
dp[a] = min(dp[a], dp[a - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1Load all elements into a hash set. Iterate through the array and for each element, check if it is the start of a sequence (i.e., element - 1 is not in the set). If it is the start, count the sequence length by repeatedly checking element + 1, element + 2, and so on. Track the maximum length seen. O(n) time because each element is visited at most twice.
Reported from SDE-2 phone screens at Amazon in 2024-2025. The sorting approach is O(n log n) and technically correct but interviewers push for the O(n) set-based solution at mid-level. The follow-up asks what happens if the array contains duplicates: the hash set handles this automatically since set membership is duplicate-agnostic.
BFS-based serialization. Write each node's value followed by the count of its children, then its children's values in the same format. This lets the deserializer know exactly how many children to expect at each node without a delimiter. Alternatively, DFS with an explicit child-count marker works and is slightly simpler to implement recursively.
Reported from a 2025 SDE-2 onsite, Round 2, with a follow-up asking for O(1) time for dynamic node additions and removals after the serialization is built. The O(1) dynamic update requirement changes the approach: instead of rebuilding the serialized string, maintain a doubly linked list of nodes at each level so insertions and deletions update only the affected position in O(1) time.
The token bucket maintains a counter of available tokens, a max capacity, and a refill rate (tokens per second). On each request, check if at least one token is available. If yes, decrement and allow. If no, reject or queue. Refill tokens based on elapsed time since the last refill, capped at capacity. Thread safety requires either a mutex around the token check-and-decrement or an atomic compare-and-swap loop.
Amazon's API Gateway uses token bucket rate limiting internally, which makes this question particularly relevant for Amazon infrastructure teams. The interviewer typically asks you to compare token bucket vs. leaky bucket vs. fixed window vs. sliding window rate limiting, each has different burst behavior. Token bucket allows short bursts up to capacity; leaky bucket enforces a smooth outflow rate; fixed window can allow 2x the rate at window boundaries; sliding window is more accurate but computationally heavier.
import java.util.concurrent.atomic.AtomicLong;
public class TokenBucketRateLimiter {
private final long capacity;
private final double refillRatePerMs;
private final AtomicLong tokens;
private volatile long lastRefillTime;
public TokenBucketRateLimiter(long capacity, long refillRatePerSecond) {
this.capacity = capacity;
this.refillRatePerMs = refillRatePerSecond / 1000.0;
this.tokens = new AtomicLong(capacity);
this.lastRefillTime = System.currentTimeMillis();
}
public synchronized boolean tryAcquire() {
refill();
if (tokens.get() > 0) {
tokens.decrementAndGet();
return true;
}
return false;
}
private void refill() {
long now = System.currentTimeMillis();
long elapsed = now - lastRefillTime;
long newTokens = (long) (elapsed * refillRatePerMs);
if (newTokens > 0) {
tokens.set(Math.min(capacity, tokens.get() + newTokens));
lastRefillTime = now;
}
}
}Scan every cell. When you hit an unvisited land cell, run a DFS or BFS that flips every connected land cell to visited and expands in all four directions. Each DFS call accounts for exactly one island, so the island counter only increments at the point a new, unvisited land cell is discovered. Time complexity is O(rows × cols) since every cell is visited once.
This is one of the most frequently reported Amazon SDE-2 phone screen questions across 2024 and 2025 loop write-ups on Glassdoor and LeetCode Discuss. The follow-up interviewers ask most often: what if the grid isn't static but a live stream of cell updates? That changes the approach to a Union-Find structure with incremental union operations on each update, rather than a full grid rescan per change.
def num_islands(grid):
if not grid:
return 0
rows, cols = len(grid), len(grid[0])
visited = [[False] * cols for _ in range(rows)]
def dfs(r, c):
if r < 0 or r >= rows or c < 0 or c >= cols:
return
if visited[r][c] or grid[r][c] == "0":
return
visited[r][c] = True
dfs(r + 1, c)
dfs(r - 1, c)
dfs(r, c + 1)
dfs(r, c - 1)
count = 0
for r in range(rows):
for c in range(cols):
if grid[r][c] == "1" and not visited[r][c]:
dfs(r, c)
count += 1
return countBuild an adjacency list from the prerequisite pairs and track the in-degree of every course. Run Kahn's algorithm: push every course with in-degree zero onto a queue, pop a course, decrement the in-degree of its dependents, and push any dependent whose in-degree drops to zero. If every course gets processed, there's no cycle and the courses can all be finished. If some courses never enter the queue, a cycle exists among them.
Amazon interviewers frame this as a build-dependency problem more often than the textbook version, something like "package A depends on package B, detect a circular build dependency before it breaks the release pipeline." That framing matches Amazon's actual internal build tooling. The standard follow-up asks you to also return a valid build order, not just a true/false answer, which the same queue naturally produces if you record the pop order.
from collections import deque
def can_finish(num_courses, prerequisites):
graph = [[] for _ in range(num_courses)]
in_degree = [0] * num_courses
for course, prereq in prerequisites:
graph[prereq].append(course)
in_degree[course] += 1
queue = deque(c for c in range(num_courses) if in_degree[c] == 0)
visited = 0
while queue:
node = queue.popleft()
visited += 1
for neighbor in graph[node]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
return visited == num_coursesRecursive DFS from the root. If the current node is one of the two targets, return it immediately. Otherwise recurse into the left and right subtrees. If both recursive calls return a non-null node, the current node is the lowest common ancestor, since one target was found on each side. If only one side returns non-null, propagate that result up, since the ancestor must be further up that branch.
Reported repeatedly in Amazon SDE-2 phone screens, usually as the second, easier problem after a harder opener. The two follow-ups worth preparing: what changes if each node has a parent pointer instead of the tree being passed in (it becomes a linked-list intersection problem, not a tree traversal one), and what happens if one of the two target nodes might not exist in the tree at all (you need a separate existence check before trusting the LCA result).
Combine a hash map for O(1) lookup with a doubly linked list for O(1) reordering. On every get, move the accessed node to the front of the list, since it's now the most recently used. On every put, insert a new node at the front; if the cache exceeds capacity, evict the node at the tail, since that's the least recently used, and remove its key from the hash map.
This is one of Amazon's most consistently reported SDE-2 coding questions across multiple years of loop write-ups, more so than at most other Big Tech companies. In 2025 loops it's shown up both as a standalone coding round and as the opening 15 minutes of a Bar Raiser's hybrid round before pivoting into Leadership Principles questions. The follow-up almost always asks how you'd make the cache thread-safe under concurrent get and put calls from multiple threads.
class Node:
def __init__(self, key, value):
self.key = key
self.value = value
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.cache = {}
self.head = Node(0, 0)
self.tail = Node(0, 0)
self.head.next = self.tail
self.tail.prev = self.head
def _remove(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def _add_to_front(self, node):
node.next = self.head.next
node.prev = self.head
self.head.next.prev = node
self.head.next = node
def get(self, key):
if key not in self.cache:
return -1
node = self.cache[key]
self._remove(node)
self._add_to_front(node)
return node.value
def put(self, key, value):
if key in self.cache:
self._remove(self.cache[key])
node = Node(key, value)
self.cache[key] = node
self._add_to_front(node)
if len(self.cache) > self.capacity:
lru = self.tail.prev
self._remove(lru)
del self.cache[lru.key]Sort meetings by start time. Keep a min-heap of end times representing rooms currently in use. For each meeting, check the smallest end time on the heap: if it's less than or equal to the current meeting's start time, that room has freed up, so pop it and push the new end time in its place. If not, no room is free, so push the new end time without popping, which allocates a new room. The final heap size is the answer.
Reported from Amazon SDE-2 onsite loops tied to internal scheduling systems, conference-room booking and warehouse shift scheduling are the two framings candidates mention most. The follow-up worth preparing: what if meetings can be canceled after being scheduled? That breaks the simple min-heap approach and pushes toward an interval tree or a heap with lazy deletion instead.
import heapq
def min_meeting_rooms(intervals):
if not intervals:
return 0
intervals.sort(key=lambda x: x[0])
end_times = [intervals[0][1]]
for start, end in intervals[1:]:
if start >= end_times[0]:
heapq.heapreplace(end_times, end)
else:
heapq.heappush(end_times, end)
return len(end_times)The O(n^2) version defines dp[i] as the length of the longest increasing subsequence ending at index i, computed as 1 plus the max of dp[j] for every j less than i where nums[j] is less than nums[i]. The O(n log n) version keeps a "tails" array, where tails[k] holds the smallest possible tail value of an increasing subsequence of length k+1, and uses binary search to find where each new number belongs.
Reported from SDE-2 phone screens where interviewers ask for the O(n^2) DP solution first and then push for the O(n log n) optimization as a mid-interview follow-up, the same escalation pattern seen on the coin-change question earlier on this page. Being able to walk from the straightforward DP to the binary-search optimization without starting over is what separates a pass from a borderline result at this level.
import bisect
def length_of_lis(nums):
tails = []
for num in nums:
pos = bisect.bisect_left(tails, num)
if pos == len(tails):
tails.append(num)
else:
tails[pos] = num
return len(tails)Amazon's first and most prominent LP. The question is testing whether your customer-focus holds when it's inconvenient. A strong answer identifies a specific customer impact (degraded latency, confusing UX, data quality issue), the internal pushback you faced (deadline pressure, technical debt concerns, stakeholder resistance), what you did anyway, and the measured result. Vague answers ("I always keep the customer in mind") fail immediately.
At SDE-2 level, the expected story involves a system-level decision, not a feature request. Something like: you delayed a release because monitoring showed an edge case that would affect 0.1% of customers with a specific usage pattern, the PM pushed to ship anyway, you held the release, and the post-hoc data confirmed the edge case would have triggered. That's the granularity Amazon interviewers want.
"Working backwards" is an Amazon-specific phrase that refers to their product development methodology: write the press release and FAQ before writing any code. Even if you didn't use that format exactly, frame your answer around how customer feedback or behavior data drove a technical decision. Specific metrics help: "our error rate for this cohort was 2.3%, which translated to roughly 40,000 customers per week seeing a failure" is better than "customers were unhappy."
The follow-up is almost always: "How did you validate that your solution actually addressed the customer problem, not just the metric you were tracking?" Have a measurement story ready for how you confirmed the fix worked.
The LP that gets candidates rejected most often when their answer is too small. "I helped a teammate debug an issue" is not the answer Amazon is looking for at SDE-2. The expected story involves taking ownership of an operational problem, a technical debt item, or a cross-team dependency that nobody was explicitly responsible for, seeing it through to resolution, and having a measurable outcome. On-call improvements, reliability work that wasn't in anyone's sprint, tooling built for the whole team, these are the right categories.
The follow-up probes whether you saw the thing through: "Did anything go wrong during the remediation? How did you handle it?" A story where everything went smoothly is less credible than one that includes a complication and how you resolved it.
At SDE-2 level, "owned" means you designed it, wrote the design document, got buy-in from stakeholders, implemented it or led the implementation, and monitored it post-launch. Stories where ownership is ambiguous, "I led part of it" or "I was the main contributor," get drilled. The interviewer will ask specific questions until they understand exactly what you were responsible for vs. what your team owned collectively.
Prepare the technical details of the design, not just the narrative. "I used a message queue to decouple the ingestion from the processing layer" is better than "I built a scalable architecture." At some point in your answer, the interviewer will stop the story and ask a technical question about a choice you made.
Simplification is harder to demonstrate than complexity. The best answers involve removing something (a service, a configuration option, an abstraction layer, a deployment step) that others believed was necessary, showing the data that justified removing it, and measuring the improvement. Adding more components or features is not simplification, even if the outcome is a better system.
The follow-up is often: "Was there any resistance to removing the component? How did you convince the team?" This brings in Earn Trust and Have Backbone simultaneously. Prepare for those follow-ups.
Adoption breadth signals that the solution was genuinely better, not just a local optimization. If you built a tool for your team and three other teams started using it, that's the pattern Amazon wants to see. Be specific about how the broader adoption happened (did you demo it? write documentation? other teams discovered it through a shared repo?) and what the impact was at the wider scale.
At SDE-2 level, the expected scope is intra-org adoption, not company-wide. A solution that went from your team to two adjacent teams with measurable impact on both is a strong answer. Company-wide adoption stories are more appropriate for senior and principal levels.
Structure: what you observed first (alert, customer report, metric spike), how you formed and tested hypotheses (not "I started looking at logs" but "I checked the error rate by service, isolated it to the payment-service dependency, then pulled CloudWatch logs for that service and found a 40% increase in timeout errors starting at 14:32 UTC"), what the root cause was, how you confirmed it, what the fix was, and what you changed to prevent recurrence.
This is the canonical Dive Deep question. The Bar Raiser version goes much deeper: they'll ask about specific log lines, what the second hypothesis was when the first didn't pan out, how you communicated status during the incident, and what the postmortem recommended. Prepare one production incident story in this level of detail. Most candidates prepare the high-level arc but not the specific technical detail, and the Bar Raiser will find the gap in the first follow-up.
Dive Deep and Are Right A Lot overlap heavily here. The story needs three components: what the conventional belief was and why it existed, what you investigated that changed your assessment, and what the outcome was. If the conventional wisdom turned out to be right and you changed your mind, that's also a valid answer, it shows intellectual honesty and the ability to update on new data, which Amazon values as part of Are Right A Lot.
The failure mode on this question is being vague about the investigation. "I did some analysis and found the approach was wrong" is not a Dive Deep answer. The analysis has to be specific: I profiled the query, I reviewed the access pattern logs, I benchmarked three approaches, I found that the existing solution was O(n^2) on inputs over 10,000 rows where we were getting 500,000 rows per batch. That level of specificity.
This LP is specifically about what happens after you lose the argument. Amazon doesn't want a story where you quietly complied the whole time (that isn't disagreeing) or one where you kept re-litigating the decision after it was made (that isn't committing). The strong version shows you stating your position once, clearly, with evidence, and then, once the decision went the other way, executing it as if it had been your own idea, including defending it to teammates who raised the same objection you originally had.
Reported as a standalone follow-up in Bar Raiser rounds even when a candidate's opening story already touched on disagreement, because interviewers want a second, cleaner example that isn't tangled up with the Earn Trust angle of an earlier answer. Keep a separate story ready for this one.
Amazon wants to see that you can scope a reversible decision and move quickly, not that you ignore risk. The answer structure should clarify what made the information incomplete (product requirements were still being finalized, third-party API behavior was uncertain, load testing data wasn't available yet), what you identified as reversible vs. irreversible, and how you moved forward with the reversible parts while deferring the irreversible ones.
At SDE-2, the expected story involves a system-level ambiguity, not just a missing feature spec. Ambiguity about data consistency semantics, SLA targets, or integration contract details are common scenarios.
Amazon's own framing for Bias for Action leans on the "two-way door" idea: most decisions are reversible and should be made quickly with roughly 70% of the information you wish you had, not 90%. This question tests whether you can tell a two-way door from a one-way one, and whether you corrected course quickly once the signal showed up, rather than defending the original call.
A credible answer includes an actual negative outcome, not a near-miss you caught before anyone noticed. Something like: you shipped a config change without a full load test because the risk looked low and reversible, it caused a brief latency regression for a slice of traffic, you rolled it back within the hour, and afterward you added a lighter-weight canary step for that class of change. That last part, what changed in your process going forward, is what separates this from a story that just admits a mistake and stops there.
Candidates who say "I always welcome feedback" without a real example get pushed immediately. Amazon wants a specific story where the feedback stung, where your first instinct was probably defensive, and where you processed it, sought to understand it, and changed your behavior as a result. The behavior change is the key part. Interviewers follow up: "Did you act on it? What specifically did you change? How do you know it worked?"
If you can also name what the feedback giver observed (specific behavior, specific code, specific incident) rather than describing vague criticism, the story reads as far more credible. "My tech lead told me my design docs were not getting enough cross-team buy-in before implementation started" is better than "I got feedback that I needed to communicate better."
This question probes two LPs at once. Earn Trust asks whether you handled the disagreement in a way that preserved the relationship and used data to support your position. Have Backbone asks whether you actually pushed back or just deferred. The strongest answers show you raised the concern with specific evidence, had a direct conversation, and either convinced the stakeholder with data or genuinely heard their reasoning and updated your position, not just backed down because of seniority.
The outcome should not always go your way. An answer where you pushed back, provided evidence, the decision went against you, and you then committed fully to the decision and made it succeed is a strong Have Backbone story. Amazon interviewers specifically watch for candidates who say "Disagree and Commit" and whose story doesn't actually include disagreeing, only committing.
Concrete examples: introduced integration tests for a system that had none, raised code review standards by establishing a review checklist, identified a class of latency regressions by adding P99 alerting that didn't previously exist. The key is that the standard was raised in a way that outlasted your individual involvement. If you left the team tomorrow, would the standard persist? If yes, the story is strong. If it only existed because you personally reviewed every PR, it's weaker.
At SDE-2, interviewers also want to see that you brought the team along rather than creating friction. A higher standard imposed unilaterally without buy-in creates resentment. The best stories include how you socialized the change, got teammates to agree it was worth the investment, and observed the improvement in outcome metrics.
Think Big is about not settling for a fix that clears today's ticket but creates drag next quarter. A strong answer names the narrow ask, explains what made you widen the scope (a pattern you noticed across multiple tickets, a platform gap you knew others would hit soon), and shows the extra cost of building bigger instead of narrower was a deliberate tradeoff, not just enthusiasm for a more interesting problem.
The failure mode at SDE-2 is a story that reads as scope creep rather than Think Big. Interviewers probe: did your manager push back on the bigger scope, and how did you get buy-in before you built it? Have that negotiation ready, not just the technical outcome.
Frugality at Amazon isn't about being cheap for its own sake, it's resourcefulness under a real constraint. Good answers name the constraint honestly (headcount got cut mid-project, a budget request for a third-party service was rejected, there wasn't time to build the "proper" version), the tradeoff made instead (reusing an existing internal tool instead of standing up new infrastructure, running a smaller pilot before requesting a bigger allocation), and the result.
Interviewers watch for candidates who confuse Frugality with under-resourcing a project that genuinely needed more. If the constrained approach caused a real quality problem down the line, say so, and explain what you'd request differently next time. That honesty reads better than a story with no downside at all.
Amazon evaluates this LP even for individual contributors, not just people managers, because SDE-2s are expected to raise the level of the people around them. The strongest stories name a specific gap (a junior engineer who kept shipping code with no tests, someone struggling with an unfamiliar part of the codebase), describe what you actually did (pairing sessions, a written onboarding guide, structured code review feedback over several weeks), and end with a measurable change in that person's output or confidence.
A story where you quietly did their work for them to hit a deadline is a red flag here, not a positive signal. The interviewer wants evidence that you built the other person's capability, not that you rescued a deliverable. Expect a follow-up on what you'd do differently if a similar situation came up again.
The obstacle has to be genuine, not a minor setback. A third-party API going down, a key engineer leaving mid-project, a scope change from senior leadership, a production incident consuming half your team's bandwidth for a week. The story should show how you assessed the impact on the project, what you cut or rescheduled, how you communicated the situation to stakeholders, and how you ultimately delivered. "Delivered on schedule" can also mean "renegotiated the schedule with stakeholder agreement based on the new constraint," which is actually a mature answer.
The follow-up probes whether the delivery was a net win: "Did cutting that scope cause any downstream problems?" Prepare a real answer.
Hard questions
4Two-pointer sliding window with a character frequency map. Build a need map for pattern characters. Expand the right pointer until all characters are satisfied, then contract the left pointer to minimize the window. Track the minimum window seen so far. Time complexity O(n + m) where n is the string length and m is the pattern length.
Reported in multiple SDE-2 phone screens from 2024 and 2025. The follow-up almost always asks about the case where the pattern has repeated characters, which requires a count-based check (how many of each character are satisfied) rather than a simple set membership check. Candidates who implement the set-based version get caught on this immediately.
public String minWindow(String s, String t) {
Map<Character, Integer> need = new HashMap<>();
for (char c : t.toCharArray()) need.merge(c, 1, Integer::sum);
int left = 0, right = 0, formed = 0, required = need.size();
int[] ans = {-1, 0, 0}; // length, left, right
Map<Character, Integer> window = new HashMap<>();
while (right < s.length()) {
char c = s.charAt(right++);
window.merge(c, 1, Integer::sum);
if (need.containsKey(c) && window.get(c).equals(need.get(c))) formed++;
while (left < right && formed == required) {
if (ans[0] == -1 || right - left < ans[0]) {
ans[0] = right - left;
ans[1] = left;
ans[2] = right;
}
char leftChar = s.charAt(left++);
window.merge(leftChar, -1, Integer::sum);
if (need.containsKey(leftChar) && window.get(leftChar) < need.get(leftChar)) formed--;
}
}
return ans[0] == -1 ? "" : s.substring(ans[1], ans[2]);
}Two-pointer approach. Maintain left and right pointers and left_max, right_max variables. At each step, the pointer with the smaller boundary value is the limiting factor. If height[left] is less than height[right], compute trapped water as left_max minus height[left] and advance left. Otherwise process the right side. This runs in O(n) time with O(1) space, versus O(n) space for the prefix/suffix max array approach.
Reported from a Bar Raiser round in late 2024. The candidate confirmed the Bar Raiser specifically asked for the O(1) space solution after the candidate produced the array-based approach first. At SDE-2 level, being able to optimize from a working O(n) space to O(1) space with a two-pointer approach is the expected progression.
Push the head of each of the k lists onto a min-heap keyed on node value. Repeatedly pop the smallest node, append it to the result, and push that node's next pointer back onto the heap if it exists. This runs in O(n log k) time, where n is the total number of nodes across all lists, since every pop and push touches a heap of at most k elements.
Candidate reports from 2024 and 2025 Bar Raiser rounds cite this as an escalation from an earlier, easier merge-two-lists warm-up. The standard follow-up compares the heap approach against pairwise divide-and-conquer merging, which is also O(n log k) but uses O(1) extra space instead of O(k) for the heap, a tradeoff worth naming even if you implement the heap version first.
Start with requirements: a cart has to survive across devices and sessions, so persist it in a fast key-value store like DynamoDB with the user ID as the partition key, rather than trusting client-side or session-only state. Since reads and writes are both heavy (every product page view can touch the cart to check for existing quantities), a write-through cache such as ElastiCache sits in front of DynamoDB for active sessions. Concurrent updates from multiple tabs or devices need optimistic locking, a version field on the cart record that gets checked and incremented on every write, so a stale write fails instead of silently overwriting a newer one. Checkout snapshots the cart into an immutable order record, keeping the cart itself lightweight and mutable.
Reported from HLD rounds in 2025 loops for retail-org teams. The interviewer follow-up worth preparing: what happens when an item's price changes while it's already sitting in a customer's cart? The correct answer re-validates price against the live catalog service at checkout time rather than trusting whatever price was cached on the cart line item when it was added.
Real-time scenario questions
10Fixed-size array with read and write index pointers, both modulo the buffer size. A mutex protects both indices and the data array. A condition variable (or semaphore pair) lets the writer block when the buffer is full and the reader block when the buffer is empty. Key edge cases: distinguishing full from empty (when read index equals write index, it's ambiguous whether zero or n items are stored), typically resolved with a separate count variable or by leaving one slot unused.
Reported from a 2024 SDE-2 coding round. The follow-up asked about performance under high-contention scenarios and whether there's a lock-free implementation. The lock-free version uses atomic compare-and-swap on the index pointers and is significantly more complex to reason about correctly.
Maintain a min-heap capped at size k. Every time a value is added, push it onto the heap; if the heap size exceeds k, pop the smallest element. The root of the heap is always the kth largest value seen so far, because everything smaller than it has already been evicted.
Reported both as a standalone OA problem and as the streaming follow-up to the plain "kth largest in an array" LeetCode question during phone screens. Amazon prefers the stateful, streaming version specifically because it tests whether a candidate understands amortized O(log k) insertion, rather than someone who just memorized the static array solution and recomputes from scratch on every call.
import heapq
class KthLargest:
def __init__(self, k, nums):
self.k = k
self.heap = nums[:]
heapq.heapify(self.heap)
while len(self.heap) > k:
heapq.heappop(self.heap)
def add(self, val):
heapq.heappush(self.heap, val)
if len(self.heap) > self.k:
heapq.heappop(self.heap)
return self.heap[0]Core entities: ParkingLot, Level, ParkingSpot (subclasses: CompactSpot, LargeSpot, HandicappedSpot), Vehicle (subclasses: Car, Motorcycle, Truck), Ticket, ParkingRate. The ParkingLot has an availability tracker (array of counts per spot type per level). Assignment uses a greedy nearest-available approach or, for large lots, a priority queue sorted by distance from the entrance. The Ticket records entry time, spot assignment, and vehicle info. Checkout computes the fee from the rate table and releases the spot atomically.
Reported from Round 3 of a 2025 SDE-2 loop, LP focus was Insist on Highest Standards. The interviewer asked about thread safety for the spot assignment operation specifically. Multiple threads assigning the same spot to two vehicles is the concurrency bug. The fix is either a synchronized method on the assignment function or an optimistic lock with a retry loop on a CAS of the spot's availability state.
Core interfaces: RateLimiter (tryAcquire(clientId) boolean), RateLimitConfig (max requests, window size, algorithm type), RateLimitStore (reads and writes token counts, typically backed by Redis for distributed scenarios). The client identifier can be an API key, IP address, or user ID. For distributed rate limiting where multiple instances share state, the token bucket state must live in Redis with Lua scripts or WATCH/MULTI/EXEC transactions to make the check-and-decrement atomic across network calls.
The LLD design question asks for the class hierarchy and interface contracts, not the implementation detail. A clean separation between the algorithm (token bucket, sliding window) and the storage backend is the key design insight interviewers look for at SDE-2 level.
Base62-encode an auto-incrementing counter to produce short codes, sharding the counter across a range-based ID generator so multiple servers aren't fighting over one shared sequence. Store the short-code-to-long-URL mapping in a key-value store like DynamoDB, with the short code as the partition key. Since reads (redirects) vastly outnumber writes (new short links created), a Redis cache in front of the datastore absorbs most of the redirect traffic. The 301-vs-302 redirect choice matters more than it looks: a 302 lets you track every single click server-side, while a 301 gets cached by the browser and skips your server entirely on repeat visits, which is faster but kills your click analytics.
Reported as a warm-up HLD question in multiple 2024 and 2025 SDE-2 loops, often used to check baseline system design fluency before the interviewer escalates to a harder problem. The recurring follow-up: how do you prevent short-code collisions if two service instances are generating IDs at the same time? The answer is a centralized or range-partitioned ID generator, not client-side random generation with a database uniqueness check after the fact.
Model the machine as a state machine with states like Idle, HasMoney, Dispensing, and OutOfStock. Core classes: VendingMachine, Inventory, Coin, Product, and a State interface with one concrete class per state, so insertCoin, selectProduct, dispense, and refund each only implement the transitions that are valid from that particular state. This avoids the common anti-pattern of one giant class with nested if/else blocks checking "what state are we in" before every action.
Reported from Round 2 LLD interviews across multiple 2025 SDE-2 loops, alongside parking lot and elevator system as the three most commonly reported OOP design prompts at Amazon. The follow-up asks how you'd extend the design to support a card-payment path without touching any of the coin-handling classes, which tests whether your state and payment interfaces are actually decoupled or just look that way on the whiteboard.
Start by clarifying requirements: order volume per second (say, 100,000 orders/sec at peak), latency targets (order acknowledgment in under 50ms), consistency guarantees (orders must not be lost, partial fills must be atomic). For the order book, a relational database like Aurora handles ACID transactions for trade matching. A Redis cache holds the current order book state for read-heavy queries (traders polling the book). Incoming orders land on an SQS FIFO queue to preserve order sequence before the matching engine processes them. The matching engine runs as a stateful service, not stateless, because the order book is in-memory for speed.
This exact problem was reported from Round 1 of a 2025 Amazon SDE-2 onsite. The interviewer probed on the matching engine design specifically: what happens when the matching engine crashes? The answer requires a persistent write-ahead log or event sourcing so the engine can replay its state from the SQS queue on restart, rather than losing all in-flight orders.
This system has a few interesting constraints. Code execution is untrusted, so each submission runs in an isolated sandbox (Docker containers with CPU and memory limits, seccomp syscall filtering). Execution is async: the user submits code, receives a job ID, and polls for results (or gets a WebSocket push when the result is ready). Storage: test case inputs and expected outputs go in S3 (large, immutable). Submission code and results go in a relational DB. A job queue (SQS or Kafka) decouples submission ingestion from execution workers. Auto-scaling the execution fleet during contest spikes is the key operational design decision.
Reported from a Hiring Manager round in a 2025 SDE-2 loop. The HM pushed on the security model for code execution and on how you'd handle a submission that goes into an infinite loop (answer: a timeout watchdog in the container runtime, not relying on the submitted code to self-terminate).
Producers (application services) emit logs to a Kafka cluster partitioned by service name. Log consumers read from Kafka and write to an Elasticsearch cluster for full-text search. A separate cold-storage writer archives raw logs to S3 Parquet files for cost-effective long-term storage. Search queries hit Elasticsearch for recent logs (last 7 to 30 days) and a Presto or Athena query layer for historical queries against S3. Ingestion rate is the main scale lever: 50GB/day is manageable with 3 Kafka brokers; 5TB/day requires partition tuning and potentially a tiered storage strategy within Kafka itself.
This is functionally the "log aggregator system" reported from Round 4 of the 2025 SDE-2 loop. The candidate proposed an optimization to the search path; the interviewer challenged it; they negotiated a compromise. Amazon's "Have Backbone, Disagree and Commit" LP is evaluated here. The interviewer wants to see that you can hold a position with evidence and also hear a counterargument, not just capitulate.
Fan-out on write vs. fan-out on read is the core trade-off. For push notifications (mobile), fan-out on write using a message queue and a fleet of push workers per platform (FCM for Android, APNs for iOS) is standard. For in-app notifications, a per-user notification inbox in a NoSQL store (DynamoDB with userId as partition key) scales predictably. Rate limiting per user (no more than N notifications per hour) prevents notification spam and protects the push infrastructure from hot users. Dead-letter queues catch failed deliveries for retry without blocking the main ingestion path.
AWS-idiomatic implementation: API Gateway receives notification triggers, SNS fans out to per-channel SQS queues, Lambda workers process each queue and call FCM/APNs. At 100 million users with 1% daily active rate, the send volume is manageable; the read latency for loading a user's notification inbox is the real design constraint.
Candidates who prepare through LastRoundAI's mock interview sessions show a consistent failure mode that doesn't appear in most prep guides. The failure isn't on LP knowledge. It's on STAR story depth under follow-up pressure.
The opening story usually lands fine. Then the interviewer asks: "What specifically did you do, not your team, you?" And candidates who prepared the team's story rather than their individual contribution start hedging. "We" becomes ambiguous and the interviewer notes it. At SDE-2 level, Amazon is evaluating whether you can own a problem at the scope of an L5. Stories where your individual contribution is unclear read as L4 scope.
The second pattern we've seen: candidates who treat the behavioral rounds as a break from the technical rounds get caught by the Bar Raiser. The Bar Raiser may pivot from LP questions to a coding problem or a design question mid-round with no warning. A candidate who mentally "shut down" their technical preparation for the behavioral session is suddenly at a disadvantage. Keep both modes active throughout the loop.

