This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.
Identity, Image, and Privacy Notice
To respect individual privacy, some names, profile photographs, avatars, biographical details, and other identifying information displayed in this guide may be replaced with pseudonyms, licensed stock images, illustrative avatars, composite images, or representative descriptions. Unless a person is expressly identified as an actual contributor, a displayed name, image, or profile should not be understood as depicting or identifying a specific candidate, interviewer, employee, or other real individual. These representations are provided for editorial and illustrative purposes only and do not imply endorsement, employment, participation, or affiliation with this guide or any company mentioned in it. Any resemblance to an actual person is coincidental.
Company Notice
This guide is an independent educational resource and is not affiliated with, endorsed by, sponsored by, or approved by the company named in this guide. Company names are used only to identify interview experiences commonly reported by candidates. Interview practices can change without notice, and inclusion of company-specific content does not mean these questions are official, complete, or guaranteed to be asked. To the fullest extent permitted by law, the author, contributors, and publisher are not responsible for outcomes related to use of this material.
Questions or comments?
Contact us for general questions, or share feedback, technical corrections, and comments with the community.
Given one missing value in a 0..n or 1..n sequence, explain how you recover the missing element without sorting and how you handle the smallest input sizes.
Short Interview Answer (30-60 seconds)
I use the arithmetic sum of the complete range. First I determine n from the input length: n equals the length for 0..n, and length plus one for 1..n. I compute n * (n + 1) / 2, sum the values that are present, and subtract the actual sum from the expected sum. The remaining value is the missing number. This takes O(n) time and O(1) auxiliary space.
The input contains all values from either 0..n or 1..n except one missing value. The goal is to return that missing value without sorting. The main idea is to compare the sum of the complete range with the sum of the values we actually received. The difference is the missing value. This works well because we only need a few numeric variables and one pass through the array.
Useful Questions to Ask the Interviewer
Will I be told whether the sequence is 0..n or 1..n?
Is exactly one value always missing, with all other expected values present once?
Can the input be empty for the smallest valid cases?
How to Explain It in an Interview
1. Understand the input and required output
The input is an integer array plus information telling us whether the complete sequence starts at 0 or 1. We must return the missing value itself, not an index. We do not need to sort the array.
2. Choose the sum-difference method
Let m be the array length. For a 0..n sequence, n = m. For a 1..n sequence, n = m +
In both cases, the complete range sum is n * (n + 1) /
The key idea is that subtracting every present value from this complete sum leaves only the missing value.
3. Initialize the state
For the diagram example, arr = [3, 0, 1]. The sequence is 0..n, so m = 3 and n = 3. The full range is 0, 1, 2, 3. Its expected sum is 6. We start actualSum at 0.
4. Walk through the example
At index 0, the value is 3. actualSum changes from 0 to 3.
At index 1, the value is 0. actualSum stays 3.
At index 2, the value is 1. actualSum changes from 3 to 4.
After all values are processed, missing = expectedSum - actualSum = 6 - 4 = 2. We return 2.
5. Explain why the result is correct
The expected sum contains every value from the complete range. The actual sum contains every value that is present in the input. All present values cancel when we subtract. Because exactly one value is missing, the remaining difference is exactly that missing value.
6. Explain complexity and edge cases
We process each array element once, so the time complexity is O(n). We use only a few numeric variables, so the auxiliary space is O(1). For the smallest 0..n case, an empty array means n = 0 and the missing value is
For 1..n, an empty array means n = 1 and the missing value is
For 0..n with arr = [0], n = 1 and the missing value is 1.
Key Insight / Why This Solution Works
Use the arithmetic sum of the full sequence and subtract the sum of the values that are present. If the array length is m, then the full-range maximum is n = m for 0..n and n = m + 1 for 1..n. The expected sum is n * (n + 1) / 2. The invariant is simple: after processing any prefix of the array, actualSum equals the sum of exactly the values processed so far. After the final element, expectedSum - actualSum is the one missing value. This avoids sorting and needs no extra collection.
Code
using System;
publicstaticclassProgram
{
publicstaticintFindMissingNumber(int[] arr, bool zeroBased)
{
// The array length is m. The full-range maximum depends on whether the sequence starts at 0// or 1.int m = arr.Length;
int n = zeroBased ? m : m + 1;
// Compute the sum of the complete range 0..n or 1..n.// Both ranges use n * (n + 1) / 2 because adding 0 does not change the sum.long expectedSum = (long)n * (n + 1) / 2;
// Accumulate only the values that are actually present in the input.long actualSum = 0;
foreach (int x in arr)
{
actualSum += x;
}
// All present values cancel, so the remaining difference is the missing value.return (int)(expectedSum - actualSum);
}
publicstaticvoidMain()
{
// Run the exact 0..n example from the diagram.int[] arr = { 3, 0, 1 };
int missing = FindMissingNumber(arr, true);
// The full range is 0, 1, 2, 3, so the missing value is 2.
Console.WriteLine(missing);
}
}
Time & Space Complexity
Time is O(n) because we add each array value to actualSum once. Auxiliary space is O(1) because the algorithm uses only m, n, expectedSum, actualSum, and the current loop value. The amount of extra memory does not grow with the input size. The implementation uses long for expectedSum and actualSum so the sum calculation does not overflow an int for large valid int-array inputs.
Where it is used
This pattern is useful when a known consecutive numeric range has exactly one missing value. It can appear in validation code, sequence checks, imported ID batches, or interview problems where sorting would add unnecessary O(n log n) work.
Why Interviewers Ask This
This question checks whether you recognize a simple arithmetic pattern instead of reaching for sorting or extra memory. The interviewer is also checking whether you define n correctly for both 0..n and 1..n, handle very small inputs, avoid integer-overflow mistakes in the sum calculation, write clear C#, and explain why subtracting the actual sum from the full-range sum leaves exactly the missing value.
Common interview mistakes
A common mistake is using n = arr.Length for both sequence types. For 1..n, n must be arr.Length + 1. Another mistake is using a different sum formula for 0..n even though 0 adds nothing. Candidates may also sort the array even though it is unnecessary, which changes the time complexity to O(n log n). Another error is using int for the running sums and risking overflow on larger inputs. Finally, do not confuse the missing value with a missing index.
Interview tip
Say the length rule first: n = m for 0..n, and n = m + 1 for 1..n. Then show expected sum minus actual sum. This makes the edge cases and the code easy to explain consistently.
Interviewer may ask next
What changes if the interviewer does not tell you whether the sequence is 0..n or 1..n?
The current method needs that contract because the array length maps to n differently in the two cases. I would ask for the range type or receive it as a parameter, as the diagram does with zeroBased. Once the range type is known, the same sum-difference algorithm stays correct. The time remains O(n) and the auxiliary space remains O(1).
How would you handle very large integer inputs safely?
Keep expectedSum and actualSum as long, and cast n to long before multiplication so the product is evaluated with 64-bit arithmetic. The algorithm itself does not change. It still scans the array once, so time is O(n), and it still uses O(1) auxiliary space. The tradeoff is only using a wider numeric type for safer arithmetic.
12. Equivalent IndexCodingEasyApple
i Question Details
Find an index where the sum on the left equals the sum on the right, and explain how a running-prefix approach avoids repeated scans.
Short Interview Answer (30-60 seconds)
I would first compute the total sum of the array. Then I keep a running left sum while moving from left to right. At each index, I calculate the right sum as totalSum - leftSum - nums[i]. If the two sums are equal, I return that index immediately. In this second pass, I process each item at most once and stop when the answer is found. The overall time complexity is O(n), and the auxiliary space complexity is O(1).
We need to find an index where the numbers before it add up to the same value as the numbers after it. The current number itself is not included on either side. We return the index, not the value stored there. Instead of adding the left and right sides again for every position, we first find the total sum. Then we keep the sum of everything already passed. This running left sum lets us calculate the right side immediately. For the example [-7, 1, 5, 2, -4, 3, 0], the first matching index is 3.
Useful Questions to Ask the Interviewer
Should I return -1 when no equivalent index exists?
If several equivalent indices exist, may I return the first one I find?
Can the array contain negative numbers and zero?
How to Explain It in an Interview
1. Understand the input and required output
The input is an integer array. We need an index i where the sum of all elements before i equals the sum of all elements after i. The current element nums[i] belongs to neither side. We return the index. If no such index exists, the shown solution returns -1.
2. Choose the running-prefix approach
A repeated-scan solution could calculate both sides again at every index. That does unnecessary work. Instead, we compute totalSum once. We also keep leftSum, which is the sum of elements strictly before the current index. At index i, the right side is totalSum - leftSum - nums[i]. This gives the right sum without scanning the remaining elements again.
3. Initialize the state
For the example array [-7, 1, 5, 2, -4, 3, 0], totalSum is 0. We start leftSum at 0 because there are no elements before index 0. We then move from left to right. At each index, we calculate rightSum, compare it with leftSum, and update leftSum only if we are going to continue.
4. Walk through the example
At index 0, nums[0] is -7. leftSum is 0. rightSum is 0 - 0 - (-7) = 7. The sums are not equal, so we update leftSum to -7.
At index 1, nums[1] is 1. leftSum is -7. rightSum is 0 - (-7) - 1 = 6. They are not equal, so leftSum becomes -6.
At index 2, nums[2] is 5. leftSum is -6. rightSum is 0 - (-6) - 5 = 1. They are not equal, so leftSum becomes -1.
At index 3, nums[3] is 2. leftSum is -1. rightSum is 0 - (-1) - 2 = -1. The two sums are equal, so the algorithm returns index 3 immediately. Indices 4, 5, and 6 are not processed.
5. Explain why the result is correct
Before checking index i, leftSum always equals the sum of elements strictly before i. Because totalSum contains every element, subtracting leftSum and nums[i] leaves exactly the sum strictly after i. Therefore, when leftSum equals rightSum, i satisfies the required condition. At index 3, the left side is -7 + 1 + 5 = -1 and the right side is -4 + 3 + 0 = -1.
6. Explain the C# implementation
The code first loops over the array to compute totalSum. It then sets leftSum to 0 and loops through the indices. For each index, it calculates rightSum from totalSum, leftSum, and the current value. If both sides match, it returns the current index. Otherwise, it adds the current value to leftSum and continues. If the loop ends without a match, it returns -1.
7. Explain complexity and edge cases
Computing totalSum takes O(n) time. The second loop processes each element at most once and may stop early, so the overall time is O(n). Only a few integer variables are used, so auxiliary space is O(1). If no equivalent index exists, the method returns -1. For an array of all zeros, index 0 is returned first. A single-element array also returns index 0 because both sides have sum 0.
Key Insight / Why This Solution Works
The key idea is to avoid recalculating the left and right sums from scratch for every index. First compute totalSum. Then maintain leftSum while moving from left to right. The invariant is that, before checking index i, leftSum is exactly the sum of all elements before i. The right side is therefore totalSum - leftSum - nums[i]. If both sums are equal, i is an equivalent index. Otherwise, add nums[i] to leftSum and continue. This turns repeated summation into constant-time work at each checked index.
Code
using System;
publicstaticclassProgram
{
publicstaticvoidMain()
{
// Use the exact example from the diagram.int[] nums = { -7, 1, 5, 2, -4, 3, 0 };
// Run the equivalent-index algorithm and print the returned index.int result = EquivalentIndex(nums);
Console.WriteLine(result);
}
publicstaticintEquivalentIndex(int[] nums)
{
// First compute the sum of every element so the right side// can later be derived without scanning it again.int totalSum = 0;
foreach (intvaluein nums)
{
totalSum += value;
}
// Before index 0 there are no elements, so the left sum starts at 0.int leftSum = 0;
// Process each index from left to right and stop as soon as a match is found.for (int i = 0; i < nums.Length; i++)
{
// Remove the left side and current value from the total.// What remains is the sum strictly to the right of index i.int rightSum = totalSum - leftSum - nums[i];
// Return immediately when both sides have the same sum.if (leftSum == rightSum)
{
return i;
}
// The current value becomes part of the prefix for the next index.
leftSum += nums[i];
}
// No equivalent index was found after checking all reachable positions.return-1;
}
}
Time & Space Complexity
Let n be the number of elements. The first loop computes the total sum in O(n) time. The second loop processes each array position at most once and may return early. Together, the two linear passes are still O(n) time. The algorithm uses only totalSum, leftSum, rightSum, and loop variables. It does not create another array or collection, so the auxiliary space is O(1).
Where it is used
This running-prefix pattern is useful when a program repeatedly needs information about everything before and after a position. It can be used for balance-point checks, partition calculations, cumulative totals, and similar array problems where rescanning each side would repeat work.
Why Interviewers Ask This
This problem tests whether you can remove repeated work with a running prefix sum. The interviewer can see whether you maintain the correct meaning of leftSum, derive the right side correctly, update state in the right order, and use early return safely. It also tests whether you can write simple C# loops, reason about edge cases such as a single element or no match, and explain why the final solution uses O(n) time and O(1) auxiliary space.
Common interview mistakes
A common mistake is including nums[i] in the left or right side even though the current element belongs to neither side. Another mistake is updating leftSum before checking the current index, which changes the meaning of the prefix. Candidates may also recalculate both sides with nested scans, giving O(n^2) time. Another mistake is returning the array value instead of the index. Finally, do not describe indices after an early return as processed.
Interview tip
State the invariant before coding: before checking index i, leftSum contains only the elements strictly before i. Then write rightSum = totalSum - leftSum - nums[i]. This makes the comparison and the update order easy to explain and verify.
Interviewer may ask next
How would you return all equivalent indices instead of stopping at the first one?
I would keep the same totalSum and running leftSum. When leftSum equals rightSum, I would add the current index to a result collection instead of returning immediately. Then I would still update leftSum and continue through the array. The invariant stays the same, so correctness is preserved. The running work remains O(n). The algorithm still uses O(1) working space apart from the output collection, which needs O(k) space for k returned indices. The tradeoff is that we can no longer stop at the first match.
What happens if no equivalent index exists?
The algorithm keeps checking each index using the same invariant. If no comparison has equal left and right sums, the loop finishes and returns -1. Nothing else changes. The time complexity is O(n) because the first pass computes totalSum and the second pass checks each index at most once. The auxiliary space remains O(1).
13. Matrix RotationCodingEasyApple
i Question Details
Rotate a square matrix in place, explain the layer-by-layer swaps, and state what changes for clockwise versus counterclockwise rotation.
Short Interview Answer (30-60 seconds)
I would rotate the square matrix in place, one layer at a time from the outside toward the center. For each layer, I rotate four matching positions together. For clockwise rotation, values move top to right, right to bottom, bottom to left, and left to top. I save the top value first so it is not lost. I repeat this for every offset in every layer. The algorithm takes O(n²) time and uses O(1) auxiliary space.
The input is a square matrix, and the goal is to turn it 90 degrees without creating another matrix. We work from the outside border toward the center. For each layer, we take four matching cells and move their values around the four sides. The order of these moves decides whether the rotation is clockwise or counterclockwise. The diagram uses a 4×4 matrix and shows the clockwise result. This method fits because every change is made directly inside the original matrix using only one temporary value.
Useful Questions to Ask the Interviewer
Should the implementation support both clockwise and counterclockwise rotation, or only the requested direction?
Do you want input validation for malformed or non-square matrices, or can I rely on the square-matrix contract?
How to Explain It in an Interview
1. Understand the input and required output
The input is an n × n square matrix. We must rotate it 90 degrees in place. In place means we modify the same matrix instead of creating another n × n matrix.
After a 90-degree clockwise rotation, the matrix becomes: 13 9 5 1 14 10 6 2 15 11 7 3 16 12 8 4
2. Process the matrix layer by layer
An n × n matrix has floor(n / 2) concentric layers. Layer 0 is the outer border. Layer 1 is the next border inside it.
For each layer, first is the layer index and last is n - 1 - layer. We process i from first up to, but not including, last. The offset is i - first.
For the 4×4 example, there are two layers. Layer 0 has first = 0 and last = 3. It uses i values 0, 1, and 2. Layer 1 has first = 1 and last = 2. It uses i = 1.
3. Perform each clockwise four-way swap
For clockwise rotation, values move top → right → bottom → left → top. The assignments must be performed carefully so a value is not overwritten before it is used.
First, save the top value. Then put the left value into the top position. Put the bottom value into the left position. Put the right value into the bottom position. Finally, put the saved top value into the right position.
For each offset, the four positions are: top = matrix[first][i] left = matrix[last - offset][first] bottom = matrix[last][last - offset] right = matrix[i][last]
All layers are complete, so the clockwise rotation is finished.
5. Explain clockwise versus counterclockwise
For clockwise rotation, values move top → right → bottom → left → top. The safe assignments are save top, top = left, left = bottom, bottom = right, and right = saved top.
For counterclockwise rotation, values move top → left → bottom → right → top. The safe assignments become save top, top = right, right = bottom, bottom = left, and left = saved top.
The layer boundaries, offsets, and traversal order stay the same. Only the four-way assignment direction changes.
6. Explain why the result is correct
Each four-way swap moves four values to their correct rotated positions for one layer and offset. Processing all offsets completes that layer. Processing all layers places every non-center element into its rotated position exactly once. If n is odd, the single center element stays unchanged. Therefore the final matrix is a valid 90-degree rotation and all original values are preserved.
7. Explain the C# implementation, complexity, and edge cases
The outer loop selects each layer. For one layer, first and last identify its boundaries. The inner loop processes each offset. A temporary variable saves the top value before the four assignments perform the clockwise cycle.
Across all layers, the swaps process Θ(n²) matrix positions, so the time complexity is O(n²). Only a temporary value and a few index variables are used, so auxiliary space is O(1). For n = 0 or n = 1, no swaps run and the matrix remains unchanged. The method is for square n × n matrices.
Key Insight / Why This Solution Works
The key idea is to treat the matrix as concentric square layers. For each layer, rotate four corresponding cells at a time instead of copying the matrix. The invariant is that after one offset is processed, those four values are in their correct 90-degree rotated positions and do not need to be changed again. For clockwise rotation, values move top → right → bottom → left → top. Saving the top value before the assignments prevents data loss. Repeating this for every offset and every layer completes the in-place rotation.
Code
using System;
publicstaticclassProgram
{
publicstaticvoidMain()
{
// Use the exact 4x4 example shown in the diagram.int[][] matrix = { newint[] { 1, 2, 3, 4 }, newint[] { 5, 6, 7, 8 },
newint[] { 9, 10, 11, 12 }, newint[] { 13, 14, 15, 16 } };
// Rotate the same matrix 90 degrees clockwise in place.
Rotate(matrix);
// Print the verified final matrix from the diagram.for (int row = 0; row < matrix.Length; row++)
{
for (int column = 0; column < matrix[row].Length; column++)
{
Console.Write(matrix[row][column]);
// Put spaces between values, but not after the last value in a row.if (column < matrix[row].Length - 1)
{
Console.Write(" ");
}
}
Console.WriteLine();
}
}
publicstaticvoidRotate(int[][] matrix)
{
// The problem contract gives an n x n square matrix.int n = matrix.Length;
// Process concentric layers from the outside toward the center.for (int layer = 0; layer < n / 2; layer++)
{
// first and last are the boundaries of the current layer.int first = layer;
int last = n - 1 - layer;
// Process each four-cell group in this layer once.for (int i = first; i < last; i++)
{
// offset is the distance from the first position of the layer.int offset = i - first;
// Save top before another value overwrites it.int top = matrix[first][i];
// Clockwise rotation: move left into top.
matrix[first][i] = matrix[last - offset][first];
// Move bottom into left.
matrix[last - offset][first] = matrix[last][last - offset];
// Move right into bottom.
matrix[last][last - offset] = matrix[i][last];
// Move the saved top value into right.
matrix[i][last] = top;
}
}
}
}
Time & Space Complexity
Let n be the number of rows and columns. Across all layers, the algorithm processes Θ(n²) matrix positions. Each four-way swap uses constant work, so the total time is O(n²). The algorithm does not allocate another matrix. It uses one temporary integer and a few index variables, so the auxiliary space is O(1). For an odd-sized matrix, the single center element remains unchanged.
Where it is used
This pattern is useful when square grid data must be rotated without allocating another full grid. Examples include image or tile transformations, board-game state changes, and matrix-based graphics operations where keeping extra memory constant is useful.
Why Interviewers Ask This
This problem tests careful reasoning about two-dimensional indices and in-place mutation. The interviewer can see whether you recognize the concentric-layer pattern, calculate matching positions correctly, save a value before overwriting it, and distinguish clockwise from counterclockwise movement. It also checks whether you can translate the index logic into correct C# code, maintain the layer invariant, reason about edge cases, and explain the O(n²) time and O(1) auxiliary space accurately.
Common interview mistakes
Common mistakes are using the wrong movement direction and producing a counterclockwise result when clockwise was requested, overwriting the top value before saving it, or calculating the left and bottom coordinates with the wrong offset. Using i <= last instead of i < last repeats a corner and corrupts the layer. Another mistake is rotating only the outer layer and forgetting inner layers. It is also incorrect to claim O(n) time because the swaps process Θ(n²) matrix positions overall.
Interview tip
Before coding, write the clockwise value cycle as top → right → bottom → left → top. Then translate it into safe assignments as save top, top = left, left = bottom, bottom = right, and right = saved top. This makes both the rotation direction and overwrite order easy to verify.
Interviewer may ask next
What changes if the matrix must be rotated 90 degrees counterclockwise?
The layer boundaries, offsets, traversal order, time complexity, and auxiliary space stay the same. Only the four-way assignment direction changes. Values move top → left → bottom → right → top. Save the top value, then perform top = right, right = bottom, bottom = left, and left = saved top. This places each group into its correct counterclockwise positions. The time remains O(n²), and auxiliary space remains O(1).
Can the auxiliary space be reduced below O(1)?
No meaningful asymptotic reduction is available because O(1) already means the extra memory does not grow with n. The algorithm uses only a few index variables and one temporary value to avoid losing data during a four-way swap. Removing that temporary value would not improve the asymptotic space bound. The algorithm still takes O(n²) time and modifies the matrix in place.
14. Walking RobotCodingEasyApple
i Question Details
Simulate the robot's movement step by step, explain how you track orientation and coordinates, and clarify how obstacles or repeated commands affect the final position.
Short Interview Answer (30-60 seconds)
I track the robot with an (x, y) position and a direction index from 0 to 3 for North, East, South, and West. Left and right commands update only the direction using modulo 4. For a forward command, I calculate the next cell and move only if that cell is not in the obstacle HashSet. In the example, the last two moves are blocked. The result is (1,2), facing North. Expected time is O(n + k), with O(k) auxiliary space.
The robot starts at (0,0) and faces North. It receives commands that turn it left, turn it right, or move it forward by one cell. Some coordinates contain obstacles that the robot cannot enter. We process the commands in order and keep track of both the position and facing direction. A small direction number makes turning simple. A set of obstacle coordinates lets us quickly decide whether each forward move is allowed. The goal is to return the robot's final coordinate and direction after every command has been processed.
Useful Questions to Ask the Interviewer
Can I assume every command is one of L, R, or G?
Are the obstacle coordinates fixed while the commands are being processed?
Should an invalid command be ignored, rejected, or considered impossible input?
How to Explain It in an Interview
1. Understand the input and required output
The robot starts at (0,0), facing North. The input contains a sequence of movement commands and obstacle coordinates. The output is the final x coordinate, y coordinate, and facing direction after all commands are processed. In the diagram, the commands are G G R G L G G. The obstacles are (1,3) and (2,2). The final result is (1,2), facing North.
2. Track orientation with a direction index
I encode the directions as 0 = North, 1 = East, 2 = South, and 3 = West. I keep dx = {0, 1, 0, -1} and dy = {1, 0, -1, 0}. The direction index tells me which coordinate change to use. Turning left uses (dir + 3) % 4. Turning right uses (dir + 1) % 4. A turn changes only the direction. It does not change the position.
3. Store obstacles in a HashSet
I store every blocked coordinate in a HashSet. Before a G command changes the robot's position, I calculate the next coordinate and check whether it is blocked. HashSet lookup is O(1) on average. If the next coordinate is blocked, the position stays unchanged. The direction also stays unchanged because a forward command does not turn the robot.
4. Walk through the example
The robot begins at (0,0), facing North. The first G moves it to (0,1). The second G moves it to (0,2). R changes the direction from North to East, while the position stays at (0,2). The next G moves it east to (1,2). L changes the direction from East back to North. The next G tries to enter (1,3). That coordinate is blocked, so the robot stays at (1,2). The final G tries to enter (1,3) again. It is still blocked, so the robot again stays at (1,2), facing North.
5. Explain why the result is correct
The main invariant is that (x, y) always represents the last valid cell occupied by the robot. A turn changes only the direction index. A forward command changes the position only after its destination passes the obstacle check. Therefore, the robot never enters a blocked cell. If a forward command is repeated while the same blocked cell remains directly ahead, the robot remains in exactly the same state. This is why the example finishes at (1,2), facing North.
6. Explain the C# implementation
The code first converts the obstacle coordinates into a HashSet. It initializes x = 0, y = 0, and dir = 0 for North. It then processes each command in order. L and R update dir with modulo arithmetic. G uses dx[dir] and dy[dir] to calculate the next coordinate. The code changes x and y only when the HashSet does not contain that coordinate. Finally, it converts the direction index to North, East, South, or West and returns the final state.
7. Explain complexity and edge cases
Let n be the number of commands and k be the number of obstacles. Building the HashSet takes expected O(k) time. Processing the commands takes expected O(n) time because each HashSet lookup is O(1) on average. Total expected time is O(n + k). Auxiliary space is O(k). With an empty command string, the robot remains at (0,0), facing North. Commands containing only turns change direction but not position. A blocked forward move leaves the position unchanged. Repeating the same blocked forward move also leaves the position unchanged. The diagram treats the grid as unbounded, so negative coordinates are allowed.
Key Insight / Why This Solution Works
The key idea is to represent the robot's state with x, y, and a direction index. The direction index is always 0, 1, 2, or 3, so modulo 4 handles turns without many special cases. The dx and dy arrays translate the current direction into one coordinate step. A HashSet stores blocked coordinates. The central invariant is that (x, y) is always a valid position the robot has actually reached. For each forward command, the algorithm calculates a candidate next cell first. It changes the position only when that cell is not blocked. This also handles repeated commands correctly because repeated attempts to enter the same blocked cell leave the state unchanged.
Code
using System;
using System.Collections.Generic;
publicstaticclassProgram
{
// Direction order used by the diagram: 0 = North, 1 = East, 2 = South, 3 = West.privatestaticreadonlyint[] Dx = { 0, 1, 0, -1 };
privatestaticreadonlyint[] Dy = { 1, 0, -1, 0 };
// Convert the final direction index back to a readable direction name.privatestaticreadonlystring[] DirectionNames = { "North", "East", "South", "West" };
publicstatic (int X, int Y, string Direction) Simulate(string commands, int[][] obstacles)
{
// Store blocked coordinates so each forward move can test its destination quickly.
HashSet<(int X, int Y)> blocked = new HashSet<(int X, int Y)>();
foreach (int[] obstacle in obstacles)
{
// Each obstacle contains one x coordinate and one y coordinate.
blocked.Add((obstacle[0], obstacle[1]));
}
// Start at the exact state shown in the diagram: (0,0), facing North.int x = 0;
int y = 0;
int dir = 0;
// Process every command in the given order because each command changes the next state.foreach (char command in commands)
{
if (command == 'L')
{
// Turn left without moving. Adding 3 is the same as subtracting 1 modulo 4.
dir = (dir + 3) % 4;
}
elseif (command == 'R')
{
// Turn right without moving to another coordinate.
dir = (dir + 1) % 4;
}
elseif (command == 'G')
{
// Calculate the cell directly ahead from the current position and direction.int nextX = x + Dx[dir];
int nextY = y + Dy[dir];
// Commit the move only when the destination is not blocked.// If blocked, leaving x and y unchanged correctly represents the failed move.if (!blocked.Contains((nextX, nextY)))
{
x = nextX;
y = nextY;
}
}
}
// Return the exact final coordinate and facing direction after all commands finish.return (x, y, DirectionNames[dir]);
}
publicstaticvoidMain()
{
// Same command sequence shown in the approved diagram: G G R G L G G.string commands = "GGRGLGG";
// Same two obstacle coordinates shown in the approved diagram.int[][] obstacles = { new[] { 1, 3 }, new[] { 2, 2 } };
// Run the example and print its verified final state.
(int X, int Y, string Direction) result = Simulate(commands, obstacles);
Console.WriteLine($"Final position: ({result.X},{result.Y}), facing {result.Direction}");
}
}
Time & Space Complexity
Let n be the number of commands and k be the number of obstacles. Building the obstacle HashSet takes expected O(k) time. We then process each command once. A HashSet lookup is O(1) on average, so processing the commands takes expected O(n) time. The total expected time is O(n + k). The HashSet stores k obstacle coordinates, so the auxiliary space is O(k). The direction arrays and the integer state variables use only constant extra space.
Where it is used
This pattern is useful in grid simulations, game movement, robot command processing, warehouse navigation, and other systems that update an object's state one command at a time. A direction index works well when an object can face a small fixed set of directions. A HashSet is useful when the program repeatedly needs to check whether a coordinate is blocked.
Why Interviewers Ask This
This problem checks whether a candidate can model changing state carefully. The interviewer can see whether you keep coordinates and orientation consistent, choose a simple direction representation, use a HashSet for repeated obstacle checks, and process commands in the correct order. It also tests whether you handle blocked moves without corrupting state, reason about repeated commands, write clear C#, maintain a useful invariant, and describe expected hash-based complexity accurately.
Common interview mistakes
A common mistake is changing the position during an L or R command. Turns should change only the direction. Another mistake is updating x and y before checking whether the destination is an obstacle. The candidate next coordinate must be checked first. It is also easy to use the wrong dx or dy order and make the direction encoding inconsistent. Another mistake is changing direction when a G command is blocked. A blocked forward move changes neither position nor direction. Finally, candidates may forget that building the obstacle HashSet contributes to the expected O(n + k) total time and uses O(k) auxiliary space.
Interview tip
State the invariant before coding: the current (x, y) is always the last valid cell reached by the robot. Then explain that every G command calculates a candidate next cell first and commits the move only after the HashSet confirms that the cell is not blocked.
Interviewer may ask next
What changes if the robot must stay inside a fixed rectangular grid?
For every G command, I would calculate nextX and nextY exactly as before. Before updating the position, I would check both conditions: the coordinate must be inside the grid bounds and it must not be in the obstacle HashSet. The invariant stays the same because the stored position is always a valid reachable cell. With n commands and k obstacles, the expected time remains O(n + k) and auxiliary space remains O(k). The tradeoff is one additional constant-time bounds check for each forward command.
What changes if one forward command can move several cells, such as G10?
I would parse the distance and simulate the movement one cell at a time. Each individual step must calculate the next coordinate and check the obstacle HashSet because an obstacle can stop movement before the full distance is completed. The same invariant is preserved because the position changes only after each next cell is validated. If s is the total number of individual forward steps represented by all commands, expected time becomes O(k + s), and auxiliary space remains O(k). The tradeoff is more work when movement distances are large.
15. Design a feature flag system for a mobile appAPI DesignEasyApple
i Question Details
Describe the API surface for targeting, rollout, and kill switches, and cover how clients fetch flags safely under partial outages.
Short Interview Answer (30-60 seconds)
At a high level, I would separate mobile flag evaluation from flag management. The mobile app calls GET /v1/flags through the API Gateway and receives flags and configuration with 200 OK. The Flag SDK caches values locally, evaluates them on the device, and can receive SSE updates from GET /v1/stream. Targeting and percentage rollouts control normal releases, while kill switches use a higher-priority override path. The reliability choice is offline-first caching with TTLs and safe defaults. The trade-off is that cached flags can briefly be stale.
Detailed Explanation
We need a safe way to change mobile app behavior without publishing a new app version. Different users may receive different feature settings. A feature can also be released slowly to reduce risk. If a feature causes a serious problem, the team needs a fast way to disable it. The main challenge is keeping the app usable when the network or flag service has problems. The diagram solves this with local caching, local evaluation, streaming updates, controlled rollouts, and high-priority kill switches.
Useful Questions to Ask the Interviewer
Should targeting use user, device, environment, or application information?
How quickly should a kill switch reach connected clients?
How long may clients use cached flags during an outage?
Do different environments need separate rollout rules?
How to Explain It in an Interview
1. Start with the mobile flag request
I would start with the normal read path. The mobile app uses the Flag SDK and sends GET /v1/flags over HTTPS toward the API Gateway. The diagram also shows JWT and mTLS on this path. The gateway owns rate limiting, authentication, schema validation, and routing. It forwards the request to the Flag Service. The Flag Service resolves flags for the client context, evaluates rules, matches segments, and uses precomputed or cached results when useful. The successful response returns through the gateway to the client as 200 OK with flags and configuration.
2. Evaluate safely on the device
The Flag SDK is offline-first. It fetches and caches flags, evaluates them locally, respects TTL values, and keeps the local cache encrypted. Local evaluation avoids a network call for every feature decision. If the network is unavailable, the SDK can serve the last good value. The resilience design also shows stale-while-revalidate behavior, so a known value may be used while a refresh is attempted. If no usable value exists, the client uses its built-in safe default.
3. Support targeting and controlled rollout
The management side contains the Admin UI, Management API, Rollout Engine, and Event Bus. Administrators create flags, rules, segments, environments, and rollouts. The Management API provides CRUD operations, versioned changes, and RBAC. RBAC means only allowed administrators can perform management actions. The Rollout Engine supports percentage rollouts, ramp schedules, and canary or ring deployments. The visible management API includes POST /v1/flags, GET /v1/flags/{id}, PUT /v1/flags/{id}, DELETE /v1/flags/{id}, GET /v1/segments, POST /v1/segments, and POST /v1/rollouts.
4. Push normal updates with SSE
The Stream Service maintains SSE connections, heartbeats, incremental updates, and reconnection. A client can call GET /v1/stream to receive updates. SSE means the server keeps an HTTP connection open and sends changes as they become available. This reduces repeated polling. The stream is a separate path from GET /v1/flags, so a temporary streaming failure does not remove the client's cached fallback.
5. Give kill switches higher priority
The Kill Switch Service owns immediate global or targeted overrides. The API surface shows POST /v1/kill-switch to trigger a kill switch and GET /v1/kill-switch/status to read its status. Kill-switch changes use a high-priority channel and can be pushed to the Flag SDK. The SDK treats this override as higher priority than ordinary flag behavior. This is important because emergency shutdown should not wait for a normal rollout schedule.
6. Store data, observe changes, and handle failures
The Flag Store keeps flags, rules, segments, and environments. The cache keeps evaluated flags, segments, and TTL-based cached data. The Audit Log records change history, impressions, and kill-switch events. Telemetry and observability collect metrics, logs, traces, alerts, dashboards, and anomaly information. These are supporting paths, not part of the synchronous mobile response.
For failures, the design uses timeouts, circuit breakers, local caching, safe defaults, and multi-region active-active operation at the global edge. The main trade-off is consistency versus availability. Cached flags keep the app responsive during outages, but a client may temporarily use an older value.
Practical Complexity & Trade-offs
The benefit of this design is that the mobile app does not need the network for every feature decision. Local evaluation, encrypted caching, TTLs, and stale-while-revalidate behavior keep the app useful during partial outages. SSE reduces repeated polling because the server can send incremental updates. The downside is that cached values can temporarily be old. Targeting, segments, and percentage rollouts give teams more control, but they add management complexity. Kill switches reduce risk because they override normal behavior, but they need a higher-priority delivery path. The API Gateway adds rate limiting, authentication, validation, and routing. The Management API adds RBAC and versioned changes. We accept this complexity because feature flags directly affect production behavior.
Why Interviewers Ask This
Interviewers want to see whether you can design a practical API around real operational needs. They are checking whether you can separate mobile evaluation from management, model request and response paths correctly, and support targeting, controlled rollouts, and emergency kill switches. They also want to see how you handle partial outages safely. Strong answers show clear ownership, sensible caching, secure management access, safe defaults, observability, and a clear explanation of the consistency-versus-availability trade-off.
Interviewer may ask next
What happens if the feature flag service is unavailable for several minutes?
The mobile app should keep working from its local state instead of depending on a successful network call. The affected path is GET /v1/flags through the API Gateway to the Flag Service. If that path fails or times out, the Flag SDK can use the encrypted local cache and its last good value. The diagram also shows TTL handling and stale-while-revalidate behavior, so the client can keep using a known value while trying to refresh it. If no usable cached value exists, the SDK uses its built-in safe default. Circuit breakers and timeouts prevent repeated waits on an unhealthy dependency. The SSE stream may also be unavailable, but that does not remove the local fallback. When service access returns, the client refreshes its flags again. The main downside is temporary staleness. A user may briefly see an older flag value, but the application remains usable and avoids depending completely on the remote service.
How would you disable a dangerous feature quickly after release?
I would use the existing high-priority kill-switch path. An authorized administrator triggers the emergency change through POST /v1/kill-switch. The Kill Switch Service owns the immediate override and supports global or targeted changes. The management side can publish kill-switch events through its delivery flow, while connected clients can receive the high-priority update through the streaming path. The Flag SDK gives the kill-switch override priority over normal feature behavior. The current status can be checked with GET /v1/kill-switch/status. Normal targeting, segments, and percentage rollout behavior stay unchanged for other flags. Management access still uses the diagram's RBAC and identity controls, and the Audit Log records kill-switch activity for traceability. The main downside is that a completely disconnected device cannot receive a new server-side push immediately. That is why the design also relies on local safe behavior and built-in defaults during partial outages.
16. Build a logging system for on-device eventsAPI DesignEasyApple
i Question Details
Focus on the ingestion API, privacy constraints, retention behavior, and how the client batches or redacts events before upload.
Short Interview Answer (30-60 seconds)
At a high level, I would make device logging private, reliable, and efficient. The app builds an event, removes sensitive data, batches and compresses events, then keeps them in an encrypted local queue. A Background Uploader sends POST /v1/logs/batch over HTTPS with TLS 1.3, a short-lived JWT, gzip, and an Idempotency-Key. The request goes through the API Gateway, Ingestion API, validation, a durable queue, workers, and storage. The API returns 200 OK to the uploader. The trade-off is more client and backend complexity for stronger privacy and dependable delivery.
Detailed Explanation
We need a safe way for an app to record useful events. The app should remove private information before anything leaves the device. It should group several events together to save network and battery use. If the device is offline, events should wait safely until upload is possible. On the server, we need to accept the batch, check it, process it, store useful data, and delete old data after a fixed time. I would explain the design by following the same client-to-server path shown in the diagram.
Useful Questions to Ask the Interviewer
Which event types are allowed?
How long should raw and aggregated data be kept?
What batch size or upload delay is acceptable?
Are privacy rules different for different events?
How to Explain It in an Interview
1. Protect data before upload
I would start with privacy on the device. The App sends an event to the Logging SDK. The Event Builder creates the event. Redaction & Privacy then applies an allowlist and removes PII, meaning information that can identify a person.
The diagram also avoids raw IP or location data and free-form text. Its sample event uses an anonymous device value and limited attributes. The client can sample or aggregate events when needed. This reduces privacy risk because sensitive data does not need to reach the backend.
2. Batch events and support offline devices
After redaction, the Batcher & Compressor groups events by size or time. This reduces network calls and battery use. The batch is compressed and placed in the encrypted Local Queue, shown as SQLite or a file.
The queue lets the app work offline. Events can wait until upload is possible. The design is also battery-aware. A device key is kept in the Keychain for local protection.
3. Send the ingestion request
The Background Uploader sends POST /v1/logs/batch to the API Gateway over HTTPS using TLS 1.3. The request includes Authorization: Bearer <JWT>, Content-Encoding: gzip, and an Idempotency-Key.
The JWT is short-lived and scoped to ingestion. The Idempotency-Key helps the backend recognize a repeated batch and avoid duplicate work after retries. The uploader uses exponential backoff with jitter for retries.
The API Gateway provides WAF, rate limiting, and DDoS protection. It forwards the request to the Ingestion API, shown as a .NET and ASP.NET Core service.
4. Validate and process asynchronously
The Ingestion API sends the data to Validation & Schema Enforcement. This stage checks JSON, size, schema, and the allowlist. Accepted events continue to the Stream / Queue.
Processing Workers consume queued events and perform deduplication, enrichment, and aggregation. Normal events go to Storage, shown as a time-series database or blob storage partitioned by time. If processing finds an invalid or poison message, the diagram shows a separate failure path from Processing Workers to the Dead Letter Queue.
5. Return the client response
The success response returns from the Ingestion API to the Background Uploader. The diagram shows HTTPS 200 OK with accepted: true, nextUploadAfterSec: 300, and invalidEvents: [...].
This acknowledgement is separate from the asynchronous processing path. The client does not need to wait for analytics or long-term processing before receiving the response.
6. Apply retention and deletion
Storage feeds Retention & Purge. Raw events are kept for 7 days in hot storage. Aggregated data is kept for 90 days. Rollups or exports are kept for 1 year. After the configured TTL, meaning time-to-live, the data is securely deleted.
Analytics / Export supports reports, dashboards, and a data lake. The design avoids user-level joins and keeps observability free of PII. The main trade-off is extra client and backend complexity in exchange for better privacy, reliable uploads, and controlled retention.
Practical Complexity & Trade-offs
The main design choice is to protect privacy before upload. The benefit is that sensitive data does not need to reach the backend. The downside is more logic inside the client SDK. Batching and gzip reduce network and battery use, but events may be delayed slightly. The encrypted Local Queue improves offline reliability, but stored events must be protected carefully. The Idempotency-Key reduces duplicate work when retries happen. The Stream / Queue separates fast ingestion from slower processing, but it adds another service to operate. Short-lived JWTs and TLS 1.3 improve security. Retention rules reduce privacy risk and storage cost, but shorter retention gives less historical data for debugging and analytics. We accept these costs because privacy and reliable delivery are the main goals.
Why Interviewers Ask This
The interviewer is testing whether you can define a clear ingestion boundary and follow the complete request and response flow. They want to see good judgment around client-side privacy, batching, offline reliability, validation, asynchronous processing, idempotency, retention, and failure isolation. A strong answer also shows that you understand which responsibility belongs on the device, at the API boundary, or in backend processing, and that you can explain the trade-offs clearly.
Interviewer may ask next
What changes if millions of devices upload batches at the same time?
I would keep the same API and scale the existing flow rather than replace the design. The affected path is Background Uploader → API Gateway → Ingestion API → Validation & Schema Enforcement → Stream / Queue. The API Gateway already provides rate limiting and DDoS protection, so it can control sudden bursts before they overload ingestion. The Stream / Queue is also important because it separates the upload rate from Processing Workers. If ingestion is temporarily faster than processing, accepted events can wait in the durable queue instead of forcing the client to wait for downstream work.
On the client, I would keep batching and exponential backoff with jitter. Jitter spreads retries over time and reduces synchronized retry spikes. The Idempotency-Key still protects against duplicate work when the same batch is retried.
The main downside is increased queue delay during a large burst. We accept that temporary processing delay because it protects the ingestion path and keeps uploads reliable.
How would you handle stricter privacy and shorter retention requirements?
I would keep the architecture and tighten the existing privacy and retention policies. On the client, Redaction & Privacy would continue using the allowed-event list, PII removal, no raw IP or location, no free-form text, and sampling or aggregation rules. That means sensitive data is removed before POST /v1/logs/batch is sent.
On the backend, the affected components are Storage and Retention & Purge. The current diagram keeps raw events for 7 days, aggregated data for 90 days, and rollups or exports for 1 year. For a stricter policy, I would reduce those configured TTL values rather than create a different storage path. Secure deletion would still happen after the new TTL, and observability would remain free of PII.
The downside is having less historical information for debugging and analytics. The benefit is lower privacy risk and less stored data. The ingestion, validation, queue, processing, and response flow stays unchanged.
17. Design a data pipeline for device analyticsAPI DesignEasyApple
i Question Details
Explain the interface between collection, transport, and storage layers, and describe how you handle batch versus streaming paths.
Short Interview Answer (30-60 seconds)
At a high level, I would separate device analytics into collection, transport, and storage and processing layers. Devices send events through a Telemetry SDK, which can stream events immediately or buffer them for periodic batches. The ingestion service validates JWTs, limits traffic, checks schemas, and routes streaming events to Kafka or batch objects to Object Storage. Stream Processing writes low-latency results to the Analytics Store, while Batch Processing writes historical results to the Data Warehouse. I use mTLS, retries, idempotent ingestion, and a Dead Letter Queue. The trade-off is lower streaming latency versus simpler, efficient batch processing.
Detailed Explanation
This design collects information from many devices and turns it into useful analytics. Some information must appear quickly, such as live activity or alerts. Other information can wait and be processed in larger groups for reports. The main challenge is supporting both needs without making devices or backend services unreliable. The diagram solves this by separating collection, transport, and storage and processing. It gives streaming and batch data different paths while sharing security, monitoring, retry, and failure handling across the pipeline.
Useful Questions to Ask the Interviewer
How quickly must streaming analytics become available?
How much event volume should the system support?
How often should devices send batch data?
How long should analytics data be retained?
How to Explain It in an Interview
1. Start with device collection
I would begin at the devices because this is where analytics events are created. Device Sources include iOS, watchOS, macOS, tvOS, and other devices. Events enter a Telemetry SDK. The SDK captures events and adds context such as device ID, app version, and timestamp.
The SDK supports both streaming and batch behavior. It also uses a local buffer and retry with backoff. A Local Persistent Buffer on disk or SQLite can keep events when immediate delivery is not possible.
2. Define the collection-to-transport interface
The next boundary is Collection to Transport. The diagram uses HTTPS or HTTP/2 with mTLS. mTLS encrypts the connection and lets both sides authenticate each other. Events use newline-delimited JSON, and a Device Token carried as a JWT provides device authentication.
The diagram defines two contracts. POST /v1/events/stream carries real-time events. POST /v1/events/batch carries periodic batches.
3. Validate and route events at ingestion
The API Gateway / Ingestion Service receives events. It terminates mTLS and performs JWT authentication and authorization checks. It also applies rate limiting and throttling, validates schemas, and routes data to the correct path.
The service uses event_id for idempotency. This helps prevent a retried event from producing an unwanted duplicate result. Invalid or failed events can move to the Dead Letter Queue for later handling.
4. Split streaming and batch transport
For streaming, the API Gateway / Ingestion Service sends events to the Apache Kafka Message Broker. Kafka carries those events toward Stream Processing. For batch traffic, the ingestion service writes batch objects into S3, GCS, or compatible Object Storage.
The Transport-to-Storage interface uses Kafka or HTTPS. The diagram shows Avro or JSON formats, mTLS or SASL authentication, and either a Kafka topic or an Object Path as the contract.
5. Process the streaming path
Stream Processing receives the real-time event stream. It parses and validates records, performs enrichment and sessionization, creates aggregations, and writes results to the Analytics Store.
The Analytics Store uses Druid or ClickHouse. The Query Service reads these processed results through REST or gRPC. This path supports low-latency dashboards and alerts.
6. Process the batch path
Batch Processing periodically reads objects from Object Storage. It performs ETL or transformations, creates aggregations, and writes results to the Data Warehouse.
The Data Warehouse can be BigQuery, Snowflake, or Redshift. The Query Service can read these processed results for historical analytics and reporting.
7. Explain shared reliability and trade-offs
The design also includes Monitoring, centralized Logging, distributed Tracing, a Config Service, a Secrets Manager, and a DLQ Reprocessor. Security uses mTLS, JWT authentication, and least-privilege access. Reliability uses local buffering, retry with backoff, idempotent ingestion, and dead-letter handling.
The design can scale horizontally using partitioned topics and autoscaled workers. The main trade-off is complexity. Streaming gives low latency but needs Kafka and continuous processing. Batch processing is slower, but it works well for large historical analytics and reporting.
Practical Complexity & Trade-offs
The main design choice is keeping streaming and batch paths separate after ingestion. The benefit is that real-time events can reach the Analytics Store quickly, while larger batches can use Object Storage and the Data Warehouse. The downside is more services to operate. Kafka and Stream Processing add operational work, while Batch Processing adds delay before results appear. Security also adds work because mTLS and JWT checks must be managed correctly. Rate limiting protects ingestion from overload. Schema validation keeps malformed events out of normal processing. Idempotency using event_id helps with repeated delivery. Local buffering and retry improve reliability but require device storage. The Dead Letter Queue isolates failed events, but those events still need later inspection and replay.
Why Interviewers Ask This
Interviewers use this question to test whether you can define clear boundaries between collection, transport, and storage. They want to see whether you understand when streaming is useful and when batch processing is better. They also evaluate authentication, validation, retries, duplicate-event handling, failed events, reliability, and scaling. A strong answer shows that you can trace data flow clearly, assign responsibilities to the correct components, and explain practical trade-offs without adding unnecessary infrastructure.
Interviewer may ask next
What would you change if the number of device events increased dramatically?
I would keep the same architecture and scale the components that already support horizontal growth. The API Gateway / Ingestion Service would run more instances so incoming device traffic can be distributed across them. Rate limiting and throttling would still protect ingestion when traffic exceeds safe capacity. For the streaming path, I would increase Kafka capacity and use partitioned topics so more Stream Processing workers can consume events in parallel. Those workers can scale with the amount of work. For the batch path, ingestion would continue writing objects to Object Storage, while more Batch Processing workers could process those objects.
I would preserve schema validation, JWT checks, mTLS, and event_id idempotency during scaling. Monitoring, Logging, and Tracing become more important because work is spread across more instances. The main downside is operational complexity. More partitions and workers improve throughput, but they require careful capacity planning, balancing, and monitoring.
How would you handle devices that temporarily lose their network connection?
I would use the existing local buffering and retry path shown in the Collection Layer. The Telemetry SDK would keep events in its Local Persistent Buffer on disk or SQLite instead of immediately discarding them. When connectivity returns, it can retry with backoff. This prevents many disconnected devices from retrying aggressively at the same moment. Streaming events may therefore arrive later than normal, while buffered events can also be delivered through the periodic batch path when appropriate.
The API Gateway / Ingestion Service still performs the same JWT checks, schema validation, rate limiting, routing, and event_id idempotency. Idempotency matters because a retry may resend an event that was already accepted. Invalid or failed events can still move to the Dead Letter Queue, and the DLQ Reprocessor can retry or replay them later. The downside is that the Local Persistent Buffer uses device storage, and delayed connectivity makes analytics temporarily less current.
18. Design an experiment platform for product teamsAPI DesignMediumApple
i Question Details
Define the API for assignment, metrics capture, and results retrieval, and cover how you keep experiment logic separate from reporting.
Short Interview Answer (30-60 seconds)
At a high level, I would separate real-time experiment decisions from reporting. Product teams send HTTPS requests with a JWT to the API Gateway. The gateway forwards traffic over mTLS to the ASP.NET Core Experiment Service. The main APIs are POST /v1/assignment, POST /v1/metrics, and GET /v1/experiments/{experimentKey}/results. Assignment uses targeting rules and stable bucketing. Metrics become asynchronous events for analytics. Results come from a separate reporting path. OAuth2/OIDC, RBAC, scopes, and audit logging protect access. The trade-off is that asynchronous reporting can lag behind new events.
Detailed Explanation
This platform helps product teams safely test different product experiences. A user should receive the correct experiment choice and keep getting a consistent choice. Teams also need to record user actions and later understand which variation performed better. The main challenge is separating fast user assignment from slower reporting work. Assignment must remain predictable and responsive. Reporting can perform heavier calculations later. I would follow the diagram by using one gateway, an ASP.NET Core experiment service, an asynchronous event path, and a separate reporting path built from captured experiment events.
Useful Questions to Ask the Interviewer
How quickly must assignment responses return?
How fresh do experiment results need to be?
Which user attributes may be used for audience targeting?
How much metrics traffic should the platform expect?
How to Explain It in an Interview
1. Start with the client and API boundary
I would start at the public API boundary. Product Teams call the API Gateway using HTTPS plus a JWT. HTTPS protects data while it travels over the network. A JWT is a signed token carrying caller identity and access information. The API Gateway is the REST entry point. It forwards requests to the ASP.NET Core Experiment Service over mTLS. mTLS encrypts that connection and lets both sides verify each other. JSON responses return from the Experiment Service through the gateway to the client.
2. Explain real-time assignment
The assignment endpoint is POST /v1/assignment. Its request contains experimentKey, userId, context, and idempotencyKey. Its response contains experimentKey, userId, variantKey, reason, and ttl. The Assignment Service handles audience targeting, bucketing or hashing, and assignment evaluation. Stable bucketing helps a user receive a consistent variation. The Configuration Store contains experiments, variants and rules, traffic allocation, feature flags, and lifecycle state. The core data area also includes the Experiment DB, a Metrics Store shown as a Time-Series DB, and Redis cache. Assignment logic stays on this real-time side of the platform.
3. Capture metrics asynchronously
For user behavior, the endpoint is POST /v1/metrics. The request contains eventName, userId, experimentKey, variantKey, value, and timestamp. The response contains status, eventId, and receivedAt. The Metrics Ingest Service captures event metrics, validates their schema, deduplicates repeated events, buffers and persists data, and supports high throughput. Valid events are published to the Event Bus, shown as Kafka or RabbitMQ. The Event Bus acknowledges publication. The Analytics Pipeline consumes those events asynchronously and acknowledges consumption. This keeps analytics processing away from the synchronous request path.
4. Read experiment results from the reporting path
The results endpoint is GET /v1/experiments/{experimentKey}/results. Its query parameters are startDate, endDate, metrics, and breakdowns. The response contains experimentKey, status, and summary information such as totalUsers and metric results. On the reporting side, a separate Metrics Store is labeled Analytics DB. The Aggregation and Statistics Service calculates aggregated metrics and statistical results. The Results Service serves queries and reports. The Visualization or Dashboard component consumes reporting output. Reporting does not call the Assignment Service to recompute user decisions.
5. Keep experiment logic separate from reporting
This separation is the most important design decision. Experiment logic, including assignment, rules, bucketing, and traffic allocation, stays in the core services and Configuration Store. Reporting is built from immutable metric events processed by the Analytics Pipeline and reporting read models. Immutable means the recorded event history is treated as stable input for analysis. The analytics side can change reports and calculations without changing assignment behavior. The benefit is isolation between real-time decisions and analytical reporting. The downside is that results may be slightly behind the newest events because analytics runs asynchronously.
6. Finish with security, reliability, and data quality
The Identity and Access component supports OAuth2/OIDC, service API keys, RBAC by team or project, scopes and permissions, and audit logging. RBAC means access is based on the caller's role. The diagram also emphasizes asynchronous events, idempotency, an immutable event log, and separation of experiment logic from reporting. Metrics ingestion validates schemas and deduplicates events. These controls improve reliability and data quality without moving reporting work into the real-time assignment path. Experiment data, the core Metrics Store, Redis cache, and the reporting Analytics DB remain separate responsibilities.
Practical Complexity & Trade-offs
The benefit of this design is clear separation of responsibilities. Assignment stays focused on fast and consistent decisions. Metrics use an asynchronous event path, so analytics work does not slow the real-time assignment path. Idempotency and deduplication reduce problems caused by repeated requests or events. OAuth2/OIDC, JWTs, mTLS, RBAC, scopes, and audit logging provide several layers of access control. The downside is more operational complexity. The Event Bus, Analytics Pipeline, analytics storage, and statistics service must all remain healthy. Reporting can also lag because events are processed asynchronously. The design keeps experiment configuration, metrics data, cache data, and reporting data in separate parts. We accept these extra components because reporting changes should not affect assignment behavior.
Why Interviewers Ask This
Interviewers use this question to test whether you can define clear API boundaries and separate responsibilities correctly. They want sensible REST contracts, correct request and response flows, consistent experiment assignment, and scalable metrics capture. They also evaluate security judgment around JWTs, mTLS, roles, scopes, and audit logging. Most importantly, they want to see whether you can isolate real-time experiment logic from analytical reporting while clearly explaining reliability, data quality, and operational trade-offs.
Interviewer may ask next
How would this design handle a large increase in metrics traffic?
I would keep the assignment and results contracts unchanged and scale the metrics path independently. POST /v1/metrics would continue accepting the same event data. The Metrics Ingest Service would still validate schemas, deduplicate events, buffer and persist data, and handle high throughput. Valid events would continue flowing through the Event Bus to the Analytics Pipeline. Because this path is asynchronous, the analytics work does not need to complete during the original metrics request. The existing Event Bus and stream-processing pipeline provide the separation needed to absorb heavier event traffic. Correctness still depends on schema validation, deduplication, and the immutable event history used for reporting. Security remains unchanged because clients still use HTTPS with JWTs, while the gateway-to-service hop still uses mTLS. The main downside is operational complexity. More traffic can create a larger event backlog, so reporting may become less fresh until the Analytics Pipeline catches up.
How do you stop one product team from accessing another team's experiments?
I would use the existing Identity and Access component for that boundary. The client still reaches the API Gateway using a JWT. OAuth2/OIDC establishes caller identity, while RBAC by team or project controls which team or project that caller may access. Scopes and permissions further limit allowed operations. Those controls apply to POST /v1/assignment, POST /v1/metrics, and GET /v1/experiments/{experimentKey}/results. Service callers can use the service API key mechanism shown in the diagram. Audit logging records access activity so operators can review use of protected platform functions. The Configuration Store still owns experiments, variants, rules, traffic allocation, feature flags, and lifecycle state. Reporting remains on its separate read path. The main downside is administration. Team membership, roles, scopes, permissions, and service credentials must remain accurate, because incorrect access configuration can block valid users or grant access too broadly.
19. Design a recommendation system for content discoveryAPI DesignMediumApple
i Question Details
Describe the request and response contract, the signals the API needs, and how you would expose ranking inputs without leaking sensitive data.
Short Interview Answer (30-60 seconds)
At a high level, I would build a recommendation API that accepts safe user context, behavior signals, ranking preferences, and pagination information. The client sends an HTTPS request with mTLS and a JWT through the API Gateway. The gateway authenticates, authorizes, rate-limits, validates, and protects sensitive data before forwarding the request to the ASP.NET Core Recommendation Service. The service generates candidates, fetches features and content data, ranks items with a versioned model, applies diversity rules, and returns recommendations with a next cursor. The trade-off is stronger personalization at the cost of more data and model infrastructure.
Detailed Explanation
The goal is to help a person find useful content while protecting private information. The client gives the system safe information about the current situation, recent actions, and ranking preferences. The system then chooses useful content and puts the best items first. The main challenge is balancing good personalization with privacy and fast response time. We also need a clear boundary between information clients may send and internal information they must never see. I would explain the design by following the diagram from the client request, through ranking, and back to the response.
Useful Questions to Ask the Interviewer
How many recommendations should one response contain?
How quickly should recent user actions affect recommendations?
Which ranking controls should clients be allowed to change?
What latency target should the online recommendation flow meet?
How to Explain It in an Interview
1. Define the client contract
I would start with the information the recommendation system needs. The client can be mobile, web, or TV. It sends user context, behavior signals, ranking inputs, and pagination information. Context includes a hashed user identity, device or app information, coarse location, session ID, and timestamp. Behavior signals include views, clicks, likes, shares, dwell time, completions, searches, and follow or unfollow actions. Exposed ranking inputs remain high level. They include topic preferences, content type weights, recency preference, diversity level, and include or exclude content IDs. These controls influence ranking without exposing internal feature values or model details.
2. Protect the request at the API Gateway
The API request reaches the API Gateway over HTTPS with mTLS and a JWT. The gateway validates the JWT for authentication. It separately checks scopes and permissions for authorization. It also applies rate limiting and request validation. The PII Guard hashes or redacts sensitive information. The diagram also keeps the protocol boundary at HTTPS or mTLS. If these checks fail, the request should not continue into the Recommendation Service. The diagram does not define specific HTTP error codes, so I would not invent them.
3. Run the ranking pipeline
After validation, the gateway sends the validated request to the Recommendation Service using gRPC or HTTPS. This service is inside the ASP.NET Core application boundary. Candidate Generation first retrieves relevant items. Feature Fetch then gathers user, item, and context features. Ranking scores candidates using the ML model. Post-processing applies diversity and business rules. Response Assembly finally prepares paging information and response metadata. This sequence keeps each ranking responsibility clear and makes the pipeline easier to reason about.
4. Use the supporting data and model services
The Recommendation Service depends on three main supporting components. The Feature Store supplies user, item, and context features. The Content Catalog supplies content metadata, permissions, and availability. The Model Store holds versioned trained models used by the ranking step. These responsibilities stay separate. The public client never receives the internal feature representation or model details. This is important because the diagram explicitly limits storage and processing to non-PII signals.
5. Return the response and emit events
The Recommendation Service sends a JSON response back through the API Gateway. The gateway then returns the HTTPS API response to the client. The response contains recommended items, meta information, and a next cursor. Separately, the Recommendation Service emits user interaction events to the Event Stream, which sends an acknowledgement back. Logging, metrics, and tracing are also supporting flows. They help engineers observe the system, but they are not part of the business response path.
6. Improve ranking offline and explain the trade-off
The offline training pipeline uses event data for Data Ingestion, Data Processing, Model Training, and Model Evaluation. Evaluated models are published as new versions to the Model Store. The online Recommendation Service can then use a trained model version for ranking. This keeps expensive training work outside the live request path. The benefit is safer and more flexible personalization. The downside is more operational complexity because the system now depends on feature storage, content data, event processing, model training, model evaluation, observability, and versioned model publishing.
Practical Complexity & Trade-offs
The benefit of this design is a clear boundary between the public API and the internal ranking system. Clients may send safe ranking preferences, but they do not see private features, raw sensitive data, or model details. Authentication, authorization, validation, rate limiting, and the PII Guard reduce risk before ranking begins. The downside is additional infrastructure. The Recommendation Service depends on a Feature Store, Content Catalog, Model Store, Event Stream, and an offline training pipeline. That makes deployment and troubleshooting harder. Logging, metrics, and tracing help engineers understand problems. Versioned models make model changes easier to control. We accept this extra complexity because it keeps model training away from the live request path while still allowing recommendations to improve over time.
Why Interviewers Ask This
Interviewers want to see whether you can design a clear API boundary for a recommendation feature. They are testing how you model request and response flow, choose useful ranking signals, protect sensitive information, and separate authentication from authorization. They also want to see whether you understand online ranking, asynchronous events, model versioning, observability, scalability, and component ownership. The important skill is explaining sensible engineering trade-offs rather than simply naming technologies.
Interviewer may ask next
What would you change if recommendation traffic increased by ten times?
I would keep the same client contract and ranking pipeline, but scale the online components independently. The client would still send context, behavior signals, ranking inputs, and pagination information through the API Gateway. The gateway would continue authentication, authorization, rate limiting, validation, and PII protection. I would run more Recommendation Service instances so more requests can execute Candidate Generation, Feature Fetch, Ranking, Post-processing, and Response Assembly in parallel. I would also scale the Feature Store and Content Catalog read capacity because the ranking pipeline depends on them. The Model Store must remain able to provide versioned trained models to the service. Event emission stays asynchronous, so the learning path does not become part of the synchronous response. Logging, metrics, and tracing would show which dependency becomes the bottleneck. The main downside is greater infrastructure and operational cost. Scaling only the Recommendation Service would not help if its supporting stores cannot handle the same traffic growth.
How would you expose ranking controls without leaking sensitive data?
I would expose only the coarse ranking inputs already shown in the design. Clients may provide topic preferences, content type weights, recency preference, diversity level, and include or exclude content IDs. They should not receive internal user features, embeddings, raw sensitive attributes, model weights, or other private ranking details. The API Gateway still validates the request and uses the PII Guard to hash or redact sensitive information before forwarding it. Inside the Recommendation Service, Feature Fetch obtains the internal user, item, and context features from the Feature Store. Ranking uses those internal values without returning them to the client. The response contains only recommended items, meta information, and the next cursor. The benefit is useful client control with a smaller privacy and abuse surface. The downside is less visibility into exactly why the model produced a particular score, but that protects internal data and model implementation details.
20. Design Apple Push Notification Service (APNs) at Apple's scaleAPI DesignMediumApple
i Question Details
Explain the service boundary from app server to device token delivery, and cover payload limits, expiry, and push versus polling tradeoffs.
Short Interview Answer (30-60 seconds)
At a high level, I would let the app server submit one notification to APNs and let Apple handle delivery at global scale. The provider sends POST /3/device/<token> over HTTP/2 and TLS. It authenticates with either a provider-token JWT signed with ES256 or a provider certificate. APNs validates, rate-limits, stores, routes, and delivers the notification through a persistent encrypted device connection. The provider receives 200 Accepted or an error response. Push gives low latency and efficient battery use, while polling gives more timing control but uses more network and battery.
Detailed Explanation
The goal is to deliver a small message from an app company to one Apple device. The app company should not keep asking every device whether new information exists. Instead, it sends one message to Apple. Apple finds the correct device and delivers the message when possible. The design must handle very large traffic, reject bad requests, avoid keeping expired messages forever, and return useful errors. I would explain the path shown in the diagram, starting at the app server, moving through APNs, and ending at the Apple device.
Useful Questions to Ask the Interviewer
Should we focus on the provider API and Apple-side delivery path?
Should offline devices receive stored notifications until their expiration time?
Should we compare APNs push with periodic polling by the application?
How to Explain It in an Interview
1. Define the provider API boundary
I would start with Your App Server. The Provider App creates a notification and selects the target device token. It also sets the payload, expiry, priority, and optional collapse ID.
The provider sends POST /3/device/<token> toward APNs. The connection uses HTTP/2 over TLS. Authentication uses either a provider-token JWT signed with ES256 or a provider certificate.
The response travels separately from APNs back to the provider. A successful request receives 200 Accepted. The diagram also shows error responses such as 400, 403, 410, 429, or 5xx, together with an error reason.
2. Receive and protect traffic at the APNs Edge
The request first reaches the APNs Edge through Global Anycast DNS and CDN routing. The edge routes traffic toward a nearby point of presence.
The edge terminates TLS. When certificate-based authentication is used, it validates the client certificate. The request then reaches the Front Door API Gateways.
These gateways perform authentication checks, rate limiting, input validation, and abuse protection. This keeps invalid or excessive traffic away from the deeper APNs services.
3. Validate, accept, and persist the notification
The Push Ingestion Service accepts and persists the valid notification. It performs de-duplication, collapse-by-ID handling, and priority handling.
The Push Queue Store provides durable storage for pending work. Other Persistent Stores hold device tokens, routing state, channel state, and metrics and logs. These stores support later routing and delivery without changing the provider-facing API.
4. Route the notification to the device connection
The Routing & Delivery Service uses device online state, channel routing, and shard mapping to choose the correct delivery path. It then attempts delivery through the Channel Service.
The Channel Service manages long-lived, persistent encrypted APNs device connections. It also handles quality-of-service choices and retry logic.
On the Apple Platform side, the APNs Client on iOS, iPadOS, watchOS, or macOS maintains the APNs connection. It receives the push and delivers it to the application.
5. Explain payload, expiry, and error behavior
For standard remote notifications, the maximum JSON payload is 4 KB, or 4096 bytes. The HTTP/2 headers are separate from this JSON payload limit. The diagram also recommends keeping payloads small.
The provider sets apns-expiration as Unix time. A value of 0 means APNs should deliver immediately or drop the notification if the device is offline. With a future expiration value, APNs may store the notification until that time. After expiration, the push is discarded.
The Response & Error Service handles provider response status, error reasons, 410 Unregistered token handling, and related metrics and alerts.
6. Finish with scale, operations, and push versus polling
The design separates ingestion, routing, channel management, storage, and cross-cutting services. Identity & Security validates provider tokens and certificates and supports key or certificate rotation. Observability collects metrics, logs, traces, dashboards, and anomaly information. Abuse Prevention uses rate limits, reputation scoring, and blacklisting. Global Operations handles regional failover, capacity management, and incident response. Configuration manages feature flags, policies, and dynamic tuning.
Push is the better choice for near-real-time notifications. It gives low latency and efficient battery and bandwidth use. Polling gives the application more control over timing and works through normal outbound requests, but it has higher latency and uses more battery and bandwidth.
Practical Complexity & Trade-offs
The benefit is that the provider has one clear API boundary. It sends a notification to APNs and receives an HTTP/2 response. Apple owns the difficult global delivery work. TLS protects the connection, while the JWT or provider certificate proves the sender's identity. Rate limiting, validation, and abuse controls protect APNs from bad traffic. Durable storage helps when a device is temporarily offline, but expiration prevents old notifications from staying forever. Collapse IDs can replace older pending notifications. Persistent device connections make push fast and efficient. The downside is greater internal complexity because APNs must manage routing, queues, device connections, retries, failures, and regional capacity. Polling gives more timing control, but it increases latency, battery use, and bandwidth use.
Why Interviewers Ask This
Interviewers use this question to test whether you can define a clear API boundary and correctly trace a request and response. They want to see good judgment around HTTP behavior, authentication, validation, rate limiting, durable delivery, failure handling, scalability, and service ownership. They also expect you to understand payload and expiry limits and explain why push is usually better than polling for mobile notifications. The important skill is communicating a defensible design and its trade-offs clearly.
Interviewer may ask next
What happens if a device is offline when APNs receives the notification?
APNs can keep the notification until its expiration time when the provider supplies a future expiration value. The affected path starts at the Push Ingestion Service and durable Push Queue Store. Routing & Delivery and the Channel Service can attempt delivery when the device becomes reachable. The provider controls this behavior with apns-expiration. If the value is 0, APNs should try immediate delivery and drop the notification when the device is offline. If the value is a future Unix time, the notification may remain pending until that time. Once it expires, APNs discards it instead of delivering stale information. A collapse ID can also replace an older pending notification with a newer one. The security model does not change. The provider still uses HTTP/2 over TLS with either a provider-token JWT or provider certificate. The main downside is extra storage, queue management, retry work, and expiration cleanup inside APNs.
Why use APNs push instead of having every app poll its server for updates?
I would prefer APNs push when the application needs timely notifications without frequent background checks. The provider still sends POST /3/device/<token> to APNs, and Apple delivers through the persistent encrypted APNs connection maintained for the device. This avoids each application repeatedly waking up and asking whether something changed. The benefit is lower latency and more efficient battery and bandwidth use. Polling gives the application more control over when it checks, and normal outbound polling requests can work through firewalls. However, frequent polling uses more network and battery. Slower polling reduces that cost but increases delay. The APNs security model stays unchanged. The provider uses HTTP/2 over TLS and authenticates with either a provider-token JWT or provider certificate. The main downside of push is dependence on APNs connectivity and a valid device token, but that trade-off is appropriate for near-real-time mobile notifications.
More questions load as you scroll
Disclaimer: This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.
Company Notice: This guide is an independent educational resource and is not affiliated with, endorsed by, sponsored by, or approved by the company named in this guide. Company names are used only to identify interview experiences commonly reported by candidates. Interview practices can change without notice, and inclusion of company-specific content does not mean these questions are official, complete, or guaranteed to be asked. To the fullest extent permitted by law, the author, contributors, and publisher are not responsible for outcomes related to use of this material.