← Uber Interview Insights

Uber·Software Engineer·Technical Phone Screen·Senior

Senior
May 2026

Summary

Uber SWE interview that was basically one big deep-dive into building a parser and evaluator from scratch. More involved than I expected for a single session, they really wanted to see both a recursive and an iterative approach plus full error handling.

Questions Asked (4)

Q1

Design and implement a parser and evaluator for a parenthesized expression language that supports sum, prod, and set operators with lexical scoping. The input may be invalid, and your implementation must detect and report errors like mismatched parentheses, wrong arity, undefined variables, and integer overflow.

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

This was the whole interview basically.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the grammar and semantics, then outline a two-phase approach: a recursive descent parser that builds an AST and an evaluator that walks the AST with an environment stack for lexical scoping. Emphasize robust error handling with specific error types and discuss trade-offs like using exceptions vs. error returns and handling integer overflow with checked arithmetic.

Pro tip: Proactively discuss how you would test the parser and evaluator, including property-based testing for round-trip parsing and fuzzing for error cases. Also, mention that you would separate parsing from evaluation to allow for optimizations like constant folding or lazy evaluation.

1. Clarify Requirements and Grammar

Ask questions to pin down the exact syntax, operator precedence, associativity, and scoping rules. Define the grammar formally, including how variables are declared and referenced.

2. Design the Parser

Choose a parsing strategy (e.g., recursive descent) and outline the AST structure. Explain how to handle errors like mismatched parentheses and wrong arity during parsing.

3. Design the Evaluator

Describe the evaluation process with an environment stack for lexical scoping. Detail how to detect undefined variables and integer overflow, and how to propagate errors.

4. Discuss Error Handling and Edge Cases

Enumerate specific error conditions and how they are reported. Discuss strategies for error recovery or fail-fast behavior, and how to provide meaningful error messages.

5. Analyze Trade-offs and Extensions

Compare implementation choices (e.g., recursive descent vs. parser combinators, exceptions vs. result types). Mention potential optimizations and how the design supports them.

Key Points to Mention

  • Recursive descent parsing with operator precedence and associativity handling.
  • AST design with nodes for sum, prod, set, and variable references.
  • Environment stack for lexical scoping, with proper variable shadowing.
  • Error detection: mismatched parentheses, wrong arity, undefined variables, integer overflow.
  • Use of checked arithmetic or big integers to handle overflow.
  • Testing strategies: unit tests for each error case, property-based testing for valid expressions.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

Walk through two implementation approaches for this evaluator: recursive descent versus an iterative stack-based approach. Compare their time and space complexity in terms of the input length.

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

I fumbled this a bit.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clearly defining the evaluator's grammar and input, then describe the recursive descent approach with its call stack, followed by the iterative stack-based approach using an explicit stack. Compare their time and space complexities, highlighting that both are O(n) time but recursive descent uses O(d) stack space where d is nesting depth, while iterative uses O(n) explicit stack space in the worst case.

Pro tip: Mention that recursive descent is more readable and easier to extend for complex grammars, but iterative avoids stack overflow risks on deeply nested inputs—a critical consideration for production systems at scale like Uber's.

1. Define the problem and assumptions

Briefly state the evaluator's input (e.g., expression string) and grammar, and clarify that n is the input length. Assume a simple expression grammar for illustration.

2. Explain recursive descent

Describe how recursive descent uses mutually recursive functions for each grammar rule, with the call stack implicitly managing state. Mention that it's top-down and naturally handles nested structures.

3. Explain iterative stack-based approach

Describe how an explicit stack replaces the call stack, often using a shunting-yard or operator-precedence algorithm. State that it processes tokens linearly and manages state manually.

4. Compare time complexity

Both approaches typically run in O(n) time because each token is processed a constant number of times. Note that recursive descent may have overhead from function calls but remains linear.

5. Compare space complexity

Recursive descent uses O(d) space for the call stack, where d is the maximum nesting depth (d ≤ n). Iterative uses O(n) space for the explicit stack in the worst case (e.g., deeply nested expressions). Both are O(n) worst-case, but iterative can be more predictable.

Key Points to Mention

  • Time complexity: both are O(n) because each token is processed once.
  • Space complexity: recursive descent uses O(d) call stack space; iterative uses O(n) explicit stack space.
  • Recursive descent is more readable and easier to implement for complex grammars.
  • Iterative approach avoids stack overflow and is safer for deeply nested inputs.
  • Trade-off: recursive descent may be slower due to function call overhead; iterative may be more complex to code.
  • Mention that in practice, for typical expressions, d is small, so recursive descent is often fine.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

How would you extend the set operator to support multiple variable bindings in a single expression, like (set x 1 y 2 body)?

Algorithms & Data StructuresSystem Design
Author's notes

Follow-up that came at the end.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Clarify the semantics of the extended set operator, then propose a design that parses multiple bindings into a sequential or simultaneous binding structure. Discuss implementation strategies such as recursive expansion or environment chaining, and analyze trade-offs in evaluation order, scoping, and performance.

Pro tip: Demonstrate awareness of evaluation order and scoping subtleties (e.g., whether bindings are sequential like let* or parallel like let) and mention how this affects the implementation and user expectations.

1. Clarify Requirements and Semantics

Ask whether bindings should be evaluated sequentially (like let*) or in parallel (like let), and whether the body can reference all variables. Confirm the expected syntax and behavior.

2. Design the Parsing and Representation

Propose a parser change to recognize alternating variable-expression pairs, and represent them as a list of bindings. Consider using an intermediate AST node for multiple bindings.

3. Choose an Implementation Strategy

Decide between recursive expansion into nested single-binding set expressions or direct environment manipulation. Discuss pros and cons of each approach.

4. Handle Evaluation and Scoping

Ensure correct evaluation order and scoping rules. If sequential, evaluate each expression in the environment extended by previous bindings; if parallel, evaluate all in the original environment.

5. Analyze Trade-offs and Edge Cases

Discuss performance implications (e.g., repeated environment copying), error handling for odd number of arguments, and potential optimizations like environment chaining.

Key Points to Mention

  • Sequential vs. parallel binding semantics and their impact on evaluation order.
  • Recursive expansion into nested single-binding set expressions as a simple implementation.
  • Direct environment extension with a new frame to avoid repeated copying.
  • Parsing changes to handle multiple variable-expression pairs.
  • Error handling for malformed input (e.g., odd number of arguments).
  • Performance considerations and potential optimizations.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

How would you add a division operator with integer truncation toward zero and proper division-by-zero error handling?

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

Pretty quick follow-up.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the requirements: integer division truncating toward zero, and handling division by zero. Then outline the algorithm: check for zero divisor, handle signs separately, compute absolute quotient, and apply sign. Finally, discuss edge cases like overflow and performance.

Pro tip: Mention that many languages (e.g., C, Java) already truncate toward zero, but if implementing from scratch, use absolute values and adjust sign. Also, consider using exceptions or error codes for division by zero based on context.

1. Clarify Requirements

Confirm that division should truncate toward zero (e.g., -7/2 = -3) and that division by zero should be handled explicitly, perhaps by throwing an exception or returning an error.

2. Design the Algorithm

Outline steps: check divisor for zero, determine sign of result, compute quotient using absolute values, then apply sign. Consider using bitwise operations for efficiency if needed.

3. Handle Edge Cases

Address overflow (e.g., INT_MIN / -1), negative numbers, and zero dividend. Discuss how to handle division by zero in different contexts (e.g., throw ArithmeticException).

4. Implement and Test

Write pseudocode or actual code, then walk through test cases: positive/negative combinations, zero dividend, division by zero, and overflow scenarios.

5. Discuss Trade-offs

Compare approaches: using built-in operators vs. manual implementation, performance implications, and error handling strategies (exceptions vs. error codes).

Key Points to Mention

  • Truncation toward zero means discarding the fractional part, so -7/2 = -3, not -4.
  • Division by zero must be checked before performing the division to avoid runtime errors.
  • Sign handling: compute quotient of absolute values, then apply negative sign if exactly one operand is negative.
  • Overflow: INT_MIN / -1 overflows in two's complement; handle by returning INT_MIN or throwing an exception.
  • Performance: manual division using subtraction or bit shifts may be slower than hardware division; use built-in when possible.
  • Error handling: choose between exceptions (e.g., ArithmeticException) or error codes based on language and API design.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.