← Karat Interview Insights

Karat·Software Engineer·Technical Phone Screen·Intermediate

Intermediate
May 2026Remote

Summary

Karat screen for a software engineer role, all TypeScript, all about a little obstacle course race tracker. Three connected problems that built on each other, which I actually appreciated once I stopped panicking about the simulation part.

Questions Asked (3)

Q1

You have a RunCollection class with a broken personalBest() method. Fix it so it only considers complete runs when computing the minimum total time.

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

The bug was subtle enough that I almost missed it.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, clarify what defines a 'complete run' and how total time is computed. Then, modify the personalBest() method to filter out incomplete runs before finding the minimum total time, ensuring edge cases like empty collections are handled.

Pro tip: Mention that you would add unit tests for scenarios with no complete runs, all complete runs, and mixed runs to validate the fix and prevent regressions.

1. Understand the requirements

Ask clarifying questions to confirm the definition of a complete run and how total time is calculated (e.g., sum of split times).

2. Review the existing code

Examine the RunCollection class and personalBest() method to identify why it currently includes incomplete runs.

3. Implement the fix

Modify personalBest() to filter runs based on completeness before computing the minimum total time, using a stream or loop with a condition.

4. Handle edge cases

Consider empty collections, no complete runs, and ties in total time; decide on appropriate return values or exceptions.

5. Test and validate

Write or describe unit tests covering various scenarios to ensure the fix works correctly and doesn't break existing functionality.

Key Points to Mention

  • Definition of a complete run (e.g., all splits recorded, non-null fields).
  • Filtering logic: use of stream filter or conditional check.
  • Time complexity: O(n) to iterate through runs, acceptable for typical collection sizes.
  • Edge cases: empty collection, no complete runs, multiple runs with same minimum time.
  • Readability and maintainability: clear variable names, separation of concerns.
  • Testing strategy: unit tests for different scenarios.

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

Q2

Implement bestOfBests(): for each obstacle position, find the minimum time recorded across all runs (including incomplete ones), then return the sum of those per-obstacle minimums.

Algorithms & Data Structures
Author's notes

This one clicked pretty fast.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, clarify the input structure: a list of runs, each with a list of times per obstacle, where incomplete runs may have null or missing values. Then, for each obstacle index, compute the minimum time across all runs, ignoring nulls, and sum these minimums. Handle edge cases like all nulls for an obstacle or empty input.

Pro tip: Proactively discuss how to handle incomplete runs: if a run has no time for an obstacle, skip it; but if all runs have no time for an obstacle, decide whether to treat it as 0 or raise an error—clarify with the interviewer.

1. Clarify input format and constraints

Ask the interviewer to confirm the data structure (e.g., list of runs, each run is a list of times with null for incomplete) and any constraints on number of runs/obstacles.

2. Initialize per-obstacle minimums

Create an array to store the minimum time for each obstacle, initialized to infinity (or a large value) to handle comparisons.

3. Iterate through runs and update minimums

For each run, iterate through its times; if a time is not null, update the corresponding obstacle's minimum if the time is smaller.

4. Sum the minimums and handle edge cases

After processing all runs, sum the minimums. If any obstacle has no valid time (still infinity), decide on a fallback (e.g., 0 or error) based on clarification.

5. Analyze complexity and test

State time complexity O(R*O) and space O(O). Walk through a small example to verify correctness.

Key Points to Mention

  • Handling incomplete runs: null or missing times should be ignored when computing minimums.
  • Edge case: all runs incomplete for an obstacle—decide whether to treat as 0 or raise an error.
  • Time and space complexity: O(R*O) time, O(O) space, where R is number of runs and O is number of obstacles.
  • Data structure choice: array for per-obstacle minimums, initialized to infinity.
  • Single-pass approach: update minimums while iterating through runs.
  • Testing with examples: include cases with nulls, all nulls, and empty input.

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

Q3

Implement chanceOfPersonalBest(inProgressRun) using Monte Carlo simulation with 10,000 trials. For each remaining obstacle, sample uniformly from historical times at that position, simulate a finish, and return the fraction of trials that beat or match the personal best.

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

I blanked for a second on the sampling part.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Clarify the problem inputs and assumptions, then outline a Monte Carlo simulation: for each trial, sample times for each remaining obstacle from historical data, sum with the in-progress time, and compare to the personal best. Finally, return the fraction of trials that meet or beat the personal best, ensuring the simulation is efficient and correct.

Pro tip: Mention that you would pre-process the historical times into arrays for O(1) random access and use a fixed random seed for reproducibility during testing, while noting that in production a seed might not be used.

1. Clarify inputs and assumptions

Confirm the structure of inProgressRun (e.g., elapsed time, obstacles completed, remaining obstacles) and the historical data format (e.g., list of times per obstacle position). Ask about edge cases like no remaining obstacles or missing historical data.

2. Design the simulation loop

For each of the 10,000 trials, initialize total time with the in-progress elapsed time. For each remaining obstacle, randomly select a historical time uniformly from the available samples for that obstacle position and add it to the total.

3. Compare and count successes

After summing all remaining obstacle times, compare the trial's total time to the personal best. If it is less than or equal to the personal best, increment a success counter.

4. Compute and return the probability

Divide the success counter by the total number of trials (10,000) to get the fraction, and return that as the chance of achieving a personal best.

5. Discuss optimizations and trade-offs

Mention potential optimizations like pre-sampling or vectorization, and discuss the trade-off between simulation accuracy and computational cost, especially if the number of trials or obstacles is large.

Key Points to Mention

  • Uniform sampling from historical times for each obstacle position
  • Monte Carlo simulation with a fixed number of trials (10,000) for statistical stability
  • Handling edge cases: no remaining obstacles, empty historical data, or personal best already beaten
  • Time complexity: O(T * R) where T is trials and R is remaining obstacles, and potential optimizations
  • Use of random number generation and reproducibility (e.g., seeding for tests)
  • Definition of 'beat or match' personal best: total time <= personal best

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