Phase 1: Requirements & Use Cases
Interviewer: "Design a thread-safe in-memory cache."
Candidate: "Let me make sure I understand the scope. We're building a generic in-memory cache that multiple threads can read from and write to concurrently — with support for cache eviction. We're NOT designing a distributed cache like Redis; this is a single-process, in-memory cache at the class level. Does that match?"
Interviewer: "Yes. What actors and use cases do you see?"
Candidate: "The actors are reader threads fetching cached values and writer threads inserting or updating entries. The core use cases are:
get(key)— retrieve a value by key, return null on missput(key, value)— insert or update an entryevict(key)— explicitly remove an entryauto-eviction— remove entries when capacity is exceeded (LRU policy)TTL expiry— optionally expire entries after a set timeclear()— remove all entries"Interviewer: "What about TTL-based expiration? Is that in scope?"
Candidate: "I'd scope it as an optional extension. The core design will handle capacity-based eviction (LRU) first — TTL can be layered on with a lazy expiry check on
getor a background sweeper. Getting the concurrency model right is the harder problem here."WHY: TTL introduces a time dimension and potentially a background thread, which complicates the concurrency model. Nailing LRU eviction with correct locking demonstrates the core challenge.
Interviewer: "What concurrency assumptions are you making?"
Candidate: "I'm assuming high read-to-write ratio — caches are typically read-heavy. That shapes my locking strategy. I'll assume we need…
In the full tutorial
- Phase 2: Object Modeling
- Phase 3: Class Design & Relationships
- Phase 4: Design Patterns Application
- Phase 5: Key Implementation
- Phase 6: Extensibility & Trade-offs