Amazon Interview Experience — Full Stack JavaScript Developer (All Rounds)

Amazon Interview Experience — Full Stack JavaScript Developer (All Rounds)

Hey everyone! This blog is a complete round-by-round breakdown of the Amazon SDE interview for a Full Stack JavaScript Developer — with real questions asked and detailed answers!

Amazon's interview is not just about coding. It tests your JavaScript depth, system design thinking, DSA skills, and Leadership Principles — all together. This is your one-stop guide.

What we will cover:

  • Round 0 — Online Assessment (OA)
  • Round 1 — Technical Phone Screen
  • Round 2 — DSA Coding Round
  • Round 3 — JavaScript and Node.js Deep Dive
  • Round 4 — React and Frontend System Design
  • Round 5 — Bar Raiser Round (Leadership Principles)
  • Quick Recap + Key Tips

Amazon Interview Process — Overview

Application → OA → Phone Screen → Loop (4–5 Onsite Rounds) → Offer

┌──────────────────────────────────────────────────────────────┐
│                   AMAZON LOOP STRUCTURE                      │
│                                                              │
│  Round 0: Online Assessment (OA)    → Filter round          │
│  Round 1: Technical Phone Screen    → JS basics + 1 LP       │
│  Round 2: DSA Coding Round          → Medium–Hard LeetCode   │
│  Round 3: JS / Node.js Deep Dive    → Core concepts + code   │
│  Round 4: React + System Design     → Frontend architecture  │
│  Round 5: Bar Raiser                → Leadership Principles  │
│                                                              │
│  Each onsite round = 55–60 minutes                           │
│  Every round has BEHAVIORAL questions (Leadership Principles) │
│  Strong code + Weak LP = NO OFFER at Amazon!                 │
└──────────────────────────────────────────────────────────────┘

Amazon's 16 Leadership Principles (LP) are tested in EVERY round. The most common ones for SDE roles:

  • Customer Obsession
  • Ownership
  • Invent and Simplify
  • Are Right, A Lot
  • Learn and Be Curious
  • Dive Deep
  • Deliver Results
  • Bias for Action

Round 0 — Online Assessment (OA)

Duration: 2 hours | Platform: HackerRank / Amazon's internal portal

The OA is the first filter. It has 3 parts:

Part 1: 10 MCQs on HTML, JavaScript, and CSS (20 min)
Part 2: 1 Machine Coding Question — Build a UI component in Vanilla JS (90 min)
Part 3: Work Style Survey (Amazon Leadership Principles) (15 min)

MCQ Questions Asked (Part 1)

Q1: What is the output of the following code?

setTimeout(() => console.log("A"), 0);
Promise.resolve().then(() => console.log("B"));
console.log("C");

A) A B C
B) C A B
C) C B A ✅
D) B C A

ANSWER: C B A
WHY: console.log("C") → synchronous, runs first.
     Promise.then → microtask, runs before macrotask.
     setTimeout → macrotask, runs last.
Q2: What does the following output?

let x = 1;
function test() {
    console.log(x);
    let x = 2;
}
test();

A) 1
B) 2
C) undefined
D) ReferenceError ✅

ANSWER: ReferenceError
WHY: 'let' is in the Temporal Dead Zone (TDZ) until its declaration.
     'x' is hoisted but not initialized — accessing it before
     the 'let x = 2' line throws ReferenceError.
Q3: What is event delegation?

A) Assigning events to each element separately
B) Using capture phase for events
C) Attaching one event listener to a parent to handle events
   from its children ✅
D) Preventing event bubbling

ANSWER: C
WHY: Instead of 100 event listeners for 100 list items,
     you put ONE listener on the parent. Events bubble up
     from child to parent, so the parent handles all of them.
Q4: Which array method does NOT mutate the original array?

A) splice
B) push
C) map ✅
D) pop

ANSWER: C — map returns a NEW array, original is unchanged.
splice, push, pop ALL mutate the original array.
Q5: What is the difference between == and ===?

== does TYPE COERCION (converts types before comparing)
=== is STRICT EQUALITY (no type conversion)

"5" == 5    → true   (string "5" coerced to number)
"5" === 5   → false  (different types, no coercion)
null == undefined  → true
null === undefined → false

Machine Coding Question (Part 2)

Question: Build an Image Carousel in Vanilla JavaScript

Requirements:
- Display images one at a time
- Previous and Next buttons
- Auto-play with 3 second interval
- Dot indicators showing current slide
- Pause auto-play on hover
- No libraries — plain HTML, CSS, JS only

// HTML Structure
<div class="carousel">
  <button id="prev">‹</button>
  <img id="slide" src="" alt="slide" />
  <button id="next">›</button>
  <div id="dots"></div>
</div>
// JavaScript Solution

const images = [
    "img1.jpg", "img2.jpg", "img3.jpg", "img4.jpg"
];

let current = 0;
let timer;

const slide = document.getElementById("slide");
const dotsContainer = document.getElementById("dots");

// Build dots
images.forEach((_, i) => {
    const dot = document.createElement("span");
    dot.className = "dot";
    dot.onclick = () => goTo(i);
    dotsContainer.appendChild(dot);
});

function goTo(index) {
    current = (index + images.length) % images.length;
    slide.src = images[current];
    document.querySelectorAll(".dot").forEach((d, i) => {
        d.classList.toggle("active", i === current);
    });
}

function startAutoPlay() {
    timer = setInterval(() => goTo(current + 1), 3000);
}

function stopAutoPlay() {
    clearInterval(timer);
}

document.getElementById("next").onclick = () => goTo(current + 1);
document.getElementById("prev").onclick = () => goTo(current - 1);

document.querySelector(".carousel").addEventListener("mouseenter", stopAutoPlay);
document.querySelector(".carousel").addEventListener("mouseleave", startAutoPlay);

goTo(0);      // Initialize
startAutoPlay();

KEY POINTS the interviewer checks:
- Modulo wrap-around logic: (index + length) % length
- Proper dot highlighting
- clearInterval on hover → prevents multiple intervals stacking
- Separation of concerns (goTo handles all state)

Round 1 — Technical Phone Screen

Duration: 45–60 minutes | First 20 min: Behavioral | Next 35 min: Technical

Behavioral Questions Asked

Q: Tell me about a time you had a disagreement with a teammate.
   How did you handle it?

STAR ANSWER:
──────────────
S — Situation:
    On a React + Node.js project, a teammate wanted to store
    user session data in localStorage for simplicity. I was
    concerned about XSS vulnerability.

T — Task:
    I needed to convince the team to use httpOnly cookies instead
    without creating conflict or slowing down the sprint.

A — Action:
    I wrote a quick 1-page document comparing localStorage vs
    httpOnly cookies on security. I showed a simple XSS demo
    where malicious script could steal localStorage tokens.
    I suggested we switch without delaying the deadline.

R — Result:
    Team agreed. We shipped with httpOnly cookies + CSRF tokens.
    2 weeks later, a security audit praised this decision.
    The feature launched on time with zero security issues.

LEADERSHIP PRINCIPLE: Are Right, A Lot + Ownership

Technical Questions Asked

Q1: What is the difference between null and undefined?

ANSWER:
undefined → variable declared but NOT assigned any value
null      → variable explicitly assigned "no value" intentionally

let a;           // undefined — JS assigned it automatically
let b = null;    // null — developer intentionally set "empty"

typeof undefined  → "undefined"
typeof null       → "object" (historical bug in JS!)

null == undefined   → true  (loose equality)
null === undefined  → false (strict equality)
Q2: What is the difference between var, let, and const?

Feature         var         let         const
────────────────────────────────────────────────────
Scope           Function    Block       Block
Hoisting        Yes (undef) Yes (TDZ)   Yes (TDZ)
Re-declare      Yes         No          No
Re-assign       Yes         Yes         No
Global object   Yes (window) No         No

var x = 1;
if (true) {
    var x = 2;  // Same variable! Overwrites!
    let y = 3;  // Block-scoped, only exists here
}
console.log(x); // 2 (var leaked out!)
console.log(y); // ReferenceError (let stays in block)

CONST gotcha:
const obj = { name: "Shreyesh" };
obj.name = "Amazon";  // ✅ WORKS — object is mutable
obj = {};             // ❌ TypeError — can't reassign binding
Q3: Implement a function that flattens a nested array.

Input:  [1, [2, [3, [4]], 5]]
Output: [1, 2, 3, 4, 5]

// Solution 1: Recursion
function flatten(arr) {
    const result = [];
    for (const item of arr) {
        if (Array.isArray(item)) {
            result.push(...flatten(item));  // recurse!
        } else {
            result.push(item);
        }
    }
    return result;
}

// Solution 2: Array.flat() (ES2019 — mention this too!)
[1, [2, [3, [4]], 5]].flat(Infinity);

// Solution 3: reduce + recursion (shows mastery)
function flatten(arr) {
    return arr.reduce((acc, item) =>
        acc.concat(Array.isArray(item) ? flatten(item) : item), []);
}

console.log(flatten([1, [2, [3, [4]], 5]]));
OUTPUT: [1, 2, 3, 4, 5]

Interviewer expects you to handle infinite nesting
and mention .flat(Infinity) as a built-in alternative!

Round 2 — DSA Coding Round

Duration: 55–60 minutes | 1–2 DSA problems | Medium to Hard difficulty

Amazon focuses heavily on Arrays, Strings, Trees, Graphs, and DP. Expect LeetCode Medium as baseline.

Question 1 Asked: Rotting Oranges (BFS)

PROBLEM:
You have a grid. Each cell is:
  0 = empty, 1 = fresh orange, 2 = rotten orange

Every minute, a rotten orange makes all adjacent
(up, down, left, right) fresh oranges rot.

Return the minimum number of minutes until no fresh orange
remains. If impossible, return -1.

Input:
[[2,1,1],
 [1,1,0],
 [0,1,1]]

Output: 4
// Solution: Multi-Source BFS

function orangesRotting(grid) {
    const rows = grid.length;
    const cols = grid[0].length;
    const queue = [];
    let fresh = 0;

    // Step 1: Find all rotten oranges + count fresh
    for (let r = 0; r < rows; r++) {
        for (let c = 0; c < cols; c++) {
            if (grid[r][c] === 2) queue.push([r, c, 0]); // [row, col, time]
            if (grid[r][c] === 1) fresh++;
        }
    }

    if (fresh === 0) return 0;  // Edge case: no fresh oranges

    // Step 2: BFS from all rotten oranges simultaneously
    const directions = [[1,0],[-1,0],[0,1],[0,-1]];
    let maxTime = 0;

    let i = 0;
    while (i < queue.length) {
        const [r, c, time] = queue[i++];

        for (const [dr, dc] of directions) {
            const nr = r + dr;
            const nc = c + dc;

            if (nr >= 0 && nr < rows && nc >= 0 && nc < cols
                && grid[nr][nc] === 1) {
                grid[nr][nc] = 2;     // Mark as rotten
                fresh--;
                maxTime = Math.max(maxTime, time + 1);
                queue.push([nr, nc, time + 1]);
            }
        }
    }

    return fresh === 0 ? maxTime : -1;
}

console.log(orangesRotting([[2,1,1],[1,1,0],[0,1,1]]));
OUTPUT: 4

WHY BFS?
BFS processes level by level — each level = 1 minute.
Multi-source BFS starts from ALL rotten oranges at minute 0.
Greedy expansion ensures minimum time.

Time: O(rows × cols)
Space: O(rows × cols) for the queue

Question 2 Asked: Two Sum (variant with indices)

PROBLEM:
Given an array of integers and a target, return INDICES
of two numbers that add up to target.
You may not use the same element twice.

Input:  nums = [2, 7, 11, 15], target = 9
Output: [0, 1]  (nums[0] + nums[1] = 2 + 7 = 9)

// NAIVE solution — O(n²) → DO NOT write this!
function twoSum(nums, target) {
    for (let i = 0; i < nums.length; i++) {
        for (let j = i + 1; j < nums.length; j++) {
            if (nums[i] + nums[j] === target) return [i, j];
        }
    }
}

// OPTIMAL solution — O(n) using HashMap!
function twoSum(nums, target) {
    const map = new Map();  // value → index

    for (let i = 0; i < nums.length; i++) {
        const complement = target - nums[i];

        if (map.has(complement)) {
            return [map.get(complement), i];
        }
        map.set(nums[i], i);
    }
}

console.log(twoSum([2, 7, 11, 15], 9));
OUTPUT: [0, 1]

WHY HASHMAP?
For each number, we need its complement (target - num).
Map gives O(1) lookup — one pass through the array!
Time: O(n) | Space: O(n)

Interviewer follow-ups:
- What if no solution exists? → return [] or throw Error
- What if duplicate numbers? → Map stores last index, handle carefully
- What if sorted array? → Two pointer approach → O(1) space

Round 3 — JavaScript and Node.js Deep Dive

Duration: 55–60 minutes | Core JS concepts + Node.js + live coding

This round is where they test HOW WELL you know JavaScript — not just that you use it!

Question 1: Explain the JavaScript Event Loop

Q: What is the Event Loop? What is the difference between
   the microtask queue and macrotask queue?

ANSWER:
=========

JavaScript is single-threaded — it can do ONE thing at a time.
The Event Loop is what lets it handle async operations
without blocking.

Components:
───────────
1. Call Stack    → where code executes (LIFO)
2. Web APIs      → setTimeout, fetch, DOM events (browser handles these)
3. Microtask Queue → Promise.then, queueMicrotask, MutationObserver
4. Macrotask Queue → setTimeout, setInterval, setImmediate (Node.js)

ORDER OF EXECUTION:
1. Run all synchronous code (Call Stack empties)
2. Run ALL microtasks (Promise.then chain completely)
3. Render the UI (browser only)
4. Run ONE macrotask (one setTimeout/setInterval)
5. Repeat from step 2

EXAMPLE — Predict the output:
console.log("1");

setTimeout(() => console.log("2"), 0);

Promise.resolve()
    .then(() => console.log("3"))
    .then(() => console.log("4"));

console.log("5");

OUTPUT:
1
5
3
4
2

WHY:
1, 5 → synchronous, runs immediately
3, 4 → microtasks (Promise.then), run before macrotask
2    → macrotask (setTimeout), runs last

Even though setTimeout has 0ms delay, it waits for
ALL microtasks to finish first!

Question 2: Implement Debounce from scratch

Q: Implement a debounce function. Where would you use it?

WHAT IS DEBOUNCE?
==================
Debounce ensures a function runs ONLY AFTER a specified
delay of inactivity. If called again before the delay
expires, the timer RESETS.

Real-World Use: Search bar — don't fire API call on every
keystroke. Wait until user stops typing for 300ms.

// Implementation
function debounce(fn, delay) {
    let timer;

    return function(...args) {
        clearTimeout(timer);       // Reset timer on each call

        timer = setTimeout(() => {
            fn.apply(this, args);  // Execute after delay
        }, delay);
    };
}

// Usage
const searchAPI = debounce((query) => {
    console.log("Fetching:", query);
    // fetch(`/api/search?q=${query}`)
}, 300);

// Simulate user typing
searchAPI("r");        // Timer starts
searchAPI("re");       // Timer RESET
searchAPI("rea");      // Timer RESET
searchAPI("reac");     // Timer RESET
searchAPI("react");    // Timer starts, no more calls...

// After 300ms of silence:
OUTPUT: Fetching: react

Only ONE API call instead of 5!

VS THROTTLE (common follow-up):
─────────────────────────────────
Debounce → "call only after silence of Xms"
Throttle → "call at most once every Xms, no matter what"

function throttle(fn, limit) {
    let lastCall = 0;
    return function(...args) {
        const now = Date.now();
        if (now - lastCall >= limit) {
            lastCall = now;
            fn.apply(this, args);
        }
    };
}

USE CASES:
Debounce  → Search bar, form validation, resize handler
Throttle  → Scroll events, mousemove, button spam prevention

Question 3: Explain Promises and async/await

Q: What is the difference between Promise.all,
   Promise.allSettled, Promise.race, and Promise.any?

ANSWER:
=========

const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.reject("Error!");

// Promise.all → ALL must succeed. If ONE fails → reject!
Promise.all([p1, p2, p3])
    .then(values => console.log(values))
    .catch(err => console.log("Failed:", err));
OUTPUT: Failed: Error!

// Promise.allSettled → waits for ALL, doesn't reject on failure
Promise.allSettled([p1, p2, p3])
    .then(results => console.log(results));
OUTPUT:
[
  { status: "fulfilled", value: 1 },
  { status: "fulfilled", value: 2 },
  { status: "rejected",  reason: "Error!" }
]

// Promise.race → first one to settle (resolve OR reject) wins
Promise.race([p1, p2, p3]).then(v => console.log(v));
OUTPUT: 1  (p1 resolved first)

// Promise.any → first SUCCESS wins. Rejects if ALL fail
Promise.any([p3, p1, p2]).then(v => console.log(v));
OUTPUT: 1  (p1 is first success; p3 rejected but ignored)

WHEN TO USE:
Promise.all       → Parallel API calls that ALL must succeed
                    (load user + posts + comments together)
Promise.allSettled → Show whatever loaded, show errors for rest
Promise.race      → Timeout pattern (race fetch vs timeout)
Promise.any       → Try multiple sources, use whichever responds first

Question 4: Node.js — What is the difference between process.nextTick and setImmediate?

Q: In Node.js, explain process.nextTick vs setImmediate
   vs setTimeout with 0ms delay.

ANSWER:
=========

Node.js Event Loop phases:
timers → I/O → poll → check → close

process.nextTick() → runs BEFORE the next event loop phase
                     even before microtasks? No — it runs IN
                     a special "nextTick queue" that drains
                     after each operation, before microtasks!

setImmediate()     → runs in the "check" phase of the event loop
                     (after I/O callbacks)

setTimeout(fn, 0)  → runs in the "timers" phase

// Example
console.log("Start");

setTimeout(() => console.log("setTimeout"), 0);
setImmediate(() => console.log("setImmediate"));
process.nextTick(() => console.log("nextTick"));
Promise.resolve().then(() => console.log("Promise"));

console.log("End");

OUTPUT:
Start
End
nextTick
Promise
setImmediate
setTimeout   (may swap with setImmediate — order not guaranteed in timers phase)

WHY nextTick before Promise?
process.nextTick queue drains BEFORE Promise microtasks in Node.js!

REAL WORLD:
process.nextTick → defer execution but before I/O
                   (e.g., emit events after constructor setup)
setImmediate    → defer to next iteration of event loop
                   (e.g., break up CPU-intensive work)

Round 4 — React and Frontend System Design

Duration: 55–60 minutes | React deep dive + Design a scalable frontend system

React Question 1: What happens during React reconciliation?

Q: Explain React's reconciliation algorithm and the Virtual DOM.

ANSWER:
=========

Virtual DOM: React keeps a lightweight JS copy of the real DOM.
When state changes:
  1. React creates a NEW Virtual DOM tree
  2. DIFFS the new tree vs the old tree (reconciliation)
  3. Calculates MINIMUM changes needed
  4. Applies ONLY those changes to the real DOM

WHY? Real DOM operations are slow.
     JS object comparison is fast.
     React batches real DOM updates for performance.

Reconciliation Rules:
─────────────────────
1. Same element type → update props (no unmount/mount)
2. Different type   → destroy old, create new subtree
3. Keys in lists    → help React identify which item changed

// BAD — no keys, React can't track items
<ul>
  {items.map(item => <li>{item.name}</li>)}
</ul>

// GOOD — keys help React diff correctly
<ul>
  {items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>

// Using index as key — bad for dynamic lists!
// If you add/remove items, index shifts → wrong diffs
items.map((item, i) => <li key={i}>...</li>)  // ❌ avoid for dynamic lists

React Question 2: useMemo vs useCallback

Q: What is the difference between useMemo and useCallback?
   When would you use each?

ANSWER:
=========

useMemo   → memoizes a COMPUTED VALUE
useCallback → memoizes a FUNCTION REFERENCE

// useMemo Example
function ProductList({ products, filterText }) {
    // Without useMemo: recalculates on EVERY render
    // With useMemo: recalculates ONLY when products or filterText changes
    const filtered = useMemo(() => {
        return products.filter(p =>
            p.name.toLowerCase().includes(filterText.toLowerCase())
        );
    }, [products, filterText]);

    return <ul>{filtered.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}

// useCallback Example
function Parent() {
    const [count, setCount] = useState(0);

    // Without useCallback: new function reference every render
    // → Child always re-renders even if nothing changed!
    const handleClick = useCallback(() => {
        console.log("clicked");
    }, []);  // Stable reference — never recreated

    return (
        <div>
            <button onClick={() => setCount(c => c + 1)}>{count}</button>
            <ExpensiveChild onClick={handleClick} />
        </div>
    );
}

const ExpensiveChild = React.memo(({ onClick }) => {
    console.log("Child rendered!");
    return <button onClick={onClick}>Click me</button>;
});

// Without useCallback → Child renders on EVERY count change
// With useCallback    → Child renders ONLY ONCE (function ref is stable)

SUMMARY:
useMemo     → expensive calculations (filtering, sorting big arrays)
useCallback → stable function refs passed to memoized children

System Design Question: Design a Type-Ahead Search Component

Q: Design a production-ready type-ahead / autocomplete search
   for an e-commerce site with millions of products.
   Discuss component design, API calls, caching, and performance.

ANSWER WALKTHROUGH:
====================

1. COMPONENT ARCHITECTURE
───────────────────────────
<SearchBar>
  ├── <Input>            ← controlled input
  ├── <SuggestionList>  ← dropdown
  │     └── <SuggestionItem> (× N)
  └── <Spinner>          ← loading state

State:
  query       → current input value
  suggestions → array of results
  isLoading   → boolean
  cache       → Map() for memoizing results

2. DEBOUNCE API CALLS
───────────────────────────
const [query, setQuery] = useState("");
const [suggestions, setSuggestions] = useState([]);
const cache = useRef(new Map());

const fetchSuggestions = useCallback(
    debounce(async (q) => {
        if (q.length < 2) return setSuggestions([]);

        // Check cache first!
        if (cache.current.has(q)) {
            return setSuggestions(cache.current.get(q));
        }

        const res = await fetch(`/api/search?q=${q}`);
        const data = await res.json();
        cache.current.set(q, data);
        setSuggestions(data);
    }, 300),
    []
);

useEffect(() => { fetchSuggestions(query); }, [query]);

3. HANDLE RACE CONDITIONS
───────────────────────────
// User types fast: "r" → "re" → "rea" → "react"
// Responses may come back OUT OF ORDER!
// "react" result arrives, then "re" result overwrites it → BUG!

// FIX: Use AbortController
const controllerRef = useRef(null);

const fetchSuggestions = async (q) => {
    if (controllerRef.current) {
        controllerRef.current.abort();  // Cancel previous request!
    }
    controllerRef.current = new AbortController();

    const res = await fetch(`/api/search?q=${q}`, {
        signal: controllerRef.current.signal
    });
    const data = await res.json();
    setSuggestions(data);
};

4. KEYBOARD NAVIGATION + ACCESSIBILITY
───────────────────────────────────────
- Arrow Up/Down → navigate suggestions
- Enter → select highlighted suggestion
- Escape → close dropdown
- aria-role="combobox", aria-expanded, aria-activedescendant
- Screen reader announcements for result count

5. PERFORMANCE OPTIMIZATIONS
───────────────────────────────
- Debounce (300ms) → reduce API calls
- Client cache (Map) → instant results for repeated queries
- React.memo on SuggestionItem → avoid re-rendering all items
- Virtualize list if > 50 suggestions (react-window)

6. BACKEND / API
───────────────────────────
GET /api/search?q=react&limit=10
Response: { suggestions: ["React", "React Native", "ReactDOM"] }

Server-side: Elasticsearch / Redis prefix trie for fast lookups
CDN caching for popular queries
Rate limiting per user to prevent abuse

Round 5 — Bar Raiser Round (Leadership Principles)

The Bar Raiser is a senior Amazonian from a DIFFERENT team with veto power over your offer. This round is pure behavioral — and it's as important as all technical rounds combined.

Bar Raiser Goal:
=================
NOT just checking if you're good enough for this role.
They check: "Is this person better than 50% of current
Amazonians at this level?"

They will:
- Dig deeper with follow-up questions
- Challenge your answers ("What would you have done differently?")
- Look for ownership, metrics, and real impact
- Probe until they find a contradiction or confirm it's real

Formula: Strong LP = Offer even with mediocre code
         Weak LP  = No offer even with perfect code

Question 1: Customer Obsession

Q: Tell me about a time you went beyond what was required
   to improve the customer experience.

STAR ANSWER:
──────────────
S — While working on an e-commerce checkout page, analytics
    showed 34% cart abandonment rate on the payment step.

T — My task was to fix a bug in the coupon field, but I
    noticed the real problem — the page had 12 form fields
    and no progress indicator.

A — I didn't just fix the bug. I proposed a 3-step checkout
    wizard with a progress bar. I built a prototype in 2 days
    using React and showed it to the product team with data.
    Got approval. Delivered in one sprint.

R — Cart abandonment dropped from 34% to 19% in 3 weeks.
    Monthly revenue increased by ₹12 lakhs. This was later
    rolled out across all product categories.

LP: Customer Obsession → I looked BEYOND my ticket to fix
    the actual customer problem.

Question 2: Ownership

Q: Describe a situation where you took ownership of something
   that wasn't your responsibility.

STAR ANSWER:
──────────────
S — Our backend team had a Node.js API that was returning
    responses in 4–6 seconds for a search endpoint. Users
    were complaining. The backend team was overloaded.

T — This was not my area — I'm a frontend developer. But
    the user experience was suffering and the deadline was
    in 3 days.

A — I profiled the API myself. Found that 3 database queries
    were running sequentially instead of in parallel. Changed
    them to Promise.all(). Also added Redis caching for
    frequently searched terms. Raised a PR to the backend repo
    with full documentation and benchmarks.

R — Response time dropped from 5.2 seconds to 380ms.
    Senior engineers reviewed and merged it. The fix went live
    in 2 days — before the deadline.

LP: Ownership → "Leaders don't say 'that's not my job.'"

Question 3: Dive Deep (Technical Leadership)

Q: Tell me about a time you had to deep-dive into a complex
   technical problem to find the root cause.

STAR ANSWER:
──────────────
S — Production showed a memory leak in our React app. Memory
    usage grew from 80MB to 800MB over 4 hours and then the
    tab crashed. No obvious errors in the logs.

T — I was the only senior frontend dev available. Needed to
    find and fix the root cause within the day.

A — Used Chrome DevTools → Memory tab → took heap snapshots
    every 30 minutes. Compared snapshots and found thousands
    of event listener objects were accumulating.

    Root cause: a useEffect was adding a window scroll event
    listener on EVERY render — but the cleanup function was
    returning early due to a bug, so listeners were never removed.

    useEffect(() => {
        window.addEventListener("scroll", handleScroll);
        // BUG: cleanup was inside an if-block that never ran!
        return () => {
            window.removeEventListener("scroll", handleScroll);
        };
    }); // ← missing dependency array → runs on every render!

    Fix: Added dependency array [] so it runs once.
    Verified: Memory stabilized at 85MB for 24 hours.

R — Zero crashes in production. Wrote internal doc on
    React memory leak patterns. Shared with team in next sprint.

LP: Dive Deep + Learn and Be Curious

Question 4: Deliver Results

Q: Tell me about a time you delivered a project despite
   facing major obstacles.

STAR ANSWER:
──────────────
S — We had a 6-week deadline to migrate a legacy jQuery app
    to React. 3 weeks in, 2 of our 4 developers resigned.

T — I needed to deliver the migration with half the team
    and without extending the deadline (client contract).

A — I immediately re-estimated and re-prioritized. Cut scope
    to the 3 most critical modules (checkout, search, user profile).
    Set up component library so any new developer could onboard
    in 1 day. Worked with QA to automate regression tests so
    we didn't spend time on manual testing.

R — Delivered all 3 critical modules on time. Deferred 2 less-
    critical modules to next sprint with client agreement.
    Post-launch: page load reduced from 6.8s to 1.9s.
    Client extended the contract for 6 more months.

LP: Deliver Results + Bias for Action

Quick Recap — All Rounds

Round What's Tested Key Topics Duration
Round 0
Online Assessment
Filter Round HTML/CSS/JS MCQs, Machine Coding (Carousel) 2 hours
Round 1
Phone Screen
JS Basics + 1 LP null/undefined, var/let/const, array flatten, STAR story 45–60 min
Round 2
DSA Coding
Algorithms BFS, HashMap, Arrays, Trees, Graph traversal 55–60 min
Round 3
JS / Node.js Deep Dive
Core JavaScript + Node Event Loop, Debounce/Throttle, Promises, nextTick 55–60 min
Round 4
React + System Design
React + Architecture Reconciliation, useMemo/useCallback, Type-ahead design 55–60 min
Round 5
Bar Raiser
Leadership Principles Ownership, Customer Obsession, Dive Deep, Deliver Results 55–60 min

Key Tips to Remember

  • Every single round has LP questions — prepare 8–10 STAR stories before the interview
  • Always include metrics in your answers — "dropped by 34%", "saved ₹12 lakhs", "from 5s to 380ms"
  • Amazon loves Vanilla JS — don't depend on React for OA and machine coding rounds
  • Think out loud — interviewers want to see how you reason, not just the final answer
  • Microtasks before macrotasks — Promise.then runs before setTimeout in the event loop
  • Debounce resets timer, Throttle limits frequency — know both implementations
  • Always handle edge cases in DSA — empty input, single element, negative numbers
  • STAR answers should be YOUR actions — use "I" not "we" in the Action step
  • Bar Raiser has VETO POWER — treat this round as the most important one
  • Promise.all vs allSettled — one fails on first rejection, other waits for all
  • useCallback for function refs, useMemo for computed values — don't mix them up
  • AbortController for cancelling stale fetch requests — shows production awareness
  • Accessibility matters at Amazon — mention aria attributes in UI design answers
  • Weak LP answers = no offer — even if your code was perfect

Keep coding, keep learning! This is the real Amazon experience — go crack it! See you in the next one!


Sources: Frontend Interview Handbook | GeeksforGeeks Amazon SDE-II Frontend | IGotAnOffer — Bar Raiser Guide | GreatFrontend Amazon Guide | Dev.to — Amazon SDE May 2024 Experience