cswork / libraryOpen practice workspace

Interview guides · 6thStreet

6thStreet Software Engineer Interview Guide

6thStreet Software Engineer interview guide: worked answers to reported tree, linked-list and array questions, design and managerial prep, and a 7-day plan.

The 60-second brief

6thStreet runs an online retail platform. The role description says a significant part of the Software Engineer role is backend service work: building services and APIs that carry retail logic such as inventory management and transaction flows, keeping those systems stable through high-traffic shopping events, and troubleshooting production issues when they appear. It also lists design discussions, code reviews, and turning business requirements from cross-functional partners into technical specifications. The languages named are object-oriented ones (Java, C++ and Python are given as examples), and microservices, cloud infrastructure, high-traffic distributed systems and prior e-commerce work appear as nice-to-have experience.

This guide is for candidates interviewing for a Software Engineer position at 6thStreet who code in an object-oriented language such as Java, C++ or Python. It covers the data structures and algorithms questions candidates report (level-order traversal, lowest common ancestor, linked-list cycle detection and reversal, maximum subarray sum, search optimisation, hash map versus tree map), the habits candidates are advised to build alongside the code (explaining the approach first, stating complexity, handling edge cases, writing without an IDE), the HLD and LLD discussions reported for mid-level and senior candidates, and the managerial and project deep-dive conversations.

Candidates report multiple rounds over a multi-week process, including coding and system design (high-level and low-level design, mainly for mid-level and senior candidates), a managerial round and deep-dive questions on past projects. The number and order of rounds have not been reported.

Focus areas

  • Data Structures and Algorithms (DSA)
  • Problem Solving
  • Communication Skills

Overview

6thStreet is an e-commerce business, and the role description is backend-heavy: services and APIs for inventory, transactions and other retail logic that have to hold up when traffic spikes during sales, plus production troubleshooting and code review. That context is useful when a design question or a project deep-dive turns to concrete trade-offs, such as how a stock count stays correct under concurrent orders or how a product lookup stays fast as the catalogue grows.

The reported questions are classic data structures and algorithms problems rather than puzzles: printing a binary tree level by level, finding the lowest common ancestor, detecting a cycle in a linked list, reversing a linked list in place, finding the maximum-sum subarray, speeding up search over a large dataset, and comparing a hash map with a tree-based map. Trees and linked lists are listed among the topics to prepare, and candidates are advised to be ready to state the Big O of each solution. Candidates rate the difficulty as average, which shifts the weight onto correctness, edge cases and a clear explanation rather than obscure tricks.

Two working conditions shape preparation. Candidates are told to expect writing code without an IDE, on a whiteboard or in a plain editor, so you need to catch your own null-pointer and off-by-one bugs by dry-running. They are also advised to treat communication as part of the answer: explaining the approach before writing anything, and asking clarifying questions when a prompt is vague. Practise every reported question out loud in a plain text editor, not in an environment with autocomplete and a run button.

Beyond coding, candidates report HLD and LLD discussions for mid-level and senior roles, and OOP principles are listed among the must-have skills, so expect class design (responsibilities, interfaces, composition over inheritance) as well as service-level design. They also report a managerial round, where the advice is to focus on conflict and collaboration, and detailed questions about past projects in which every technical decision needs a reason. Prepare two or three projects you can defend line by line, and pick one language and know its collections and pitfalls well.

Where to focus your preparation

No round order or round count has been reported for 6thStreet, so this guide groups everything candidates describe (coding, design, project deep-dives and the managerial round) into one preparation stage. Ask your recruiter for the sequence once you are in process.

Preparation focus

Candidates report multiple rounds over a multi-week process, including coding and system design, plus a managerial round and deep-dive questions about projects on the resume; the order and number of stages are not reported. The coding questions candidates report are standard data structures and algorithms problems on trees, linked lists, arrays and maps, with candidates told to expect writing code without an IDE and to be ready to state the complexity of each solution. Explaining the approach before coding and asking clarifying questions on vague prompts are also advised. For mid-level and senior candidates, HLD and LLD discussions are reported. Approach this stage by working every reported coding question in a plain editor while speaking, then preparing one low-level class design and one service-level design in an e-commerce setting, and finally rehearsing project and conflict stories for the managerial conversation.

What to demonstrate

  • Preparation target: working, bug-free code for tree, linked-list and array problems written without IDE support
  • Preparation target: stating time and space complexity and justifying the choice of data structure
  • Preparation target: explaining the approach and clarifying requirements before writing code
  • For mid-level and senior candidates, reported HLD and LLD discussions, plus project and collaboration stories for the managerial round

How to prepare

  • Write level-order traversal, lowest common ancestor, Floyd cycle detection, in-place list reversal and Kadane's algorithm from memory in a plain editor, then dry-run each on an empty input, a single element and one adversarial case
  • Prepare a one-minute comparison of a hash map and a tree map in your chosen language (Java HashMap vs TreeMap, C++ unordered_map vs map, or Python dict vs a sorted structure), including worst cases and ordered operations
  • Sketch one LLD (a shopping cart with pricing rules) and one HLD (an inventory reservation service under sale-day traffic). Then drill the OOP concepts aloud: encapsulation, polymorphism, overloading vs overriding, abstract class vs interface, and SOLID applied to the cart design
  • Pick two past projects and write down every significant technical decision, the alternative you rejected and why, plus one conflict story for the managerial round

Where candidates lose points

Linked-list or tree code that crashes on a null pointer or loses the rest of the list, with no IDE to catch it

Practise every reported linked-list and tree question in a plain text editor. Before declaring the code done, dry-run it on an empty input, one node and a three-node case, tracking each pointer in a small table. In cycle detection, check fast and fast.next before advancing; in reversal, save next before overwriting curr.next.

The textbook algorithm without its edge cases: Kadane's starting at 0, an LCA that assumes both nodes exist, binary search that loops forever

For each reported algorithm, write down the one edge case that breaks the naive version and test it out loud: an all-negative array for maximum subarray, a missing node for LCA, a two-element array and duplicate keys for binary search, a self-loop for cycle detection. Mention the case before you are asked.

Answering the hash map versus tree map question with only 'O(1) versus O(log n)'

Cover the average and worst cases for hashing (collisions, resizing, treeified buckets in Java), what only a tree map can do (sorted iteration, floor and ceiling, range views), the key contracts (hashCode/equals versus a comparator) and memory overhead. Then give a concrete case for each, such as a SKU lookup versus a price-range query.

Loose or incomplete complexity: ignoring the recursion stack, calling an O(n * m) loop O(n^2), or quoting average cases as guarantees

Candidates are advised to be ready to state the Big O of each solution, so state time and space separately on every practice problem, name the variables, include stack depth for recursion, and label each bound as worst case, average or amortised.

Being unable to explain decisions in your own projects during the deep-dive or the managerial round

For two projects, write down each major choice (datastore, data structure, service boundary, library), the alternative you rejected, the reason, and what you would change now. Prepare one specific conflict story and one collaboration story, each with a clear decision you made and a measurable outcome, rather than a general claim about teamwork.

Questions & practice

How would you traverse a binary tree and print nodes level by level?

Medium · Data Structures and Algorithms

Approach

  1. Use breadth-first search with a queue. At the start of each level, record the queue's current size k, then pop exactly k nodes, printing each and enqueuing its non-null children; that boundary is what separates the levels.
  2. Complexity is O(n) time, since every node is enqueued and dequeued once, and O(w) extra space, where w is the maximum width of the tree, which can be about n/2 for the last level of a complete tree.
  3. Mention the depth-first alternative: a recursive preorder that carries the depth and appends each node to result[depth]. It produces the same grouping using O(h) stack space, but a skewed tree makes h = n, which can overflow the stack (Python's default recursion limit is about 1000).
  4. Edge cases: a null root (print nothing and return an empty list), a single node, and a completely skewed tree. Confirm the output format first: one line per level, or a list of lists.

Follow-up

  • Print the levels in zigzag order, alternating left-to-right and right-to-left.
  • Return only the rightmost node of each level (the right-side view).
  • Print the levels bottom-up, or handle an n-ary tree instead of a binary one.

Can you explain and implement a solution for detecting a cycle in a linked list?

Medium · Data Structures and Algorithms

Approach

  1. Mention the simple version first: walk the list storing visited node references in a hash set and report a cycle when a node repeats. That is O(n) time and O(n) space.
  2. Then implement Floyd's tortoise and hare: slow moves one step and fast moves two. If fast or fast.next becomes null there is no cycle; if the two pointers meet, there is one. That is O(n) time and O(1) space.
  3. To find where the cycle starts, reset one pointer to the head and advance both one step at a time; they meet at the entry. Reason: if the head is a steps from the entry and the meeting point is b steps past it, then a + b is a multiple of the cycle length L, so a = kL - b.
  4. Edge cases: an empty list, a single node with no next, a single node pointing to itself, and a cycle that starts at the head. Check fast != null && fast.next != null before advancing so you never dereference null.

Follow-up

  • Return the node where the cycle begins, and prove why the reset trick works.
  • Compute the length of the cycle.
  • Find a duplicate in an array of n+1 values in the range 1..n without modifying it, using the same technique.

Given an array of integers, how would you find the subarray with the largest sum?

Medium · Data Structures and Algorithms

Approach

  1. Brute force: check every (i, j) pair with a running or prefix sum, which is O(n^2). Name it, then improve on it.
  2. Kadane's algorithm: keep cur = max(x, cur + x) and best = max(best, cur) in a single pass. A negative running sum can never help a later subarray, so you restart at x. That is O(n) time and O(1) space.
  3. Initialise cur and best to the first element, not to 0, or an all-negative array wrongly returns 0. Confirm whether an empty subarray is allowed, because that changes the answer.
  4. To return the subarray itself, record a tentative start whenever cur restarts at x, and copy it with the current index whenever best improves.

Follow-up

  • Return the start and end indices, not only the sum.
  • Solve it for a circular array: take the larger of normal Kadane and the total minus the minimum subarray, unless every element is negative.
  • Find the maximum product subarray instead, which means tracking both the running maximum and the running minimum.

How do you optimize the time complexity of a search algorithm in a large dataset?

Medium · Data Structures and Algorithms

Approach

  1. Start with the access pattern. A single lookup in unsorted data is O(n) and cannot be beaten. Repeated lookups justify preprocessing, so ask how many queries there are, how often the data changes and whether you need exact matches, ranges or prefixes.
  2. Exact-key lookups: build a hash index for O(1) average per query after O(n) setup. Range or ordered queries: sort once in O(n log n) and binary search in O(log n), or use a balanced BST or B-tree when the data keeps changing (a sorted array insert is O(n); a tree insert is O(log n)).
  3. Prefix or autocomplete search over product names calls for a trie; free-text search calls for an inverted index from term to document ids. Data that does not fit in memory needs a disk-oriented index such as a B-tree, or partitioning the data across machines.
  4. Get binary search exactly right: compute mid as lo + (hi - lo) / 2, decide whether you want the first match (lower bound) when there are duplicates, and test arrays of size 0, 1 and 2.

Follow-up

  • The dataset receives thousands of updates a second. Which structure do you keep, and what does each update cost?
  • How would you support searches that tolerate typos?
  • The index no longer fits on one machine. How do you split it and route queries?

Can you explain the difference between a hash map and a tree-based map in terms of performance?

Medium · Data Structures and Algorithms

Approach

  1. A hash map gives O(1) average get, put and remove, but O(n) worst case when keys collide. Resizing is O(n) but amortised O(1) per insert. It keeps no key order. In Java 8+, a bucket with more than 8 entries (once the table has at least 64 slots) becomes a red-black tree, which caps the bucket at O(log n) only when the colliding keys are Comparable; non-Comparable keys sharing one hash can still cost O(n) to search in a tree bin.
  2. A tree map (Java TreeMap, C++ std::map) is a red-black tree with guaranteed O(log n) operations, keys iterated in sorted order, and ordered queries such as floorKey, ceilingKey, headMap and subMap that a hash map cannot answer.
  3. The contracts differ. A hash map needs consistent equals and hashCode (or hash and operator==), and keys must not change while they are stored. A tree map needs a comparator that is consistent with equality. Mention memory as well: tree nodes carry child pointers and a colour, and hash tables keep spare capacity (Java's default load factor is 0.75).
  4. Choose by the operations you need. Exact lookups, such as SKU to stock count, suit a hash map. Range queries or sorted output, such as products within a price band or the next available delivery slot, suit a tree map. In Python, dict preserves insertion order, not sorted order; use bisect on a sorted list or a sorted container for tree-map behaviour.

Follow-up

  • Design an LRU cache with O(1) get and put, using a hash map and a doubly linked list.
  • What happens if an attacker chooses keys that all collide, and how do runtimes defend against it?
  • What breaks if you mutate a key after inserting it into each map?

This includes your mastery of data structures, algorithms, and language-specific nuances.

Medium · Getting Ready for Your Interviews

Approach

  1. Pick one language from those the role lists (Java, C++ or Python) and know its standard collections cold: which one is backed by an array, which by a hash table and which by a tree, and the cost of each operation.
  2. Java pitfalls to know: comparing Integer objects with == (values -128 to 127 are cached, so it only works sometimes), String immutability and when to use StringBuilder, the equals/hashCode contract, ArrayList versus LinkedList, and generics type erasure.
  3. C++ pitfalls to know: references versus pointers, RAII and unique_ptr/shared_ptr instead of raw new/delete, iterator invalidation after vector::push_back, and the fact that std::sort (introsort) is not stable while std::stable_sort is.
  4. Python pitfalls to know: mutable default arguments, shallow versus deep copies (including [[0]*n]*m sharing rows), the default recursion limit of about 1000 for deep tree recursion, and the stable Timsort behind sorted() and list.sort().

Follow-up

  • Is your language pass-by-value or pass-by-reference, and what does that mean when a function modifies a list argument?
  • Which sorting algorithm does your standard library use, and is it stable?
  • How does your language manage memory for the nodes a linked-list removal leaves behind?

Interviewers look for clean, bug-free code that demonstrates an understanding of time and space complexity.

Medium · Getting Ready for Your Interviews

Approach

  1. Candidates are told to expect coding without an IDE, so build the habit of writing the function signature, a one-line statement of the loop invariant, and small named helpers before filling in the body. Code structured this way is easier to check by eye.
  2. After writing, dry-run a small concrete input line by line, tracking every variable in a table. This catches off-by-one errors in index loops and binary search, and null dereferences in linked-list and tree code.
  3. Run a standard set of tests aloud: empty input, one element, two elements, duplicates, negatives or zero, and the largest size allowed. Fix bugs by reasoning about them rather than by trial edits.
  4. State time and space separately. Include the recursion stack (O(h) for tree recursion), say whether the output counts towards space, and say whether a bound is worst case or amortised.

Follow-up

  • What is the space complexity of your recursive version, and can you make it iterative?
  • Walk through your code on this input and say what each variable holds after the second iteration.
  • Which line would fail first if the input were empty?

You will be tested on how you break down ambiguous requirements into actionable technical tasks.

Medium · Getting Ready for Your Interviews

Approach

  1. Start with clarifying questions that reduce the ambiguity: who uses the feature, what the inputs and outputs are, the expected scale, what counts as success, and what is explicitly out of scope. Candidates are advised to ask these before starting to code.
  2. Write two or three concrete examples, including one edge case, and confirm them. In a coding question this pins down the function contract; in a feature question it becomes the acceptance criteria.
  3. Break the work into pieces along natural seams: data model, API contract, core logic, validation and failure handling, tests, rollout. Order them by dependency and by risk, building the riskiest or least understood piece first.
  4. Practise on an e-commerce example such as 'let customers apply a coupon at checkout': list the questions (stacking rules, expiry, per-user limits, behaviour when an item goes out of stock) and the resulting tasks, and state the assumptions you made where no answer was available.

Follow-up

  • Which task would you ship first, and why?
  • A stakeholder changes a requirement halfway through. How do you re-plan?
  • How would you estimate this work and communicate the uncertainty?

Trees and Graphs: Common data structure questions focused on traversal and manipulation.

Medium · Technical Rigor and DSA

Approach

  1. Know all the tree traversals in both recursive and iterative form: preorder, inorder and postorder using an explicit stack, and level order using a queue. An inorder traversal of a binary search tree visits the keys in sorted order, which solves many BST questions.
  2. For common tree manipulations, state the technique: invert or mirror by swapping children recursively; validate a BST by passing down (min, max) bounds, because checking each node only against its parent is wrong; serialise with preorder plus null markers.
  3. For graphs, keep a visited set, because unlike trees graphs have cycles. Traversal is O(V + E) with an adjacency list. BFS gives shortest paths in an unweighted graph. Detect a directed cycle with three-colour DFS, or order dependencies with Kahn's topological sort.
  4. Edge cases: an empty tree, a single node, a skewed tree (O(n) recursion depth), disconnected graph components (run the traversal from every unvisited node) and self-loops.

Follow-up

  • Validate that a binary tree is a BST, and explain why comparing each node with its parent is not enough.
  • Write an iterative inorder traversal without recursion.
  • Detect a cycle in a directed graph, such as a category hierarchy that must not loop.

Linked Lists: Frequently used for testing your ability to manage pointers and memory.

Medium · Technical Rigor and DSA

Approach

  1. Know the standard techniques: a dummy head node so that removing or inserting at the head needs no special case; fast and slow pointers to find the middle or detect a cycle; a gap of n between two pointers to remove the nth node from the end in one pass; in-place reversal with prev/curr/next; and merging two sorted lists.
  2. Draw boxes and arrows for every pointer change and save the next node before overwriting curr.next. Most linked-list bugs come from losing the rest of the list or dereferencing null.
  3. Memory: in C++, free removed nodes (or use unique_ptr) and never touch a node after deleting it. In Java and Python the garbage collector reclaims a node only once nothing references it, so clear stray references in long-lived structures.
  4. Test every solution on an empty list, one node, two nodes, and an operation on the head and on the tail.

Follow-up

  • Remove the nth node from the end in a single pass.
  • Merge k sorted lists: compare a min-heap at O(N log k) with merging pairs repeatedly.
  • Check whether a list is a palindrome using O(1) extra space.

Complexity Analysis: Always be ready to state the Big O complexity of your solutions.

Medium · Technical Rigor and DSA

Approach

  1. Count the dominant operation as a function of input size. Sequential blocks add and nested loops multiply. Use separate variables when there are two inputs (O(n + m) or O(n * m), not O(n^2)).
  2. For recursion, write the recurrence. T(n) = 2T(n/2) + O(n) gives O(n log n), as in merge sort. T(n) = T(n/2) + O(1) gives O(log n), as in binary search. Naive Fibonacci with two recursive calls is exponential, and memoising it makes it O(n).
  3. Distinguish worst, average and amortised cases. A hash map is O(1) on average but O(n) in the worst case. Appending to a dynamic array is O(1) amortised because the capacity doubles. Quicksort is O(n log n) on average and O(n^2) in the worst case.
  4. Space includes the recursion stack (O(h) for a tree, which is O(n) when the tree is skewed) and any auxiliary structures. Say whether the output itself is counted.

Follow-up

  • Why is building a binary heap O(n) rather than O(n log n)?
  • What is the amortised cost of appending to a dynamic array, and why?
  • Your solution is O(n log n). Is there a lower bound that says you cannot do better?

"Given a binary tree, how would you find the lowest common ancestor?"

Medium · Technical Rigor and DSA

Approach

  1. Recursive solution for a general binary tree: if the node is null or equals p or q, return it. Otherwise recurse into the left and right subtrees. If both calls return non-null, the current node is the LCA; if not, pass up whichever result is non-null. That is O(n) time and O(h) stack space.
  2. State the assumption that both nodes exist in the tree. If that is not guaranteed, return flags saying whether p and q were found and only report an LCA when both were.
  3. If the tree is a binary search tree, use the ordering instead: walk down from the root, going left when both values are smaller and right when both are larger. The first split point (or a node equal to one of the values) is the LCA, found in O(h) time and O(1) space iteratively.
  4. If nodes have parent pointers, compute both depths, lift the deeper node until the depths match, then move both up together until they meet. This is the same idea as finding the intersection of two linked lists.

Follow-up

  • Solve it for a binary search tree in O(h) time.
  • Handle the case where p or q might not be in the tree.
  • Answer many LCA queries on the same tree, using binary lifting with O(n log n) preprocessing and O(log n) per query.

"Implement a function to reverse a linked list in-place."

Medium · Technical Rigor and DSA

Approach

  1. Iterate with three references: prev = null and curr = head. In each step, save next = curr.next, set curr.next = prev, then advance with prev = curr and curr = next. When curr is null, return prev as the new head.
  2. Complexity is O(n) time and O(1) extra space. The recursive version (reverse the rest, then set head.next.next = head and head.next = null) is also O(n) but uses O(n) stack, which is risky for long lists.
  3. Edge cases: an empty list returns null, and a single node returns itself. Dry-run a three-node list aloud, because candidates are told to expect writing code without an IDE and the order of the pointer assignments is where bugs appear.
  4. If the list is doubly linked, swap prev and next on every node and return the last node visited.

Follow-up

  • Reverse the list in groups of k nodes.
  • Reverse only the sublist between positions m and n.
  • Write the recursive version and explain its stack cost.

Write the update path that detects a concurrent edit

Medium · optimistic concurrency · transactional outbox · audit history

resource carries version INT NOT NULL DEFAULT 1. resource_revision holds revision_id, resource_id, version, actor_user_id, change_kind, patch JSONB, request_id, created_at with UNIQUE (resource_id, version). outbox_event holds aggregate_type, aggregate_id, aggregate_version, event_type, payload, status. A PUT carries the version the client read. Write the exact statements for the single transaction that applies the edit, records the revision and enqueues 'resource.updated', and give the handler's branch on zero affected rows. Then say what PostgreSQL 16 does under READ COMMITTED when two of these updates hit one row at once.

Approach

  1. One transaction, three writes, no network call inside it: UPDATE resource SET title = $3, version = version + 1, updated_at = now() WHERE resource_id = $1 AND tenant_id = $4 AND version = $2; then INSERT the resource_revision row at version $2 + 1; then INSERT the outbox_event row at the same aggregate_version. The event goes to a table rather than a broker because no transaction spans both.
  2. Branch on the affected-row count before doing anything else. Zero has three causes — stale version, wrong tenant, row gone — so re-read once and map to 409 carrying the current version, or 404 for an id outside the caller's tenant, which also stops the endpoint confirming that another tenant's id exists.
  3. State the engine behaviour instead of assuming it. Under READ COMMITTED the second UPDATE blocks on the row lock, and when the first commits PostgreSQL re-evaluates the WHERE clause against the newly committed row, so the version predicate now fails and the statement reports zero rows. Under REPEATABLE READ the identical collision raises SQLSTATE 40001 instead, so the handler must fold both shapes into one conflict response.
  4. Keep UNIQUE (resource_id, version) even though the predicate already serialises writers. It is what makes a lost update unwritable if any other path ever reaches the revision table, and it converts a logic bug into 23505 rather than into a silently missing history row.

Follow-up

  • A client sends the version it read ten minutes ago and the resource has moved three versions. What is in your 409 so it can resolve the conflict without a full re-fetch?
  • Two editors, two disjoint fields, no overlap. Does your answer still refuse the second write, and should it?

Hold a per-tenant active cap against concurrent creates

Hard · write skew · isolation levels · constraints

A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.

Approach

  1. Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
  2. Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
  3. Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
  4. State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.

Follow-up

  • A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
  • The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?

Be prepared to discuss why you chose a specific data structure over an alternative.

Medium · Getting Ready for Your Interviews

Approach

  1. List the operations the problem needs and how often each runs (insert, delete, exact lookup, ordered iteration, min/max, range query), then compare the cost of each candidate structure for those operations. The structure that makes the most frequent operation cheap usually wins.
  2. Have standard pairings ready to state with numbers: top-k via a size-k heap at O(n log k) instead of a full sort at O(n log n); membership via a hash set; ordered or range access via a tree map; FIFO via a deque; an LRU cache via a hash map plus a doubly linked list.
  3. Mention constant factors and memory as well as Big O. Arrays have good cache locality while linked lists chase pointers. A hash set of a million small keys costs far more memory than a sorted array used with binary search.
  4. Practise this on your own projects too: for each data structure choice on your resume, say what you rejected and why, because candidates are told to expect deep-dive questions about justifying each technical decision.

Follow-up

  • Reads now outnumber writes a hundred to one. Does your choice change?
  • Several threads now update the structure. What do you swap in, such as ConcurrentHashMap or a lock?
  • Memory is capped at a tenth of what your structure needs. What do you give up?

Demonstrate your ability to think through edge cases and handle potential bottlenecks in your logic.

Medium · Getting Ready for Your Interviews

Approach

  1. Keep a fixed edge-case checklist and run it on every solution: null or empty input, a single element, all elements equal, negatives and zero, integer overflow on sums or products, maximum input size, self-references and cycles, and skewed trees that make recursion depth O(n).
  2. Find the bottleneck by locating the dominant term: nested loops over the same input, repeated recomputation (fix it with memoisation or a precomputed prefix sum), or a linear scan inside a loop (fix it with a hash map or a sort followed by binary search).
  3. At the service level, name the usual suspects in a retail backend: N+1 database calls when loading a cart, a hot key when one product gets heavy traffic during a sale, and lock contention when many orders decrement the same stock count.
  4. For each fix, state the trade-off you accept: extra memory for a cache or index, staleness for a cached value, or complexity for a sharded counter.

Follow-up

  • The input grows to a billion elements. Which part of your solution breaks first?
  • Which edge case did your solution miss, and how would a test have caught it?
  • Two requests decrement the same item's stock at the same moment. What goes wrong, and how do you prevent it?

Practice articulating your thought process out loud, as this is a key indicator of how you will collaborate in

Medium · Getting Ready for Your Interviews

Practice articulating your thought process out loud, as this is a key indicator of how you will collaborate in a team setting.

Approach

  1. Candidates are advised to explain the approach before implementing it, and to treat communication as part of the answer. Follow a fixed sequence: restate the problem, confirm the inputs, outputs and constraints, work one example, name the brute force and its complexity, then describe the improved approach and get agreement before writing code.
  2. While coding, narrate intent rather than syntax ('I am advancing fast two steps, so I check fast.next first'), and pause after each block to say what invariant now holds.
  3. When stuck, say what you are considering and why each idea fails ('a hash set works but costs O(n) space; I want O(1)'). Silence hides progress, and stating the failure out loud often points to the fix.
  4. Finish by testing aloud on an edge case and stating the final time and space complexity without being asked.

Follow-up

  • Why did you rule out the alternative approach you mentioned at the start?
  • If you had ten more minutes, what would you improve?
  • Explain your solution as you would to a new teammate reviewing the code.

Edge instances grow 400 MB per hour until the nightly restart

Hard · memory leak · heap analysis · metric cardinality

Edge API instances start at 700 MB resident and grow about 400 MB/hour; a nightly rolling restart has hidden it for weeks. Growth continues unchanged when request rate halves overnight, p99 degrades in the last hours before an instance is recycled, and heap used immediately after a forced full GC rises monotonically. The service holds no product state. Name the discriminating measurement that separates the plausible causes, give the most likely cause, and give the fix and how you would verify it.

Approach

  1. Separate resident memory from live heap first, because they fail differently. Resident size can grow from fragmentation, native buffers or thread stacks while the heap is flat; heap used after a full GC rising monotonically is the measurement that says objects are reachable and not being released. You already have it, so this is retention, not fragmentation, and that closes off half the candidate list.
  2. Use the rate's independence from traffic as the discriminator. Growth that continues at half the request rate rules out per-request objects that are merely slow to collect and points at a structure that grows with distinct values observed rather than with call volume. Write the candidates that have that property: a metrics registry keyed on a high-cardinality label, an unevicted cache, an interner, a per-key lock map.
  3. Take two heap snapshots an hour apart and diff by retained size, reading the dominator tree, not by allocation count or instance count. Expect one root holding a map with millions of entries, then follow the reference chain to the code that inserts and never removes. Allocation profilers point at churn, which is the wrong signal here.
  4. The candidate that fits this service is an observability label carrying an identifier, such as a request path recorded before templating so that /v1/resources/48213 becomes its own metric series. That grows with distinct ids seen, is independent of rate, and explains the late p99 degradation, since GC cost rises with the size of the live set.

Follow-up

  • Post-GC heap is now flat but resident size still creeps. What are you looking at, and does it matter?
  • How would you have detected this before an OOM, given the nightly restart masked the trend?

Suggested practice checklist

The plan works through the reported question groups in the order they build on each other: trees, linked lists, arrays and search, then maps and language details, then coding habits, then design, and finally the project and managerial conversations.

Day 1 — Trees: traversal and lowest common ancestor

  • In a plain editor without autocomplete, write level-order traversal using a queue and the level-size boundary, then the depth-first version that records the depth of each node
  • Write the recursive LCA for a general binary tree, the BST walk-down version, and the variant that handles missing nodes
  • Write iterative inorder traversal and BST validation with min/max bounds, and state the time and the stack space of each

Deliverable: Seven tree solutions (BFS level order, DFS level order, three LCA variants, iterative inorder and BST validation) written without an IDE, each with a dry run on an empty tree and a skewed tree and a written time and space complexity.

Day 2 — Linked lists: cycles, reversal and pointer handling

  • Write Floyd cycle detection, then extend it to return the cycle's start, and explain aloud why resetting one pointer to the head works
  • Write in-place reversal iteratively and recursively, then reverse the sublist between positions m and n
  • Write removal of the nth node from the end using a dummy head, and merge two sorted lists

Deliverable: Seven linked-list solutions (cycle detection, cycle start, iterative and recursive reversal, sublist reversal, nth-from-end removal and sorted merge), each dry-run on zero, one, two and three nodes, with the cycle-start proof written out in two or three lines.

Day 3 — Arrays, search and complexity

  • Write Kadane's algorithm returning both the sum and the indices, then the circular-array and maximum-product variants
  • Write lower-bound binary search and test it on arrays of size 0, 1 and 2 and on duplicate keys
  • Write a one-page comparison of hash index, sorted array, balanced tree, trie and inverted index for searching a large product catalogue, with the cost of building, querying and updating each

Deliverable: Three Kadane variants, a tested binary search and a one-page search-structure comparison with the complexity of every operation.

Day 4 — Maps, data-structure choice and your language

  • Write and rehearse a spoken comparison of hash map and tree map in your chosen language, covering collisions, ordered operations, key contracts and memory
  • Implement an LRU cache with a hash map and a doubly linked list, and state the cost of each operation
  • List ten pitfalls in your chosen language (Java, C++ or Python) with a two-line example of each

Deliverable: A rehearsed map comparison, a working LRU cache and a ten-item language pitfall sheet.

Day 5 — Coding habits: thinking aloud, edge cases and clean code

  • Re-solve two earlier problems while recording yourself and following the sequence: restate, example, brute force, optimise, confirm, code, test
  • Write a personal edge-case checklist and apply it to every solution from days 1 to 3, fixing any bug it finds
  • Rewrite one earlier solution for readability, with named helpers and the loop invariant stated in a comment

Deliverable: Two recorded mock answers with notes on where you went silent or skipped the explanation, an edge-case checklist, and one refactored solution.

Day 6 — Design: requirements, OOP, LLD and HLD in a retail setting

  • Take one vague feature request, such as applying coupons at checkout, and write the clarifying questions, the assumptions and the ordered task breakdown
  • Do an LLD for a shopping cart with pricing rules: classes, interfaces, where the rules live, and how a new discount type is added without editing existing classes. Answer aloud the OOP basics against it: encapsulation, polymorphism, overloading vs overriding, abstract class vs interface, and SOLID
  • Do an HLD for an inventory reservation service that must not oversell during a sale, then work the concurrent-update and per-tenant-cap practice problems
  • Work the practice problem on edge instances that grow 400 MB per hour, as a way to rehearse diagnosing memory growth in a service that runs through a sale day

Deliverable: A written requirements breakdown, a class diagram for the cart with a one-page OOP and SOLID answer sheet, a service diagram with a written answer on how concurrent stock decrements stay correct, and a short diagnosis write-up for the memory-growth problem.

Day 7 — Projects and the managerial round

  • For two resume projects, write every major technical decision, the rejected alternative and the reason
  • Prepare a conflict story and a collaboration story with a clear decision and outcome, and rehearse them aloud
  • Work the practice prompts on losing a design argument, breaking callers you do not own, and shipping under a deadline

Deliverable: A decision log for two projects, plus five rehearsed behavioural stories, each written as situation, action and result in a few lines.

Managerial round and project deep-dive stories

Candidates report a managerial round, where the advice is to focus on how you handle conflict and team collaboration, and deep-dive questions on resume projects in which each technical decision has to be justified. The role description also covers production troubleshooting, code review and turning business requirements into technical specifications, so prepare stories from that kind of work, each with a specific decision you made and its result.

  1. Tell me about a disagreement with a teammate or manager over a technical approach. What did each side argue, how was it resolved, and what happened afterwards?
  2. Pick a project on your resume and explain the most important technical decision in it, the alternative you rejected and whether the choice held up.
  3. Describe a production issue you helped troubleshoot. How did you narrow down the cause, what did you change, and what stopped it from recurring?
  4. Tell me about a time you received a vague business requirement. What questions did you ask, and how did you turn the answers into tasks?
  5. Describe a code review in which you pushed back on a change, or in which your own change was pushed back on. What did you learn?

Argue against a design, lose, and commit anyway

Medium · disagreement · service boundaries · decision records

Describe a design you argued against and lost. State the failure you predicted as a named mechanism, not a feeling about complexity: two services that would need one transaction, a projection with no rebuild path, a write path with no idempotency key. Say what evidence you brought, what the decision maker weighed instead, and what you did after the decision was made: what you instrumented, what you wrote down, and whether the prediction came true. Five minutes.

Approach

  1. State the prediction in falsifiable form up front: the mechanism, the condition that triggers it, and the observable outcome. A prediction that cannot be checked also cannot be credited to you later.
  2. Show the evidence you had at the time and label each piece honestly as measured, analogous, or intuition. Keeping the intuition is fine; disguising it as data is the thing that erodes your standing in the next argument.
  3. Represent the opposing case at full strength, including the constraint you did not control: a fixed date, a team boundary, or the fact that the decision was cheap to reverse and yours was not.
  4. Make disagree-and-commit concrete. Name the artefact you left behind so the prediction could be settled without you: the alert and its threshold, the counter on the dashboard, the decision note that recorded the trade-off and the condition that would revisit it.

Follow-up

  • What threshold on that alert would have proved you right, and did anyone ever look at it?
  • If the same proposal arrived tomorrow with the same deadline, would you argue it the same way?

Tell callers you do not own that their integration breaks

Medium · deprecation · compatibility · stakeholders

A field in a write endpoint's response must change shape. You own the endpoint; you do not own the four internal callers or the outbound webhook consumers who read it. Describe a deprecation you were responsible for: what you shipped first, how you established who was actually reading the field, the window you gave and what set its length, what you did about the consumer who never moved, and how you decided removal was safe. Name the signal you used, not the announcement you sent.

Approach

  1. Establish the reader set empirically rather than from a wiki of owners: per-field usage counters keyed by principal, or access logs attributed to a consumer. State the blind spot of whichever you pick, since a consumer that reads the field only on a monthly job will not appear in a week of logs.
  2. Ship additive first. Populate the new field alongside the old one so no reader is forced to move, which is also what keeps a rolling deploy safe, because old and new instances answer the same requests at the same time and a rollback must still find the old shape present.
  3. Set the window from the slowest legitimate consumer's release cadence, not from your calendar, and decide separately what to do for a consumer with no release process at all, such as an external webhook endpoint you can only email.
  4. Convert silence into evidence before you rely on it: a short, low-traffic removal window that makes a still-dependent consumer fail visibly and loudly while you are watching, rather than at three in the morning after you have moved on.

Follow-up

  • How would you detect a consumer that reads the field only during a monthly export?
  • One caller refuses to move and has a commercial relationship behind it. What changes in your plan and what does not?

Ship under a deadline and bound the debt you chose

Medium · pagination · technical debt · tradeoffs

You have four days to ship a tenant-facing listing endpoint. The version you would defend uses keyset pagination over (tenant_id, status, updated_at DESC, resource_id DESC); the version you can finish uses LIMIT/OFFSET with no matching index. Describe a deadline call you actually made of this shape: what you shipped, what you knowingly deferred, how you bounded the damage with a mechanism rather than an intention, and the specific numeric condition that would force the follow-up. Name who you told and where you wrote it down.

Approach

  1. Name the deferred failure precisely instead of calling it slow. OFFSET n makes the database produce and discard n rows, so cost grows with page depth; without an index matching the sort, every matching row is read and sorted before the limit applies; and rows inserted between two page fetches shift across the boundary so items are skipped or repeated with nothing in the response to signal it.
  2. Bound the blast radius with something mechanical rather than a promise: cap maximum page depth, cap page size, restrict the endpoint to one internal caller, or keep it behind a flag. State which failure each cap removes and which it leaves standing.
  3. Attach a number to the trigger and wire it to an alarm: the first tenant crossing N resources, or the endpoint's p99 crossing its share of the 400 ms budget, so the debt announces itself instead of waiting to be remembered.
  4. Write it where the next engineer looks, which is the code and the ticket, not a chat message: what was deferred, why, the cap, and the trigger.

Follow-up

  • At what page depth does the offset version breach your latency budget, given your page size and row counts?
  • What breaks first when you switch to keyset pagination later, and what does a client holding an old page token see?