Question 5Coding
Build a small in-memory cache. Then add expiry. Then a size limit.
Staged exercises show how your code holds up when requirements change mid-interview, like at a customer.
“Engineering background. You're a genuine builder. Proficient in Python and at least one of TypeScript/JavaScript, Java, or Go; comfortable across data plumbing, APIs, and a bit of front-end when a demo needs it.”Forward Deployed Engineer · NinjaTech AI
The answerFour steps
- 1
Start with get and set on a dictionary, plus tests
Say it like this
“a class wrapping a dict with get and set, plus three tests: set then get, a missing key returns None, and a second set overwrites.”
Why it scores
A plain first version with tests, so later stages are small, safe edits.
- 2
Store an expiry time with each value and check it on read
Say it like this
“For expiry, set stores the value with expires_at, which is time.monotonic() plus the TTL. On get, if it's past expiry I delete it and return a miss. I pass the clock in, so tests don't sleep.”
Why it scores
Lazy expiry and an injected clock. Tests stay fast and exact.
- 3
Track use order and evict the least recently used entry when full
Say it like this
“For the size limit I switch to an OrderedDict: move_to_end on every get and set, and past, say, 1,000 entries, popitem(last=False) evicts the least recently used. Both are O(1).”
Why it scores
Names the standard O(1) LRU structure in Python, and why it's O(1).
- 4
Say what you'd change for many threads or many machines
Say it like this
“If they ask about threads, I'd put a lock around each operation. Across machines, I'd stop and use Redis.”
Why it scores
Sees the limits of in-process state and stops building at the right point.
StrongAll together
About a minute, out loud. The marked parts are the four steps; hover one to find it above.
I'd keep stage one boring: a class wrapping a dict with get and set, plus three tests: set then get, a missing key returns None, and a second set overwrites.1 For expiry, set stores the value with expires_at, which is time.monotonic() plus the TTL. On get, if it's past expiry I delete it and return a miss. I pass the clock in, so tests don't sleep.2 For the size limit I switch to an OrderedDict: move_to_end on every get and set, and past, say, 1,000 entries, popitem(last=False) evicts the least recently used. Both are O(1).3 If they ask about threads, I'd put a lock around each operation. Across machines, I'd stop and use Redis.4
WeakThe answer that fails
Sounds fine. Common. Scores low.
“I'd use a dictionary, and for expiry I'd run a background thread that scans all the keys every second and deletes old ones. When it's full, I'd remove the oldest key.”
Interviewer's note
A scanning thread adds races and cost. “Oldest added” isn't least recently used, and no tests to lean on between stages.
PracticeTwo minutes, then they push back
Your turn
Build a small in-memory cache. Then add expiry. Then a size limit.
Answer like you're in the room: about two minutes, no notes. Say it out loud, even alone. It feels different from thinking it. Then check your answer against what interviewers score.