← Affirm Interview Insights

Affirm·Software Engineer·Technical Phone Screen·Intermediate

Intermediate
May 2026

Summary

Affirm software engineer interview with a meaty filtering function problem that had a bunch of edge cases, plus a debugging section where you had to spot subtle Python bugs. The kind of question that looks straightforward until you actually read the requirements carefully.

Questions Asked (4)

Q1

Implement a filter_events function that filters a list of event dictionaries by type inclusion/exclusion, timestamp range, deduplication by id (keeping the latest ts), and a limit on results. Results should be sorted by timestamp ascending with stable ordering on ties.

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

This one had way more moving parts than I expected.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying requirements and edge cases, then outline a pipeline: filter by type and timestamp, deduplicate by id keeping the latest timestamp, sort by timestamp ascending with stable tie-breaking, and finally apply the limit. Discuss time/space complexity and potential optimizations, and consider trade-offs between clarity and performance.

Pro tip: Mention that Python's sort is stable, so if you deduplicate by keeping the latest timestamp and then sort by timestamp, ties will preserve the original relative order of the deduplicated events, which satisfies stable ordering. Also, clarify whether the limit should be applied before or after deduplication and sorting, as it affects results.

1. Clarify requirements and edge cases

Ask about input format, expected output, handling of missing fields, duplicate ids with same timestamp, and whether the limit applies before or after deduplication and sorting.

2. Design the filtering pipeline

Outline the sequence: filter by type inclusion/exclusion, filter by timestamp range, deduplicate by id keeping the latest timestamp, sort by timestamp ascending with stable tie-breaking, then apply limit.

3. Implement deduplication and sorting

Use a dictionary to track the latest event per id, then sort the deduplicated events by timestamp. Leverage stable sort to maintain original order for ties, or explicitly use a secondary key if needed.

4. Analyze complexity and trade-offs

Discuss time complexity (O(n log n) due to sorting) and space complexity (O(n) for deduplication). Consider alternatives like sorting first then deduplicating, or using a heap for limit if limit is small.

5. Test with edge cases

Walk through examples: empty list, all filtered out, duplicate ids with different timestamps, ties in timestamps, and limit larger than result size.

Key Points to Mention

  • Use a dictionary to deduplicate by id, keeping the event with the maximum timestamp.
  • Python's sort is stable, so sorting by timestamp after deduplication preserves original order for ties.
  • Time complexity: O(n log n) due to sorting; space complexity: O(n) for deduplication.
  • Apply the limit after sorting to get the earliest events, unless specified otherwise.
  • Handle edge cases: missing fields, empty input, and duplicate ids with identical timestamps.
  • Consider trade-offs: if limit is small, a heap could reduce time to O(n log k), but sorting is simpler.

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

Q2

What is the time and space complexity of your filter_events implementation, and how would you optimize it if the input is already sorted by timestamp?

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

Talked through O(n log n) for the sort dominating everything else, O(n) space for the dedup dict.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clearly stating the time and space complexity of your current implementation, then explain how you would leverage the sorted input to optimize. Focus on reducing time complexity from O(n log n) to O(n) or O(log n) depending on the operation, and discuss trade-offs such as space usage and code complexity.

Pro tip: Mention that if the input is sorted, you can use binary search for point queries or two-pointer techniques for range queries, but also consider whether the sorted property is guaranteed and how to handle edge cases like duplicates or unsorted data.

1. State current complexity

Clearly articulate the time and space complexity of your existing filter_events implementation, assuming it uses a hash map or sorting. For example, O(n) time and O(n) space if using a hash map, or O(n log n) time and O(n) space if sorting.

2. Identify optimization opportunities

Explain that if the input is already sorted by timestamp, you can avoid sorting and use more efficient algorithms. For filtering events within a time range, use binary search to find the start and end indices, achieving O(log n) time for the search plus O(k) for output.

3. Describe optimized approach

Detail the optimized algorithm: for a range query, perform two binary searches to find the first and last event within the range, then return the slice. For multiple queries, consider pre-processing or using a two-pointer approach if queries are also sorted.

4. Analyze new complexity

State the new time complexity: O(log n + k) where k is the number of events in the range, and space complexity O(k) for the output (or O(1) extra space if returning a view). Compare with the original complexity.

5. Discuss trade-offs and edge cases

Mention trade-offs: binary search requires random access, so if the data is a linked list, it's not efficient. Also, consider if the sorted property is guaranteed, and how to handle duplicates or if the input might be unsorted. Discuss whether to modify the original data or create a new list.

Key Points to Mention

  • Time complexity of original implementation (e.g., O(n) with hash map or O(n log n) with sorting)
  • Space complexity of original implementation (e.g., O(n) for hash map or sorted copy)
  • Binary search for range queries on sorted data: O(log n) to find boundaries
  • Two-pointer technique for merging or filtering multiple sorted lists
  • Trade-offs: binary search requires random access (arrays), not suitable for linked lists
  • Edge cases: duplicates, empty input, range not found, and whether sorted property is guaranteed

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

Q3

Find and fix the bug in this snippet: `def filter_types(events, t): return [e for e in events if e['type'] is t]`. What test would catch it?

Algorithms & Data Structures
Author's notes

The `is` vs `==` thing.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Identify the bug as the use of `is` for value comparison instead of `==`, explain why this fails for non-interned strings, and propose a test that uses dynamically constructed strings to expose the issue. Then discuss the fix and any edge cases.

Pro tip: Mention that `is` checks identity, not equality, and that relying on string interning is a common pitfall; this shows you understand Python's memory model and write robust code.

1. Identify the bug

Spot that `is` is used to compare the event type to `t`, which checks object identity rather than value equality.

2. Explain the impact

Describe how this can lead to incorrect filtering when strings are not interned, such as when they are constructed at runtime.

3. Propose a test

Design a test that creates a string dynamically (e.g., via concatenation or user input) and verifies that the filter returns the expected events.

4. Provide the fix

Change the comparison to use `==` to compare string values, ensuring correct behavior regardless of interning.

5. Discuss edge cases

Consider other types (e.g., integers) and whether `is` might work there, but emphasize that `==` is the correct general solution.

Key Points to Mention

  • Difference between `is` (identity) and `==` (equality) in Python.
  • String interning: why `is` sometimes works for short strings but is unreliable.
  • How to construct a test that fails with `is` but passes with `==` (e.g., using `''.join` or `str()`).
  • The fix: replace `is` with `==`.
  • Potential performance considerations: `==` is slightly slower but necessary for correctness.
  • General principle: use `is` only for singletons like `None`, `True`, `False`.

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

Q4

Find and fix the bug in this snippet: `def filter_events(events, include=set()): return [e for e in events if e['type'] in include]`. What test would catch it?

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

Mutable default argument, classic Python footgun.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Identify the mutable default argument bug in the function signature, explain why it's problematic, and propose a fix using None as the default. Then describe a test that calls the function twice with different include sets to demonstrate the bug.

Pro tip: Mention that mutable default arguments are evaluated once at function definition, so the same set object is reused across calls, leading to unexpected accumulation of elements. This shows deep understanding of Python's evaluation model.

1. Identify the bug

Spot that the default argument include=set() is a mutable object, which is shared across all calls to the function.

2. Explain the impact

Describe how modifications to the default set persist between calls, causing incorrect filtering when the default is used.

3. Propose a fix

Change the default to None and inside the function set include = include or set() to create a new set each call.

4. Design a test

Write a test that calls filter_events twice without providing include, and verifies that the second call does not include elements from the first call's include set.

5. Discuss trade-offs

Mention that using None as default is a common Python idiom, and that immutable defaults (like tuples) could also be used if appropriate.

Key Points to Mention

  • Mutable default arguments are evaluated once at function definition time.
  • The default set is shared across all calls, leading to state leakage.
  • Using None as a sentinel value and creating a new set inside the function fixes the issue.
  • A test that calls the function multiple times with different inputs can catch the bug.
  • This is a common Python gotcha and best practice is to avoid mutable defaults.
  • Consider using type hints or linters to catch such issues early.

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