Fix a Unique-Entity Counter, Then Flag Suspicious Events in an Event Stream
Quick Overview
A two-part stream-processing exercise: debug a function that counts unique entities in an event stream, then implement a detector that stores flag events and decides whether later data events are suspicious from their key-value pairs. It tests systematic debugging, state design, clarifying rules and code maintainability.
Fix a Unique-Entity Counter, Then Flag Suspicious Events in an Event Stream
Company: Affirm
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: easy
Interview Round: Technical Screen
This exercise has two parts that share one background: a stream of events. First you debug an existing function that counts the unique entities in the stream; then you implement a method that detects suspicious data in the same stream. Each part comes with test cases you can run to check your code, and after each part you are asked how you would keep the code maintainable.
The original code and event format were not reported. The format and the buggy function below are illustrative stand-ins written for practice; they keep the reported shape of the exercise.
```text
Event format (illustrative): every event is a dict with a "type" field.
flag event: {"type": "flag", "key": "ip", "value": "10.0.0.2"}
data event: {"type": "data", "user_id": "u3", "ip": "10.0.0.2"}
Sample stream:
1 {"type": "data", "user_id": "u1", "ip": "10.0.0.1"}
2 {"type": "data", "user_id": "u2", "ip": "10.0.0.2"}
3 {"type": "flag", "key": "ip", "value": "10.0.0.2"}
4 {"type": "data", "user_id": "u1", "ip": "10.0.0.3"}
5 {"type": "data", "ip": "10.0.0.4"}
6 {"type": "data", "user_id": "u3", "ip": "10.0.0.2"}
7 {"type": "data", "user_id": "u3", "ip": "10.0.0.9"}
```
### Clarifying Questions
- Can an event arrive with a type other than flag or data, and should it be ignored or rejected?
- Is the stream a finite list or an unbounded feed, and must a decision be produced as each event arrives?
- Are ids and values compared exactly, or should case and surrounding whitespace be normalized?
### Part 1 — Find and fix the bugs
The function below should return the number of distinct entities in the stream, where an entity is a distinct `user_id` among data events. Flag events are not entities, and data events with no `user_id` are skipped. For the sample stream the expected answer is `3` (`u1`, `u2` and `u3`), but the function returns `1`. Find every defect, fix it, and explain how you found it.
```python
def count_unique_entities(events):
seen = []
count = 0
for i in range(1, len(events)):
event = events[i]
if event["type"] == "flag":
break
uid = event.get("user_id")
if uid not in seen:
count += 1
seen.append(uid)
return count
```
```hint Let the samples localize it
Reduce the failing sample to the smallest stream that still gives a wrong count, then change a single event at a time and note which change moves the result. Re-run the full sample after every fix instead of stopping at the first one.
```
#### What This Part Should Cover
- Every defect, and the evidence that exposed each one
- A fix that matches the stated semantics, with its time and memory cost
- Tests that would have caught each defect
### Part 2 — Detect suspicious data
Implement a detector that processes the same stream one event at a time and decides whether each data event is suspicious. Event types are handled differently. A flag event is stored and used when processing later events. For a data event, its key-value pairs are checked against what has been stored to decide whether it is suspicious, and its other key-value pairs are also stored and used, together with the flags, when processing later events.
```python
class SuspiciousDetector:
def process(self, event: dict) -> bool:
"""Return True if event is a suspicious data event; False for flag events and clean data."""
```
For the sample stream, events 1 to 5 are not suspicious, and event 6 is suspicious because its `ip` was flagged by event 3. A flag applies only to later events: event 2 used the same `ip` before it was flagged, so it stays clean. Event 7 shares only its `user_id` with the suspicious event 6; whether it is suspicious depends on the storage policy you clarify below.
```hint Model what is remembered
Decide what a stored entry is (a key and a value, or a value alone) and what a single lookup per field of an incoming event looks like.
```
#### Clarifying Questions for this Part
- Which of a data event's other key-value pairs are stored: those of every data event, or only those of events found suspicious? Is event 7 suspicious?
- Does a stored value match only under the same key (an `ip` value flags only `ip` fields), or under any key?
- Can a flag be removed or expire, and should a flag ever affect events processed before it arrived?
#### What This Part Should Cover
- A state model for flags and stored values with constant-time lookups per field
- Separation of event-type dispatch from the suspicion rule
- Behavior under each clarified storage policy, shown on the sample stream
- How the stored state grows over a long stream
### What a Strong Answer Covers
- Systematic debugging driven by the provided samples and tests rather than guesswork
- A corrected counter that matches the stated entity semantics on every input, with its cost analyzed
- Clarifies the storage policy and matching rules before implementing the detector
- A structure in which a new event type or rule can be added without rewriting the dispatch
- Concrete maintainability answers: tests, naming, structure and bounded state
### Follow-up Questions
- How would you change the code so that a new event type, for example one that clears a flag, can be added without touching the existing handlers?
- The stream runs for months. How do you keep the stored state from growing without bound, and what do you give up?
- Two workers now process the stream in parallel. What breaks in your detector, and how would you split the work?
- What would you put in place so that the next engineer who edits the suspicion rule cannot silently change which events are flagged?
Overview: A two-part stream-processing exercise: debug a function that counts unique entities in an event stream, then implement a detector that stores flag events and decides whether later data events are suspicious from their key-value pairs. It tests systematic debugging, state design, clarifying rules and code maintainability.
This exercise has two parts that share one background: a stream of events. First you debug an existing function that counts the unique entities in the stream; then you implement a method that detects suspicious data in the same stream. Each part comes with test cases you can run to check your code, and after each part you are asked how you would keep the code maintainable.
The original code and event format were not reported. The format and the buggy function below are illustrative stand-ins written for practice; they keep the reported shape of the exercise.
Event format (illustrative): every event is a dict with a "type" field.
flag event: {"type": "flag", "key": "ip", "value": "10.0.0.2"}
data event: {"type": "data", "user_id": "u3", "ip": "10.0.0.2"}
Sample stream:
1 {"type": "data", "user_id": "u1", "ip": "10.0.0.1"}
2 {"type": "data", "user_id": "u2", "ip": "10.0.0.2"}
3 {"type": "flag", "key": "ip", "value": "10.0.0.2"}
4 {"type": "data", "user_id": "u1", "ip": "10.0.0.3"}
5 {"type": "data", "ip": "10.0.0.4"}
6 {"type": "data", "user_id": "u3", "ip": "10.0.0.2"}
7 {"type": "data", "user_id": "u3", "ip": "10.0.0.9"}
Clarifying Questions Guidance
Can an event arrive with a type other than flag or data, and should it be ignored or rejected?
Is the stream a finite list or an unbounded feed, and must a decision be produced as each event arrives?
Are ids and values compared exactly, or should case and surrounding whitespace be normalized?
Part 1 — Find and fix the bugs
The function below should return the number of distinct entities in the stream, where an entity is a distinct user_id among data events. Flag events are not entities, and data events with no user_id are skipped. For the sample stream the expected answer is 3 (u1, u2 and u3), but the function returns 1. Find every defect, fix it, and explain how you found it.
def count_unique_entities(events):
seen = []
count = 0
for i in range(1, len(events)):
event = events[i]
if event["type"] == "flag":
break
uid = event.get("user_id")
if uid not in seen:
count += 1
seen.append(uid)
return count
What This Part Should Cover Guidance
Every defect, and the evidence that exposed each one
A fix that matches the stated semantics, with its time and memory cost
Tests that would have caught each defect
Part 2 — Detect suspicious data
Implement a detector that processes the same stream one event at a time and decides whether each data event is suspicious. Event types are handled differently. A flag event is stored and used when processing later events. For a data event, its key-value pairs are checked against what has been stored to decide whether it is suspicious, and its other key-value pairs are also stored and used, together with the flags, when processing later events.
class SuspiciousDetector:
def process(self, event: dict) -> bool:
"""Return True if event is a suspicious data event; False for flag events and clean data."""
For the sample stream, events 1 to 5 are not suspicious, and event 6 is suspicious because its ip was flagged by event 3. A flag applies only to later events: event 2 used the same ip before it was flagged, so it stays clean. Event 7 shares only its user_id with the suspicious event 6; whether it is suspicious depends on the storage policy you clarify below.
Clarifying Questions for this Part Guidance
Which of a data event's other key-value pairs are stored: those of every data event, or only those of events found suspicious? Is event 7 suspicious?
Does a stored value match only under the same key (an
ip
value flags only
ip
fields), or under any key?
Can a flag be removed or expire, and should a flag ever affect events processed before it arrived?
What This Part Should Cover Guidance
A state model for flags and stored values with constant-time lookups per field
Separation of event-type dispatch from the suspicion rule
Behavior under each clarified storage policy, shown on the sample stream
How the stored state grows over a long stream
What a Strong Answer Covers Guidance
Systematic debugging driven by the provided samples and tests rather than guesswork
A corrected counter that matches the stated entity semantics on every input, with its cost analyzed
Clarifies the storage policy and matching rules before implementing the detector
A structure in which a new event type or rule can be added without rewriting the dispatch
Concrete maintainability answers: tests, naming, structure and bounded state
Follow-up Questions Guidance
How would you change the code so that a new event type, for example one that clears a flag, can be added without touching the existing handlers?
The stream runs for months. How do you keep the stored state from growing without bound, and what do you give up?
Two workers now process the stream in parallel. What breaks in your detector, and how would you split the work?
What would you put in place so that the next engineer who edits the suspicion rule cannot silently change which events are flagged?