cswork / libraryOpen practice workspace

Interview guides · Amazon Web Services

Amazon Web Services Software Engineer Interview Guide

Amazon Web Services Software Engineer interview guide: reported rounds, coding, design and Leadership Principles questions, with a 7-day plan.

The 60-second brief

Amazon Web Services runs cloud computing infrastructure and platforms. Candidates name EC2 for compute, S3 for storage and DynamoDB as a database among its foundational services, with Lambda and SQS also appearing in the role's nice-to-have list, plus generative AI services built on top. Candidates describe the Software Engineer role as building and operating backend services of this kind: writing design documents and taking them through peer review, shipping production code in Java, C++, Python or Go, adding unit, integration and canary tests, owning deployment pipelines, watching operational dashboards, carrying on-call and writing Correction of Errors post-mortems after incidents.

This guide is for candidates interviewing for a Software Engineer position at Amazon Web Services at any level candidates describe (entry level, mid-level or senior). It covers all five reported stages, fifteen reported technical questions across system design, algorithms and low-level design, five reported Leadership Principles questions, and a seven-day plan that pairs them.

Candidates report five stages over roughly four to six weeks: resume screening, an online assessment, a technical phone screen, the loop itself and a Bar Raiser interview. Behavioral questions are reported in every round, alongside coding and design.

Focus areas

  • Data Structures & Algorithms (DSA)
  • Coding Interviews (Algorithmic Problem Solving)
  • Machine Learning System Design (System ML Design)

Overview

The reported questions fall into three technical groups plus a behavioral group. The design group mixes service-scale prompts (an EC2 availability lookup across zones, a task scheduler with dependencies and retry limits, a CSV merging API for files larger than memory, an org chart system, a secure healthcare data pipeline). The algorithm group includes an O(1) structure with get, set, setall and delete, a grid-based locker placement problem, a streaming parenthesis check and an LRU cache. The low-level design group asks for class models: file system search, crafting recipes, a dependency manager that detects cycles, a parking lot and Battleship.

Candidates also report that the role is about operating services, not only writing them. Design documents, peer review, canary tests, dashboards, on-call and Correction of Errors write-ups all appear in the role description, so a design answer that covers failure modes, alarms and rollback fits the work better than one that stops at a diagram. When you design the task scheduler or the EC2 pool lookup, say what you would alarm on and what happens during a bad deploy.

Language choice is open: candidates list Java, C++, Python, Go or C# as acceptable, with strong object-oriented design, multithreading and concurrency control as expected foundations. Candidates also report a shared online editor without autocomplete or code execution in the technical stages, so rehearse writing complete solutions in a plain text file and tracing test cases by hand.

Detailed knowledge of AWS products is described as helpful but secondary to computer science fundamentals. Know the vocabulary (queues and pub/sub for decoupling, key-value stores versus relational databases, multi-tier caching, leader election, token bucket rate limiting) well enough to justify a choice, and spend the rest of your time on fundamentals and on your stories.

The Amazon Web Services interview process

The five stages below are what candidates report, in the order they report them. The reported questions are not tied to a specific stage, so prepare every question type for the phone screen, the loop and the Bar Raiser.

Resume Screening

Candidates describe this stage as a review of your resume against the position's qualifications before any interview. The qualifications candidates describe vary by level: a degree plus internship experience at entry level, several years of backend experience for mid-level, and a longer record of leading designs and mentoring for senior roles. Treat the resume as the source of your stories: every line you list is something you can be asked to explain.

What to demonstrate

  • Fit between your experience and the level's qualifications
  • Evidence of data structures, algorithms and object-oriented work in a language the role lists
  • Operating or distributed-systems experience, where you have it

How to prepare

  • Mark each resume bullet that has a measurable result (latency, throughput, cost, error rate) and rewrite the ones that do not.
  • Match your experience to the level you are targeting and make the matching evidence easy to find: a service you owned, a design you led, an on-call stretch.
  • Pick the language you will code in during the loop and name it on the resume with the projects that used it.
  • List the five or six projects you could talk about for a full hour, since the later stages draw on the same history.

Online Assessment

Candidates report two programming challenges plus a work-style assessment, completed online. The coding topics candidates list for the coding stages are graph traversal (DFS, BFS, topological sort, Dijkstra), arrays with hash maps, sliding windows, two pointers and prefix sums, then trees, heaps, tries and dynamic programming. No further detail on the work-style portion is reported, so prepare it by knowing your own working examples well.

What to demonstrate

  • Working code for two programming problems
  • Choice of data structure and its complexity
  • Consistency of your approach to work across the assessment

How to prepare

  • Solve timed problems in the families above: a sliding window over a stream of response times, a prefix-sum range query, a BFS on a grid, a topological sort.
  • Practise reading a problem, writing down the complexity target, then coding and testing against your own edge cases (empty input, single element, duplicates).
  • Write solutions in a plain editor with no autocomplete and no run button, then trace two cases by hand before declaring it done.
  • Reread the Leadership Principles candidates cite (Customer Obsession, Ownership, Bias for Action, Dive Deep, Insist on the Highest Standards) so your instincts on the work-style questions match stories you could back up.

Technical Phone Screen

Candidates report a coding interview with a senior engineer. The coding questions reported for this guide are the ones to rehearse for this stage: a structure with get, set, setall and delete in constant time, an LRU cache, a grid placement problem, a streaming parenthesis check and a system-cost array problem. Expect to explain your choices while you write.

What to demonstrate

  • Working code in a shared editor
  • Time and space complexity of the solution you choose
  • Handling of edge cases and a path from brute force to an efficient version

How to prepare

  • Rehearse the O(1) structure with setall (a version counter or a global value with per-key stamps) and the LRU cache (hash map plus doubly linked list) until you can write each without hesitating.
  • For every problem, say the brute-force approach and its complexity first, then the improvement, then code.
  • Extend each solution once after finishing: make the LRU cache thread-safe, or let the parenthesis check take user-defined delimiters.
  • Practise saying your assumptions and complexity out loud while typing, since the stage is a conversation with a senior engineer.

The Loop

Candidates report a series of three to five 60-minute interviews that mix behavioral and technical questions. The technical share covers system design, algorithms and low-level design, and candidates also report behavioral questions in every round. Plan for each interview to include at least one story and one technical problem.

What to demonstrate

  • System design: requirements, scale estimates, data store choice, caching, queues and fault tolerance
  • Low-level design: clean class models, extensibility and concurrency control
  • Leadership Principles behaviors told through STAR stories
  • Coding fundamentals and complexity analysis

How to prepare

  • For each design prompt (EC2 pool lookup, task scheduler, CSV merge, org chart, healthcare pipeline), write a one-page outline: requirements, rough numbers, data model, API, failure modes.
  • For each low-level design prompt (file system search, crafting recipes, dependency manager, parking lot, Battleship), sketch the classes and the one design pattern that keeps it extensible, such as Strategy, Composite or Observer.
  • Prepare at least one story per principle candidates cite, and make sure no single project carries most of them.
  • Practise stating clarifying questions and functional and non-functional requirements before any diagram.

Bar Raiser Interview

Candidates report an interview with an experienced interviewer from a team outside the one hiring. This person does not share your team's context, so every answer needs its own background. The same technical and behavioral question types as the rest of the loop are the right preparation.

What to demonstrate

  • Whether your answers stand on their own for someone outside the team
  • Consistency between your stories and the rest of the loop
  • Technical depth when a project is questioned past the first layer

How to prepare

  • Rehearse each story with a stranger: one sentence of context, then your own actions, then numbers, without internal project names or jargon.
  • Know the trade-offs behind your two or three biggest design decisions, including the alternative you rejected and why.
  • Prepare your answers for the incident story and the disagreement story in full, since both invite follow-up about what you personally decided.
  • Keep a short list of stories you have not used earlier in the loop so you can avoid repeating a project.

Where candidates lose points

Implementing setall by looping over every key, which makes the structure O(n) and misses the point of the question

Store a global value and a version counter, with each key remembering the version of its last write. A set after setall writes the current version, a get compares versions, and delete stays O(1). State the invariant before coding.

Opening a design answer with boxes (queue, cache, database) before fixing requirements for the task scheduler, EC2 pool lookup or CSV merge

Open with who calls it, the read and write rates, consistency needs and failure behavior, then estimate throughput and storage. Only then name components and say which requirement each one serves.

Modeling a low-level design problem as one large class with branching on attributes, for example file system search with an if for each of size, extension and date

Model each criterion as a small filter object behind a common interface, combine filters with AND and OR, and keep traversal separate from matching. Show how a new criterion is added without editing existing classes.

Presenting a cache, a scheduler or a dependency manager without concurrency or cycle handling, even though the reported questions ask for them

For the LRU cache, say where the lock goes and what contention costs. For the dependency manager, name the cycle detection method (three-state DFS or Kahn's algorithm) and report the cycle path in the exception.

Telling behavioral stories in 'we' language with no numbers, so your own contribution is unclear

Write each story with your actions in the first person and at least one measurable result (latency, error rate, cost, time saved). Say which decisions were yours and which belonged to others.

Questions & practice

Given an array representing system resource allocations, implement an optimal Minimize System Cost Algorithm t

Medium · Data Structures & Algorithmic Problem So

Given an array representing system resource allocations, implement an optimal Minimize System Cost Algorithm to eliminate continuous cost overhead.

Approach

  1. Pin the cost model down with a worked example before coding: which operation is allowed (combine two allocations, remove one, level them out), what each operation costs, and whether "continuous overhead" means a cost charged on everything that remains at each step.
  2. If any two allocations can be combined at a cost equal to their sum, merge the two smallest each time using a min-heap. That is O(n log n) time and O(n) space, and an exchange argument proves it optimal, as for Huffman coding.
  3. If only adjacent allocations can be combined, the greedy choice is no longer safe. Use interval DP, dp[i][j] = min over k of dp[i][k] + dp[k+1][j] + sum(i..j), with prefix sums for the range sums: O(n^3) time and O(n^2) space.
  4. If the goal is to bring every allocation to one level t at cost |a_i - t|, choose t as the median: O(n log n) by sorting or O(n) on average with quickselect. For squared cost, the mean is optimal.
  5. Edge cases: an empty or single-element array (cost 0), sums that overflow 32 bits (use 64-bit), and negative values if the input allows them.

Follow-up

  • Prove that merging the two smallest is optimal, then explain why the same greedy choice can fail when only adjacent merges are allowed.
  • If the input arrives already sorted, can you reach O(n)? (Two queues: the sorted input and the merged results.)
  • What changes if every merge also carries a fixed overhead cost?

Solve the Optimal Amazon Locker Placement problem: given coordinate grids of residential houses and terrain ob

Medium · Data Structures & Algorithmic Problem So

Solve the Optimal Amazon Locker Placement problem: given coordinate grids of residential houses and terrain obstacles, determine the optimal geographical placement for fulfillment lockers.

Approach

  1. Clarify the objective: one locker or k lockers, minimizing the total walking distance or the worst one, 4-directional movement, obstacles impassable, and whether a locker may sit on a house cell.
  2. For one locker minimizing total distance, run a BFS from every house over open cells, accumulating a distance sum and a reach count per cell. The answer is the cell with the smallest sum among cells reached by every house, or -1 if there is none. That is O(H x R x C) time and O(R x C) space.
  3. Without obstacles, Manhattan distance separates by axis: the median row and median column minimize the total in O(H log H). Obstacles break this shortcut, which is why the BFS is needed.
  4. For k lockers, placement is a k-median or k-center problem, which is NP-hard on general graphs. Enumerate combinations from precomputed BFS distances when k and the candidate set are small, or use a greedy heuristic and say it is not guaranteed optimal. Once the lockers are fixed, one multi-source BFS gives every house its nearest locker in O(R x C).
  5. Edge cases: houses walled off from each other, a grid with no free cell, ties between candidate cells, and choosing to BFS from whichever side (houses or candidate cells) is smaller.

Follow-up

  • How does the answer change if you minimize the longest walk instead of the total?
  • With thousands of houses and few open cells, how would you cut the BFS cost?
  • How would you place two lockers, and what is the cost of your approach?

Write an efficient algorithm to perform a Valid Parenthesis String Check on streaming character data, and exte

Medium · Data Structures & Algorithmic Problem So

Write an efficient algorithm to perform a Valid Parenthesis String Check on streaming character data, and extend your design to support custom user-defined delimiters.

Approach

  1. Base case: a stack of expected closers. Push on an opener; on a closer, the stack top must match it. The string is valid if the stack is empty at the end. O(n) time, O(depth) space. With only one bracket type a counter is enough, but per-type counters fail on "([)]", so mixed types need the stack.
  2. Ask whether the input contains '' that may stand for '(', ')' or nothing, which is the classic Valid Parenthesis String problem. Then track the range [lo, hi] of possible open counts: '(' raises both, ')' lowers both, '' lowers lo and raises hi. Fail if hi < 0, clamp lo at 0, and the string is valid if lo == 0 at the end. O(n) time, O(1) space.
  3. For a stream, wrap the state in a class with feed(chunk) and finish(). Keep the stack across chunks, fail fast and stay invalid after the first mismatch, report the character offset of the error, and check for an empty stack only in finish().
  4. Custom delimiters: take an opener-to-closer map at construction and validate it (no character may act as both an opener and a different pair's closer). For pairs whose opener and closer are the same symbol, such as quotes, pop if the top is that symbol and push otherwise. For multi-character delimiters, match with a trie and carry the partial match across chunk boundaries.
  5. Bound memory on untrusted streams with a maximum nesting depth, and decide whether an escape character suppresses the next delimiter.

Follow-up

  • A two-character delimiter is split across two chunks. How does your parser still recognise it?
  • How do you stop a malicious stream of a million openers from exhausting memory?
  • Would you return only true or false, or the position and kind of the first error? Why?

Implement a Least Recently Used (LRU) cache and discuss cache invalidation, thread safety, and memory manageme

Medium · Data Structures & Algorithmic Problem So

Implement a Least Recently Used (LRU) cache and discuss cache invalidation, thread safety, and memory management strategies under heavy read/write loads.

Approach

  1. Use a hash map from key to node plus a doubly linked list ordered by recency, with sentinel head and tail nodes. get moves the node to the front. put inserts at the front or updates in place, and on overflow evicts the tail and removes it from the map. Both operations are O(1).
  2. Name the library shortcuts and their behaviour: Java's LinkedHashMap with accessOrder=true and an overridden removeEldestEntry, or Python's OrderedDict with move_to_end and popitem(last=False). Then write the manual version.
  3. Edge cases: capacity 0, put on an existing key (update the value and refresh recency without evicting), and removing the evicted key from the map as well as the list.
  4. Thread safety: get changes the list, so a read-write lock gives no concurrency, because every get needs the write lock. Options are one mutex (simple, contended), sharding into N independent LRUs by key hash (approximate global recency), or buffering recency updates and applying them in batches, CLOCK-style.
  5. Invalidation and memory: per-entry TTL checked lazily on get plus a periodic sweep, explicit invalidate on writes to the backing store, and a capacity in bytes rather than entries when value sizes vary widely.

Follow-up

  • Why does a read-write lock not help an LRU cache, and what would you use instead?
  • When a hot key is evicted and a thousand threads miss at once, how do you avoid stampeding the backing store?
  • How would you turn this into an LFU cache that is still O(1)?

Find version gaps and relay lag with window functions

Medium · window functions · event tables · frame semantics

outbox_event holds event_id, aggregate_type, aggregate_id, aggregate_version, event_type, payload, status ('pending','published','dead'), attempts, created_at, published_at. A projection is missing rows and you must decide whether the relay skipped events or the consumer dropped them. Write three queries over the last seven days: one listing every aggregate_id whose published aggregate_version sequence has a hole, one giving per-day counts with a running total, and one returning the newest published event per aggregate. For each, say where the window function is evaluated relative to WHERE and LIMIT. PostgreSQL 16.

Approach

  1. Gaps: compute lead(aggregate_version) OVER (PARTITION BY aggregate_id ORDER BY aggregate_version) in a subquery, then filter next_version <> aggregate_version + 1 in the outer query. Window functions are evaluated after WHERE, GROUP BY and HAVING and before the outer ORDER BY and LIMIT, so the predicate cannot sit in the same WHERE clause and PostgreSQL 16 has no QUALIFY.
  2. Say what the seven-day filter does to the answer: it truncates every partition, so the first row per aggregate has no predecessor inside the window and a hole spanning the boundary is invisible. Widen the window, or join to resource.version as the authority for the true maximum.
  3. Running total: SELECT date_trunc('day', created_at) AS d, count() AS n, sum(count()) OVER (ORDER BY date_trunc('day', created_at) ROWS UNBOUNDED PRECEDING). An aggregate inside a window call is legal because grouping runs before windowing. The grouping key is unique per row here so ROWS and RANGE agree, but write the frame anyway — over ungrouped rows with tied timestamps the default RANGE frame pulls in every peer row and the total jumps.
  4. Newest per aggregate: DISTINCT ON (aggregate_id) ... ORDER BY aggregate_id, aggregate_version DESC is the cheap PostgreSQL-only form when an index matches that order; row_number() OVER (PARTITION BY aggregate_id ORDER BY aggregate_version DESC) = 1 is the portable form and needs a subquery for the same evaluation-order reason as the gap query.

Follow-up

  • Relay failover redelivers events. Does a duplicate break the gap query, and how would you detect one from this table alone?
  • Turn the gap check into a continuous monitor rather than a query someone runs after an incident. What does it watch?

Replace offset paging on the resource feed with keyset

Easy · keyset pagination · composite index · sort order

resource holds resource_id, tenant_id, owner_user_id, title, body_ref, version, status ('draft','active','archived','deleted'), created_at, updated_at, deleted_at, with an index on (tenant_id, status, updated_at DESC, resource_id DESC). The listing endpoint returns active resources for one tenant, newest update first, 50 per page, today with LIMIT 50 OFFSET n. Tenants reach page 400 and rows are created while they read. Write the keyset query, define what the cursor carries and how it is encoded, and say which part of the index each predicate uses. Assume PostgreSQL 16.

Approach

  1. Name the two failures separately. OFFSET 20000 makes the server produce and discard 20,000 rows, so page cost grows with depth rather than with page size. Independently, any write that changes how many rows sort above the offset moves the window between two fetches, and the direction decides which anomaly you get: an insert lands at the head of updated_at DESC and pushes already-returned rows down past the boundary, so they are returned a second time; a delete above the offset, or a row whose updated_at is bumped above the cursor, pulls rows up and one is never returned at all. Nothing in the response reveals either.
  2. Write the seek: WHERE tenant_id = $1 AND status = 'active' AND (updated_at, resource_id) < ($2, $3) ORDER BY updated_at DESC, resource_id DESC LIMIT 50. The row-value comparison is one index range rather than a disjunction, and both columns are NOT NULL, which is what makes that comparison well defined.
  3. Map each predicate onto the index: tenant_id and status are equality on the leading columns, (updated_at, resource_id) is the range, and the ORDER BY matches the index order so no Sort node appears and the scan stops after 50 rows. The DESC in the definition only matters for mixed directions — a plain ascending btree on the same columns is read backwards for this query.
  4. Put both sort columns in the cursor and nothing the client can tamper with into another tenant: base64 of (updated_at, resource_id), validated server-side, with tenant_id taken from the principal.

Follow-up

  • The client asks for 'jump to page 400'. What do you offer instead, and what does the honest version cost?
  • Sort order becomes user-selectable across four columns. How many indexes is that, and which would you refuse to add?

Design an EC2 Instance Available Pool Retrieval System capable of querying hardware availability across multip

Medium · System Design & Cloud Architecture

Design an EC2 Instance Available Pool Retrieval System capable of querying hardware availability across multiple availability zones under high concurrency.

Approach

  1. Split the API in two before drawing anything: a high-volume read call (availability by instance type, optionally per AZ) that may be slightly stale, and a reserve call that must never hand the same capacity to two callers.
  2. Partition inventory by AZ, keyed by (AZ, instance type), with free-slot counts derived from host-level slot records. Each AZ owns its own inventory so one AZ failing does not block the others. Build the cross-AZ view by fanning out with per-AZ timeouts, or by having each AZ publish counts to a read cache.
  3. Make reserve a conditional write: decrement only if count >= n (compare-and-set on a version or count), never read-then-write. Shard the counters for hot instance types, and give reservations a lease with a TTL so a crashed caller's capacity comes back.
  4. Serve reads from a cache with a short TTL and present the counts as hints. The reserve call does the authoritative check, so a stale read costs at most a retry, never a double allocation.
  5. Cover failure: idempotency tokens on reserve so client retries do not double-book, partial results flagged when an AZ times out, and a reconciliation job that compares counters with host heartbeats to repair drift.

Follow-up

  • Two callers ask for the last three slots of one type in the same AZ at the same moment: walk through exactly what stops both from succeeding.
  • How would you answer a request for 50 instances that must be spread across at least two AZs?
  • How do you detect and repair drift between the availability counters and the real state of the hosts?

How would you architect a Task Scheduler API that manages asynchronous job execution, dependency tracking, and

Medium · System Design & Cloud Architecture

How would you architect a Task Scheduler API that manages asynchronous job execution, dependency tracking, and execution retry limits under strict SLAs?

Approach

  1. Define the API and the state machine: submit(payload, runAt, dependsOn[], maxRetries, idempotencyKey) returns a task id, plus getStatus and cancel. States run PENDING, READY, RUNNING, then SUCCEEDED, RETRY_WAIT, FAILED or DEAD.
  2. Track dependencies as a DAG stored with each task: a count of unfinished parents plus a children list. When a task succeeds, decrement each child's count and mark the child READY at zero (Kahn's algorithm, applied incrementally). At submit time, reject any edge set that would form a cycle.
  3. Dispatch from a durable store indexed by runAt (time-bucketed partitions or a sorted index). Workers lease tasks with a visibility timeout and send heartbeats, and an expired lease puts the task back to READY. Delivery is therefore at-least-once, so task handlers must be idempotent.
  4. Retries: exponential backoff with jitter and a stored attempt counter. After maxRetries, the task goes to a dead-letter queue and its dependents are marked blocked or failed, according to a policy the caller chooses.
  5. For the SLA, measure start lag (actual start minus runAt) and queue depth, autoscale workers on them, use separate priority queues, and partition the task-id space with one leader per partition so there is no single scheduler bottleneck.

Follow-up

  • A worker finishes the job but crashes before acknowledging it. What happens, and how do you keep side effects from running twice?
  • A parent task fails permanently: do you cancel its whole subtree, hold it, or let the submitter decide?
  • How do you avoid a hot partition when a million tasks are all scheduled for exactly midnight?

Design a CSV Merging API that ingests and streams gigabyte-scale datasets under constrained memory limits.

Medium · System Design & Cloud Architecture

Approach

  1. Pin down what "merge" means first: concatenating files whose headers differ, joining on a key column, or merging files that are already sorted by a key. Also settle the duplicate-key rule (last wins, keep all, or aggregate).
  2. If the inputs are sorted by the key, use a k-way merge with a min-heap of one cursor per file, streaming rows. Time is O(N log k), and memory is O(k) rows plus read buffers, independent of file size.
  3. If they are not sorted, use an external sort: read chunks that fit the memory budget, sort each one, spill sorted runs to disk or object storage, then k-way merge the runs. Add merge passes if the number of runs exceeds the open-file or buffer budget.
  4. Use a real CSV parser, not line splitting: quoted fields can contain commas and newlines. Handle the encoding and BOM, build the output header as the union of input headers with blanks for missing columns, and decide what to do when the same column has conflicting types.
  5. Run it as an async job: upload to storage, POST a merge request that returns a job id, poll its status, then stream the result back with chunked transfer. Checkpoint completed runs so a failed worker resumes instead of restarting.

Follow-up

  • You have 1,000 sorted inputs but memory for only 100 read buffers. How do you merge them?
  • The worker dies at 80% done. How does the job resume without producing duplicate rows?
  • One field contains a newline inside quotes. How does your reader handle it, and what happens with a naive line split?

How would you design an Org Chart System to manage complex corporate hierarchies with low-latency update and q

Medium · System Design & Cloud Architecture

How would you design an Org Chart System to manage complex corporate hierarchies with low-latency update and query patterns?

Approach

  1. List the operations and how often each runs: manager chain (ancestors), direct reports, the full subtree, moving an employee or a whole subtree, add and remove, and the lowest common manager of two people.
  2. Compare storage models by that read/write mix. An adjacency list (manager_id) gives O(1) moves but needs a recursive query for subtrees. A materialized path answers subtree queries with a prefix match, but a move rewrites the path of every node in the moved subtree. A closure table indexes ancestors and descendants, but a move costs O(subtree x depth) rows. Nested sets give fast subtree reads but expensive updates.
  3. On a re-parent, reject cycles: walk the new manager's ancestor chain (O(depth)) and fail if the moved employee is on it. Do this inside the same transaction as the move so two concurrent moves cannot create a loop together.
  4. For low-latency reads, cache ancestor chains and per-subtree headcounts. On a move, update the counts along the old and new ancestor paths (O(depth)) and invalidate the cached chains of the moved subtree.
  5. Lowest common manager: bring both nodes to the same depth using parent pointers and stored depths, then walk up together, O(depth). Binary lifting gives O(log depth) if this query is hot.

Follow-up

  • A re-org moves 5,000 people at once. How do you apply it atomically and keep readers from seeing a half-moved tree?
  • How would you support dotted-line reporting, which turns the tree into a DAG?
  • What is the cost of a full-subtree query in each of the storage models you compared?

Design a secure cloud-based data processing pipeline for sensitive healthcare records while maintaining strict

Medium · System Design & Cloud Architecture

Design a secure cloud-based data processing pipeline for sensitive healthcare records while maintaining strict encryption and regulatory compliance.

Approach

  1. Clarify scope: the data sources, the volume, batch or streaming, which regulations apply (for example HIPAA for US health data), who consumes the output, and the retention rules.
  2. Encrypt in transit with TLS on every hop. Encrypt at rest with envelope encryption: a data key per object or dataset, wrapped by a master key held in a KMS. Use separate keys per dataset or tenant to limit the blast radius. Rotating the master key re-wraps the data keys, so the bulk data does not have to be re-encrypted.
  3. Split the pipeline into zones: a restricted landing zone, validation, then de-identification or tokenization of identifying fields before anything reaches analytics. The re-identification mapping lives in a separately secured store, and each stage runs under its own least-privilege role on private networking with no public endpoints.
  4. Make access auditable: immutable logs of who read which dataset or record, lineage from source to output, and alerts on unusual access patterns.
  5. Reliability without leaks: idempotent stages, a quarantine area for malformed records, error messages and logs that never include record contents, and enforced deletion once the retention period ends.

Follow-up

  • An analyst must join one patient's records across two sources without seeing who the patient is. How do you allow that?
  • How would you show an auditor exactly who accessed a specific record last month?
  • A downstream team asks for raw data to debug a model. What do you give them instead?

Design and code a custom data structure that supports get, set, setall, and delete operations in $O(1)

Medium · Data Structures & Algorithmic Problem So

Design and code a custom data structure that supports get, set, setall, and delete operations in $O(1)$ time complexity.

Approach

  1. Keep a hash map from key to (value, stamp) and a global (allValue, allStamp), with stamps drawn from a monotonically increasing counter. set writes the value with a fresh stamp. setAll records allValue with a fresh stamp in O(1), without touching any entry.
  2. get(key): if the key is absent, return null. If the entry's stamp is newer than allStamp, return its own value; otherwise return allValue. delete removes the key from the map, so a later get returns null, and a later set gets a stamp newer than allStamp.
  3. Settle semantics up front: setAll applies only to keys that exist (it does not create keys), and a key set after setAll keeps its own value. Test that exact sequence: set a, setAll, set b, get a, get b.
  4. Be precise about complexity: every operation is O(1) expected because of the hash map, not worst case. Use a 64-bit counter so the stamps cannot wrap.
  5. For thread safety, read allValue and allStamp together: store them as one immutable pair behind an atomic reference, or guard the structure with a lock, so a get cannot observe a new stamp paired with an old value.

Follow-up

  • Add incrementAll(delta) in O(1) as well. What do you store per entry?
  • How would you allow concurrent readers and writers without one global lock?
  • Do entries overwritten by setAll still use memory, and does that matter?

Design a File System Search Class that supports filtering files hierarchically based on attributes like size,

Medium · Low-Level Design & Object-Oriented Archi

Design a File System Search Class that supports filtering files hierarchically based on attributes like size, file extension, and creation timestamp.

Approach

  1. Model the tree with the Composite pattern: an abstract Node (name, parent), a File with size, extension and createdAt, and a Directory holding its children.
  2. Make filters pluggable: a Filter interface with matches(File), concrete SizeRangeFilter, ExtensionFilter and CreatedAfterFilter, and AndFilter, OrFilter and NotFilter combinators (specification pattern). New criteria then plug in without changing the search code.
  3. Search with an iterative DFS using an explicit stack, to avoid recursion limits on deep trees. Support options for max depth and non-recursive search, and return a lazy iterator so huge trees do not build one big list. Each search visits O(N) nodes.
  4. Edge cases: symlinks that create cycles (track visited real paths), unreadable directories (skip them and report), extension case sensitivity, files with no extension, and names with several dots such as archive.tar.gz.
  5. If the same queries repeat over a large tree, add indexes: a hash map by extension and sorted structures by size and timestamp for range queries. The cost is keeping those indexes current on every file change.

Follow-up

  • How would a caller register a brand-new filter at runtime without editing your classes?
  • How could a filter prune a whole directory subtree instead of testing every file in it?
  • What changes when the tree holds millions of files and the same query runs every few seconds?

Design a Crafting Recipe System class that accepts a target item and returns the full tree list of required le

Medium · Low-Level Design & Object-Oriented Archi

Design a Crafting Recipe System class that accepts a target item and returns the full tree list of required leaf ingredients.

Approach

  1. Classes: Item, Recipe (output item, output quantity, and a map from input item to quantity), and a RecipeBook mapping each item to its recipe. A leaf is any item with no recipe. Return both the nested tree, for display, and an aggregated map from leaf to total quantity.
  2. Expansion: DFS from the target, multiplying quantities down each edge. When a recipe yields more than one unit, crafts needed = ceil(required / outputQty). Rounding on each branch separately overcounts, so total the demand for every intermediate item first by processing items in topological order (Kahn's algorithm), and expand each item once.
  3. Detect cycles with a three-colour DFS (an edge to a grey node means a cycle) and raise an error that names the cycle path, rather than recursing forever.
  4. Complexity: the expanded tree can be exponential in size when sub-recipes are shared. Aggregation with memoisation or a topological pass is O(V + E), so say which output the caller really needs.
  5. Clarify the extensions: several recipes for the same item (then it becomes an optimisation problem), items missing from the book, and stock already in inventory that reduces demand.

Follow-up

  • An item has two alternative recipes. How do you choose the one that minimises total raw materials?
  • How do you subtract what the player already holds, including intermediate items?
  • Two branches each need one plank, and one log yields four planks. Show how your algorithm avoids asking for two logs.

Architect a Dependency Manager Library capable of detecting circular build dependencies across software module

Medium · Low-Level Design & Object-Oriented Archi

Architect a Dependency Manager Library capable of detecting circular build dependencies across software modules.

Approach

  1. API: addModule, addDependency(from, to), buildOrder() and findCycles(), over an adjacency-list graph. A custom CircularDependencyException carries the cycle as an ordered list of modules.
  2. Detection: a three-colour DFS, where an edge to a grey node is a back edge, and the cycle is rebuilt from the recursion stack. O(V + E). Use an iterative DFS so deep graphs cannot overflow the call stack.
  3. Build order: topological sort with dependencies before dependents, either reverse DFS postorder or Kahn's algorithm. Kahn's levels double as batches that can build in parallel. If Kahn's algorithm stops with nodes left over, a cycle exists, but it does not give you the path.
  4. To report every cycle group, not just the first, run Tarjan's SCC algorithm (O(V + E)). Every component with more than one module, or with a self-loop, is a cycle the user has to break.
  5. Settle scope: exact versions versus version ranges. Choosing versions under range constraints is NP-complete in general, which makes it a separate solver problem from cycle detection. For incremental use, check whether from is reachable from to before accepting a new edge.

Follow-up

  • How do you report every circular group in one run instead of failing on the first?
  • How would you turn the build order into parallel batches, and how many workers could they use?
  • What changes once modules declare version ranges instead of exact versions?

Create an object-oriented design for a multi-story Parking Lot Management System with real-time spot allocatio

Medium · Low-Level Design & Object-Oriented Archi

Create an object-oriented design for a multi-story Parking Lot Management System with real-time spot allocation and vehicle tracking.

Approach

  1. Clarify: number of levels, spot types (motorcycle, compact, large, and possibly EV), which vehicle fits which spot, multiple entry and exit gates, ticketing and payment, and live free-spot displays.
  2. Classes: ParkingLot, Level, ParkingSpot (id, type, level, occupied state), a VehicleType enum or a Vehicle hierarchy, Ticket (id, plate, spot, entry time), and Gate. Put a SpotAllocationStrategy (nearest to the entrance, lowest level first) and a PricingStrategy behind interfaces (Strategy pattern).
  3. Allocation: per level and spot type, keep a free pool, either a min-heap keyed by distance (O(log n) per allocation) or a queue when any spot will do (O(1)). A map from plate to ticket finds a vehicle in O(1). Fall back to a larger spot type when the exact type is full.
  4. Concurrency: two gates must not get the same spot. Take the spot from the pool under a lock per pool, or flip its state with an atomic compare-and-set. Make exit idempotent so a repeated exit scan does not free a spot twice.
  5. Real-time tracking: keep free counts as counters updated on allocation and release, and push changes to display boards with the Observer pattern. Persist tickets and spot state so a restart can rebuild the pools.

Follow-up

  • How would you add EV charging spots with their own rate without changing the existing allocation code?
  • Two cars reach different gates for the last compact spot at the same moment. What guarantees only one of them gets it?
  • A driver loses the ticket. How does your model find the car and price the stay?

Design the domain model and core engine logic for a multi-player Battleship Game including state persistence a

Medium · Low-Level Design & Object-Oriented Archi

Design the domain model and core engine logic for a multi-player Battleship Game including state persistence and turn validation.

Approach

  1. Domain: Game (id, players, state, turn order), Player, Board (size, ships, shots received), Ship (cells, hit count), Coordinate, and a ShotResult enum (MISS, HIT, SUNK, GAME_OVER). The game state machine runs SETUP, IN_PROGRESS, FINISHED.
  2. Placement validation: ships in bounds, in a straight line, not overlapping, plus any adjacency rule. Store a map from coordinate to ship so a shot resolves in O(1). A ship sinks when its hits reach its length, and a player is out when a remaining-ships counter reaches zero, with no board scan.
  3. Turn validation, in order: the game is IN_PROGRESS, it is this player's turn, the target is a live opponent, the coordinate is in bounds, and the cell has not already been fired at. Only then apply the shot and advance the turn. With more than two players, keep a turn list and skip eliminated players.
  4. Persistence: either an append-only log of moves (event sourcing, which allows replay and crash recovery) or snapshots with a version number. Use optimistic concurrency on that version, plus an idempotency key per move, to reject duplicate or out-of-order submissions.
  5. Hidden information: the server is authoritative. It never sends opponents' ship positions to a client, only shot results, so a modified client cannot see the board.

Follow-up

  • A player submits two moves at once from two devices. What stops the second one?
  • How do you rebuild a game exactly after a server crash in the middle of a match?
  • What changes when three or more players can fire at any opponent?

Listing latency scales with page size, not with filters

Easy · n+1 · latency · query batching

The tenant listing endpoint reads resource filtered by tenant_id and status, ordered by updated_at DESC, and returns each row plus the owner's display name from app_user and the actor of that resource's latest resource_revision. p99 is 55 ms at 10 rows per page and 1.4 s at 200. Database telemetry shows 401 statements per request, each under 1 ms, and nothing in the slow-query log. Diagnose the cause and give the fix, stating the statement count per request and the p99 you expect afterwards.

Approach

  1. Read the counters before forming a theory. 401 statements for 200 rows is one driver query plus two per row, and sub-millisecond execution with an empty slow-query log rules out a bad plan. The time is round trips, which is why it is invisible in every per-query metric and scales with rows returned rather than with filter selectivity.
  2. Name the two per-row statements from their normalised text: a single-row app_user lookup by user_id, and a resource_revision lookup by resource_id ordered by version DESC LIMIT 1. Confirm by dropping those two response fields and watching the statement count fall to one. That locates the calls in the serialisation layer, not the repository.
  3. Check that the arithmetic accounts for the whole gap. Measure one round trip to the replica in isolation; 400 trips at roughly 3 ms of network plus 0.2 ms of execution is about 1.3 s on top of a 55 ms baseline, which matches. If the multiplication had fallen short, the N+1 would only be part of the story and you would keep looking.
  4. Batch both lookups. Collect owner_user_ids and resource_ids from the driver query, then issue WHERE tenant_id = $1 AND user_id = ANY($2) for the users, and PostgreSQL's SELECT DISTINCT ON (resource_id) ... WHERE resource_id = ANY($2) ORDER BY resource_id, version DESC for the latest revision, which the UNIQUE (resource_id, version) index serves directly. On an engine without DISTINCT ON, use a lateral join or a row_number window. Three statements per request at any page size.

Follow-up

  • The page size is capped at 200 today. What breaks first if it is raised to 2,000, and is it still this bug?
  • How do you stop the next N+1 from reaching production, given that no individual query is slow and the endpoint's tests pass?

Suggested practice checklist

Built around the reported coding, design and low-level design questions and the Leadership Principles prompts, in the order a phone screen, loop and Bar Raiser would reach them. Each day ends with something you can hand to a friend for a mock.

Day 1 — Stories and the loop map

  • Write out the five reported stages and mark which question types each one most likely needs.
  • Choose five projects from your resume and write a STAR outline (first person, with numbers) for the disagreement, ambiguity and ownership prompts.
  • Note the Leadership Principles candidates cite and which story covers each, with gaps marked.

Deliverable: A one-page loop map plus three written STAR outlines for the disagreement, ambiguity and ownership prompts, each with at least one metric.

Day 2 — Constant-time structures and caches

  • Code the get, set, setall and delete structure in your chosen language, in a plain editor, with a version counter, and test setall followed by set.
  • Code the LRU cache with a hash map and a doubly linked list, then add a lock and write down what it protects.
  • Write down the cost of each operation and one edge case per structure (capacity of one, deleting a missing key).

Deliverable: Two working solutions (setall structure and LRU cache) with a complexity table and a list of tested edge cases.

Day 3 — Grids, streams and array cost problems

  • Solve the locker placement problem with a BFS from each house over the grid, summing distances and skipping obstacles; state the complexity in terms of houses and cells.
  • Write the streaming parenthesis check with a stack, then change it to accept a configurable map of opening to closing delimiters.
  • Work the system-cost array problem: state the brute force, then the improvement, then the complexity.

Deliverable: Three solutions with stated complexity, the delimiter-configurable version of the parenthesis checker, and a handwritten trace of one test case for each.

Day 4 — Service design: availability, scheduling, streaming

  • Outline the EC2 pool retrieval system: data model per zone, read-heavy path, caching, and what staleness you accept.
  • Outline the task scheduler: job table, dependency graph, retry counters, worker leasing, and what an SLA breach looks like on a dashboard.
  • Outline the CSV merge: streaming read, k-way merge by key or sorted runs on disk, and the memory bound.

Deliverable: Three one-page design outlines (requirements, numbers, data model, API, failure modes) that you can speak through.

Day 5 — Hierarchies, secure pipelines and failure analysis

  • Outline the org chart system: storage for parent links, queries for subtrees, how re-parenting works, and how you keep reads fast.
  • Outline the healthcare pipeline: encryption in transit and at rest, access control, audit logging, and how data moves between stages.
  • Work through the listing-latency debugging practice problem and write the investigation as a sequence of hypotheses and checks.

Deliverable: Two design outlines (org chart and healthcare pipeline) and a written debugging sequence you could reuse in the incident story.

Day 6 — Low-level design set

  • Design file system search with a filter interface and composable AND and OR filters; code the interface and two filters.
  • Design crafting recipes (expand a recipe tree to leaf ingredients) and the dependency manager (cycle detection with a three-state DFS); share the traversal where it fits.
  • Model the parking lot and Battleship: classes, responsibilities, and where state persistence and turn validation live.

Deliverable: Class diagrams and key method signatures for five designs, plus working code for the filter interface and cycle detector.

Day 7 — Full mock and Bar Raiser stories

  • Write the deadline-compromise and production-incident stories in full, using a post-mortem structure for the incident.
  • Run a mock with a friend: one design prompt, one coding problem and two stories, in a plain editor with no autocomplete.
  • Retell each story to someone outside your field and remove anything that needs team context to understand.

Deliverable: Five finished STAR stories (all five behavioral prompts) and notes from one full mock with the weakest section marked for review.

Leadership Principles stories

Candidates report behavioral questions in every round, framed around Leadership Principles and answered in STAR form with measurable results. The reported prompts cover disagreement, ambiguity, ownership, deadline compromise and incident response, and the role itself includes on-call and post-mortems. Prepare stories where you personally decided and acted, quantify each one, and build several that can serve more than one principle.

  1. Describe a scenario where you had to push back against a product manager's feature request due to technical debt or operational risk.

Describe a situation where you strongly disagreed with your manager or team lead regarding an architectural de

Medium · Behavioral & Leadership Principles

Describe a situation where you strongly disagreed with your manager or team lead regarding an architectural decision. How did you handle the situation and arrive at a consensus? (Disagree and Commit)

Approach

  1. Choose a disagreement about a real architectural choice, such as synchronous calls versus a queue, or one shared database versus a per-service store, where you had a position and evidence for it.
  2. Lay out the two options in neutral terms, then your evidence: a benchmark, a failure scenario, a cost estimate or a customer commitment. Show you raised it openly, ideally in a design review or a written comment, rather than around your manager.
  3. Give the resolution honestly. If the group chose your option, say what you did to make it safe. If it chose the other, say that you committed fully and what you did to make that path succeed.
  4. Close with the result and what you changed afterward, such as bringing data to the review earlier or agreeing on a revisit date with a metric.

Follow-up

  • What would have changed your mind during the debate?
  • What happened if the decision was wrong, and how did you handle it?
  • How did your working relationship with that person change afterward?

Tell me about a time when you were faced with extreme ambiguity in a project. How did you gather data, define

Medium · Behavioral & Leadership Principles

Tell me about a time when you were faced with extreme ambiguity in a project. How did you gather data, define requirements, and deliver results? (Dive Deep / Bias for Action)

Approach

  1. Pick a project that started with an unclear goal and name what was unknown: who the customer was, what scale to expect, what success meant.
  2. Show your method: the questions you asked, the data you pulled (logs, usage metrics, customer conversations) and the assumptions you wrote down and had someone check.
  3. Describe the first small, reversible step you took to avoid waiting for certainty, such as a prototype or a limited release, and how it informed the next step.
  4. Finish with requirements you ended up with, what you shipped and a measured outcome, and name an assumption that turned out to be wrong.

Follow-up

  • How did you decide you had enough information to start?
  • Which assumption was wrong, and what did it cost you?
  • What did you cut or postpone, and who agreed to that?

Give an example of a project where you took calculated risks or took initiative outside your explicit job scop

Medium · Behavioral & Leadership Principles

Give an example of a project where you took calculated risks or took initiative outside your explicit job scope to solve a critical customer pain point. (Ownership / Customer Obsession)

Approach

  1. Name the customer pain precisely and say how you knew it was real: support tickets, error rates, a customer escalation or your own measurements. The customer can be internal, but it must be a specific group of people.
  2. State why the work was outside your assigned scope and what you did to avoid damaging others: informing the owning team, using a feature flag or canary, agreeing on rollback.
  3. Say what the risk was and how you sized it before acting; calculated means you can state the downside and the safeguard you put in place.
  4. Report the outcome with numbers and what happened to the work afterward, such as handing it to the owning team with documentation and alarms.

Follow-up

  • Who owned that area, and how did you get them on board?
  • What would you have done if it had failed?
  • What did you give up to make time for it?

Describe a time when you had to make a technical sacrifice or architectural compromise to meet a critical deli

Medium · Behavioral & Leadership Principles

Describe a time when you had to make a technical sacrifice or architectural compromise to meet a critical delivery deadline. (Deliver Results)

Approach

  1. Choose a real compromise with a real deadline, and state exactly what you cut: scope, test depth, a consistency guarantee, an automated step or a cleaner data model.
  2. Explain how you chose which corner was safe to cut, such as the one with the smallest customer impact or the easiest to reverse, and who you consulted.
  3. Show how you bounded the debt: a documented ticket, an alarm or guardrail, a named owner and a date for the follow-up work.
  4. Report whether the deadline was met, the customer outcome, and when and how the debt was repaid. A story where the shortcut quietly stayed forever weakens the answer.

Follow-up

  • What was the alternative you rejected and why?
  • Did the compromise cause any incident or rework?
  • How do you decide now when a deadline justifies a shortcut?

Tell me about a system failure or production incident you caused or managed. What was your immediate response,

Medium · Behavioral & Leadership Principles

Tell me about a system failure or production incident you caused or managed. What was your immediate response, and how did you prevent recurrence? (Insist on the Highest Standards)

Approach

  1. Give the timeline in order: how the problem was detected (an alarm, a customer report), the first mitigation (rollback, failover, scaling, disabling a feature), and only then the diagnosis. Mitigating before explaining is the sequence to show.
  2. State your role plainly. If you caused it, say so without blame shifting, and describe how you communicated to affected people during the incident.
  3. Explain the root cause with specifics, such as a missing validation, an unbounded retry or a config change that skipped canary, and write it as in a Correction of Errors post-mortem: what happened, why it was not caught, what changes.
  4. List the concrete fixes and how you verified them: a new alarm, a regression test, a deployment guardrail, a runbook. 'Be more careful' is not a fix. End with measurable change, such as shorter detection time or no recurrence.

Follow-up

  • Why did existing monitoring not catch it sooner?
  • How did you confirm the fix worked and that nothing else regressed?
  • What would you do differently in the first ten minutes next time?