← Headway Interview Insights

Headway·Software Engineer·Technical Phone Screen·Intermediate

Intermediate
May 2026

Summary

Coding round at Headway for a software engineer role. The problem was built around an obstacle course race tracker, and you had to debug a broken implementation, then add two new methods, then talk through testing and code quality. Pretty involved for a single session.

Questions Asked (4)

Q1

There's a bug in this RunCollection implementation that's causing a test to fail. Find and fix it.

Root Cause AnalysisAlgorithms & Data Structures
Author's notes

Debugging someone else's code under time pressure is its own skill and I don't think I'm great at it.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by reading the failing test to understand the expected behavior, then trace through the RunCollection implementation to identify where it deviates. Use a systematic debugging approach: reproduce the failure, isolate the faulty logic, and verify the fix with the test.

Pro tip: Before diving into code, articulate your hypothesis about the bug based on the test failure and the code's purpose. This demonstrates structured thinking and often leads to faster resolution.

1. Understand the expected behavior

Read the failing test and any relevant documentation to determine what RunCollection should do. Identify the specific input and expected output that fails.

2. Trace the execution path

Walk through the RunCollection code with the test input, either mentally or with a debugger, to see where the actual behavior diverges from the expected.

3. Identify the root cause

Pinpoint the exact line(s) causing the bug, considering edge cases like empty collections, null values, or incorrect iteration logic.

4. Implement and verify the fix

Make the minimal change needed to correct the logic, then run the test to confirm it passes and check for regressions.

Key Points to Mention

  • Reproducing the failure first to confirm the bug
  • Using the test as a specification for correct behavior
  • Checking for off-by-one errors or incorrect loop conditions
  • Considering edge cases such as empty inputs or null values
  • Verifying the fix with the test and ensuring no other tests break
  • Communicating the root cause clearly and concisely

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

Q2

Implement a best_of_bests() method that computes the theoretically fastest possible run time by taking the minimum recorded time for each obstacle index across all runs, including incomplete ones.

Algorithms & Data StructuresData Modeling
Author's notes

The definition sounds clean until you realize incomplete runs are in scope.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Clarify the data model: each run has a list of times per obstacle index, and incomplete runs may have missing entries. Then iterate through all runs, maintaining a dictionary mapping obstacle index to the minimum time seen so far, ignoring missing values. Finally, return the dictionary or list of minimum times per obstacle.

Pro tip: Discuss how to handle incomplete runs: missing times should be skipped, not treated as infinity or zero. Also mention that if an obstacle has no recorded time across all runs, it should be excluded or flagged, depending on requirements.

1. Clarify requirements and data structure

Ask about the input format: are runs objects with a list of times? How are incomplete runs represented (e.g., null, missing index)? Confirm output format (dictionary, list, etc.).

2. Initialize a result container

Use a dictionary to map obstacle index to the minimum time. Alternatively, if obstacle indices are contiguous and known, use an array initialized to infinity.

3. Iterate through runs and update minimums

For each run, iterate through its recorded times. For each obstacle index with a non-null time, update the minimum if the current time is smaller.

4. Handle edge cases and finalize

Decide how to handle obstacles with no recorded times (exclude or set to null). Return the result in the required format.

Key Points to Mention

  • Time complexity: O(N*M) where N is number of runs and M is number of obstacles per run, or O(total recorded times).
  • Space complexity: O(K) where K is number of distinct obstacle indices.
  • Handling incomplete runs: skip missing entries rather than treating as zero or infinity.
  • Data modeling: consider whether obstacle indices are 0-based or 1-based, and if they are contiguous.
  • Edge cases: no runs, all runs incomplete, obstacle with no times.
  • Potential optimization: if runs are sorted by time, but generally not needed.

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

Q3

Implement chance_of_personal_best(test_run) using a Monte Carlo simulation over 10,000 trials, sampling remaining obstacle times from historical data and returning the fraction of trials that beat the current personal best.

A/B Testing & ExperimentationAlgorithms & Data StructuresTechnical Trade-offs
Author's notes

This was the most interesting part.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the inputs: test_run contains current personal best time and remaining obstacles with their historical time distributions. Then outline a Monte Carlo simulation: for each of 10,000 trials, sample a time for each remaining obstacle from its historical data, sum them with the time already elapsed, and count how many trials result in a total time less than the personal best. Finally, return the fraction of successful trials.

Pro tip: Mention that you would use vectorized operations (e.g., NumPy) to efficiently run 10,000 trials, and discuss how to handle edge cases like missing historical data or obstacles with no samples.

1. Clarify inputs and assumptions

Confirm the structure of test_run: current elapsed time, personal best time, and remaining obstacles with their historical time samples. Ask about the source of historical data and whether obstacles are independent.

2. Design the simulation loop

For each trial, sample a time for each remaining obstacle from its historical distribution (e.g., by bootstrapping from recorded times). Sum these with the elapsed time to get a simulated total time.

3. Run trials and count successes

Repeat the sampling 10,000 times, counting how many simulated total times are less than the personal best. This count divided by 10,000 gives the estimated probability.

4. Optimize and handle edge cases

Use vectorized operations for speed. Address cases where historical data is insufficient (e.g., fallback to global average or skip obstacle) and ensure reproducibility with a random seed if needed.

5. Return and interpret the result

Return the fraction as a float between 0 and 1. Optionally, discuss confidence intervals or the impact of sample size on estimate stability.

Key Points to Mention

  • Monte Carlo simulation basics: repeated random sampling to estimate probabilities.
  • Bootstrapping from historical data to model obstacle time distributions.
  • Independence assumption for obstacle times and its potential limitations.
  • Efficiency considerations: vectorization, avoiding nested loops, and time complexity.
  • Edge cases: missing historical data, obstacles with zero variance, and personal best already beaten.
  • Interpretation of the result: probability as a fraction, and its use in decision-making (e.g., pacing strategy).

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

Q4

What additional tests would you write beyond the ones already provided, and what improvements would you make to the codebase after implementing these methods?

Technical Trade-offsSystem Design
Author's notes

Talked through edge cases like no complete runs when calling personal_best, obstacle index out of range for sampling, and what happens if best_of_bests is called on an empty collection.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the existing tests and code context, then propose additional tests that cover edge cases, error conditions, and integration points. Finally, discuss codebase improvements such as refactoring for testability, enhancing observability, and addressing technical debt revealed by the new tests.

Pro tip: Frame your answer around risk mitigation and long-term maintainability, not just test coverage metrics. Show that you prioritize tests that catch real-world failures and improvements that reduce future debugging time.

1. Clarify the current state

Ask about or infer the existing test coverage, code structure, and known pain points to tailor your suggestions. This demonstrates you don't propose changes blindly.

2. Identify gaps and risks

Analyze what scenarios are untested: edge cases, error handling, concurrency, performance, and integration with external systems. Prioritize tests based on business impact and likelihood of failure.

3. Propose additional tests

List specific tests you would add, such as property-based tests, contract tests, or chaos experiments, and explain how they address the identified gaps.

4. Suggest codebase improvements

Recommend refactoring for testability (e.g., dependency injection, pure functions), improving error messages, adding logging/metrics, and updating documentation.

5. Prioritize and sequence

Explain how you would prioritize these changes based on effort, impact, and dependencies, and how you would measure success (e.g., reduced bug reports, faster CI).

Key Points to Mention

  • Edge cases and error conditions (null inputs, timeouts, partial failures)
  • Non-functional tests: performance, security, and load testing
  • Test maintainability: avoiding flaky tests, using test doubles appropriately
  • Refactoring for testability: dependency injection, separation of concerns
  • Observability improvements: structured logging, metrics, tracing
  • Technical debt and documentation updates

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