Fundamentals and Low-Level Design
- Run the OOD round in order: requirements, classes, method contracts, then how the design extends
- Build an LRU cache live, then work two problems where the right structure is the solution
- Answer the oral follow-ups on OOP, memory and ownership, and complexity
This is the round the India loop is decided on, and the one the C++ track spends the most time in. After the coding screen, BlackRock turns to two things the screen barely touches. Can you design a small system as clean classes? Do you understand the tools underneath the code you write? The set piece is a machine-coding exercise, usually build a cache and make it run. Around it sit medium data-structure problems where the trick is choosing the right structure. A steady drip of oral follow-ups covers object orientation, memory, and complexity. This lesson builds the cache, works two structure-choice problems in full, and then drills the fundamentals questions you will have to answer out loud.
How BlackRock runs the OOD round
The low-level-design round has a shape, and following it is half of a strong performance. You're handed a small, concrete thing to build: a cache, a rate limiter, a producer-consumer buffer, an in-memory store. You get roughly forty minutes to design and implement it while you talk. The failure mode is jumping straight to a single fat class and coding. The interviewer wants to see you move through four moves in order, and each move is a place to earn signal.
Clarify the requirements
Pin down the operations and their contracts before any code. What methods exist, and what does each return on a miss? Is there a fixed capacity, and what's the eviction policy? Does it need to be thread-safe, and are keys and values generic? Two minutes of questions here stops you from designing the wrong thing.
Name the classes and interfaces
Sketch the types out loud. A cache is an interface with get and put; one implementation carries one eviction policy. A linked-list node is its own small class. Putting the policy behind an interface later lets you swap LRU for LFU without touching the callers.
Define the method contracts, then fill them
Write the signatures first, agree on what each does at the boundary, then implement. Get returns the value or a miss sentinel and counts as a use; put inserts or updates and evicts when full. Coding against agreed contracts keeps the implementation honest.
Defend the extension
The interviewer will move the constraint: now make it thread-safe, now switch to LFU, now add a time-to-live. A design with clean seams answers each in a sentence about which class changes. A tangled one has to be rewritten. This is where the round is won.
Written as a skeleton before a single method body exists, a strong cache design reads like this. One interface, one implementation per policy, a small node type, and the recency structure declared up front:
interface Cache<K, V> { V get(K key); // the value, or a miss sentinel void put(K key, V value); // insert or update, evicting when full}// One implementation carries one eviction policy, hidden behind the interface.final class LruCache<K, V> implements Cache<K, V> { private final int capacity; private final Map<K, Node<K, V>> index; // O(1) lookup by key private final Node<K, V> head, tail; // sentinels: most- and least-recent ends LruCache(int capacity) { /* wire head <-> tail, size the index */ } public V get(K key) { /* touch the node, move to front, return */ } public void put(K key, V val) { /* upsert, move to front, evict if over capacity */ } private void evict() { /* unlink the node before tail, drop it from index */ }}// A tiny class of its own, not an anonymous tuple.class Node<K, V> { K key; V value; Node<K, V> prev, next; }Build it: an LRU cache in O(1)
BlackRock caches reference data, security-master rows and latest prices, so a hot lookup never pays for a database round trip. The cache holds a fixed number of entries; when it fills, the least-recently-used entry is dropped to make room. Both operations have to run in constant time, which is the constraint that rules out anything linear and forces the two-structure design. Build it as clean classes, and be ready for the interviewer to ask for the eviction policy to change.
Reference-Data Cache: LRU Cache
Problem
LRUCache:-
LRUCache(capacity) creates a cache that holds at most capacity entries.-
get(key) returns the value stored for key, or -1 if the key is not present. A successful get counts as a use, making that entry the most recently used.-
put(key, value) inserts the entry, or updates it if the key already exists, and marks it most recently used. If inserting pushes the size above capacity, evict the least-recently-used entry first.Both
get and put must run in O(1) average time.Input: LRUCache(2); put(1,1); put(2,2); get(1); put(3,3); get(2); put(4,4); get(1); get(3); get(4)
Output: [null,null,null,1,null,-1,null,-1,3,4]
put(3,3) evicts key 2 (least recent), so get(2) is -1. put(4,4) evicts key 1, so get(1) is -1. Keys 3 and 4 remain.
Two structures cooperate. A hashmap keyed by key gives O(1) lookup to a node. A doubly linked list holds the nodes in most-to-least recently used order. Touching a node means unlinking it and re-inserting it at the front, and eviction means dropping the node at the back.
Reference-Data Cache: LRU Cache
Click "Run" to execute your code
Test Cases
[["LRUCache",[2]],["put",[1,1]],["put",[2,2]],["get",[1]],["put",[3,3]],["get",[2]],["put",[4,4]],["get",[1]],["get",[3]],["get",[4]]]Expected: [null,null,null,1,null,-1,null,-1,3,4][["LRUCache",[1]],["put",[1,10]],["get",[1]],["put",[2,20]],["get",[1]],["get",[2]]]Expected: [null,null,10,null,-1,20][["LRUCache",[2]],["put",[1,1]],["put",[1,5]],["get",[1]],["put",[2,2]],["put",[3,3]],["get",[1]]]Expected: [null,null,null,5,null,null,-1][["LRUCache",[2]],["get",[9]],["put",[1,1]],["get",[1]],["get",[2]]]Expected: [null,-1,null,1,-1][["LRUCache",[3]],["put",[1,1]],["put",[2,2]],["put",[3,3]],["get",[1]],["put",[4,4]],["get",[2]],["get",[3]],["get",[4]],["get",[1]]]Expected: [null,null,null,null,1,null,-1,3,4,1]Structure choice: days until a higher price
The second and third problems here are not about a class. They are about the fundamentals question underneath every medium prompt: which data structure turns a quadratic brute force into a linear pass. This one looks like a simple scan of a price series, and the naive answer, for each day look forward until a higher price appears, is O(n squared). A monotonic stack does it in one pass. Recognizing that the answer is a stack, and which order to keep it in, is the entire test.
Price Series: Days Until a Higher Price
Problem
prices, a list where prices[i] is a security's closing price on day i. For each day, find how many days you would have to wait for a strictly higher closing price.Return a list
answer where answer[i] is that number of days, or 0 if no later day has a strictly higher price.Input: prices = [73,74,75,71,69,72,76,73]
Output: [1,1,4,2,1,1,0,0]
Day 0 (73) waits 1 day for 74. Day 2 (75) waits until day 6 (76), 4 days out. Days 6 and 7 never see a higher price, so 0.
Input: prices = [60,50,40,30]
Output: [0,0,0,0]
Prices only fall, so no day ever has a higher price ahead of it.
Keep a stack of day indices whose higher price has not been found yet, with prices decreasing from bottom to top. When today beats the price at the top of the stack, today is that day's answer, so pop it and record the day gap. Then push today.
Price Series: Days Until a Higher Price
Click "Run" to execute your code
Test Cases
[[73,74,75,71,69,72,76,73]]Expected: [1,1,4,2,1,1,0,0][[30,40,50,60]]Expected: [1,1,1,0][[60,50,40,30]]Expected: [0,0,0,0][[89,62,70,58,47,47,46,76,100,70]]Expected: [8,1,5,4,3,2,1,1,0,0][[5,5,5,5]]Expected: [0,0,0,0][[100]]Expected: [0][[]]Expected: []Complexity trade-off: the longest consecutive run
The last problem is where BlackRock most directly probes complexity reasoning. It has an obvious answer and a better one, and the interviewer wants to hear you compare them. Given a bag of integer ids, find the longest run of consecutive values. Sorting gets you there in O(n log n) and is a perfectly reasonable first offer. A hash set gets you O(n), and the leap is seeing how to avoid re-counting the same run. Say the sort solution, state its cost, then improve it out loud.
Trade IDs: Longest Consecutive Run
Problem
ids, an unsorted list of integer identifiers that may contain duplicates. A consecutive run is a set of ids that form an unbroken sequence of integers, such as 4, 5, 6, 7.Return the length of the longest consecutive run present in
ids. Aim for O(n) time, faster than sorting.Input: ids = [100,4,200,1,3,2]
Output: 4
The run 1, 2, 3, 4 has length 4. The values 100 and 200 are isolated.
Input: ids = [0,3,7,2,5,8,4,6,0,1]
Output: 9
The values 0 through 8 form one unbroken run of length 9; the duplicate 0 does not extend it.
Put every id in a hash set for O(1) membership. A value starts a run only if the value one below it is absent from the set. From each such run-head, walk upward while the next value is present, counting the length. Each value is visited at most twice overall.
Trade IDs: Longest Consecutive Run
Click "Run" to execute your code
Test Cases
[[100,4,200,1,3,2]]Expected: 4[[0,3,7,2,5,8,4,6,0,1]]Expected: 9[[]]Expected: 0[[9]]Expected: 1[[5,5,5]]Expected: 1[[10,20,30]]Expected: 1[[1,2,0,1]]Expected: 3The fundamentals oral: OOP, memory, complexity
Every problem above trails a set of spoken follow-ups, and the interviewer uses them to tell an engineer who understands their tools from one who memorized a pattern. They are the questions a colleague would ask while reading your code, not gotchas. Have crisp, spoken answers ready across three areas, tied to the code you just wrote rather than recited from a glossary.
Object orientation. Expect why you drew the class boundaries you did. Why is the eviction policy behind an interface? So a new policy is a new class, not an edit to the cache. Why is the linked-list node its own type rather than a tuple? So it can carry its key and its neighbors and stay readable. Be ready for compile-time versus runtime polymorphism: overloading resolved by the compiler against static types, overriding dispatched at runtime against the real object. Encapsulation comes up too, as the reason the cache exposes get and put but hides the list surgery. The honest answer to why bother is that the follow-up will change the requirements, and clean boundaries are what let you answer in a sentence.
Memory and ownership. This is where Java and C++ diverge, and BlackRock probes the language you claimed. On the Java side, know what lives on the stack versus the heap. A local reference sits on the stack while the object it points to lives on the heap. Know how garbage collection decides an object is unreachable and reclaimable. On the C++ side of the same cache, expect who owns the nodes and why a smart pointer expresses that ownership. Then the difference between a unique owner and a shared, reference-counted one, and how RAII ties a node's lifetime to a scope. If you mention move semantics, be able to say what moving a node does that copying it would not, and why that matters for a container that reshuffles pointers constantly.
Complexity. Do not just state a Big-O; justify it against the structure you chose. Why is the cache O(1) per operation when it clearly does several steps? Because each step is a constant number of map operations and pointer writes, none of which scale with the number of entries. Why is the price sweep O(n) when it has a while loop inside a for loop? Because each index enters and leaves the stack once, so the inner work is amortized constant. Why does the consecutive-run guard preserve O(n)? Because it caps each value at two visits. The pattern the interviewer rewards is naming the structure, then deriving the bound from how the structure behaves, rather than pattern-matching a number to a shape.
Tie every fundamentals answer back to the code on the screen. Asked about the heap, point at the nodes your cache allocates. Asked about polymorphism, point at the policy interface you put the eviction behind. Asked about O(1), walk the two pointer writes that make a get constant. An answer anchored to the artifact in front of you reads as understanding; the same fact recited in the abstract reads as revision. That anchoring is the difference this round is built to detect.
Common questions
What is the BlackRock machine-coding round?
It is a low-level-design exercise where you design and implement a small system live, most often a cache, in about forty minutes while narrating your choices. The canonical prompt is an LRU cache: a hashmap for O(1) lookup married to a doubly linked list for recency, written as clean classes. The grading is object-oriented judgment, sensible interfaces, the right structures, and a design that survives the follow-up asking you to change the eviction policy or add thread-safety.
How do I design an LRU cache in an interview?
Move in order. Clarify the operations and their contracts, then name the types: a cache interface with get and put, one implementation, and a small node class. Use a hashmap from key to node for constant-time lookup and a doubly linked list for recency, with head and tail sentinels so the pointer surgery has no edge cases. Touch a node on every access to keep it fresh, and evict from the tail when you overflow. Store the key on each node so eviction can also clean the map.
What fundamentals does BlackRock ask about?
The oral follow-ups cluster in three areas. Object orientation covers why your class boundaries, compile-time versus runtime polymorphism, and encapsulation. Memory covers stack versus heap and garbage collection in Java, plus smart pointers, ownership, RAII and move semantics in C++. Complexity means justifying a Big-O from how the chosen structure behaves instead of reciting it. The strongest answers anchor to the code you just wrote rather than to definitions.
Should I use Java or C++ for the design round?
Use the language you can defend under questioning, because the round drills whichever you pick. The India loop leans Java, so expect JVM memory and collection internals; the C++ track mirrors the same exercises and probes ownership, smart pointers, and move semantics instead. Python is accepted as a working language. The structures and the design are identical across all three; only the memory-model follow-ups change.
How is this round different from the coding screen?
The screen tests whether you can solve an easy-to-medium problem quickly. This round tests whether you can design cleanly and explain deeply. The problems here are deliberately not hard, an LRU cache, a monotonic stack, a hash-set scan, but each carries a design decision and a chain of oral follow-ups. Prepare by building the cache end to end and by rehearsing the spoken justification for every structure you reach for.
Key Takeaways
- ✓The BlackRock low-level-design round is a forty-minute machine-coding exercise, usually a cache. Move through it in order: clarify the requirements, name the classes and interfaces, define the method contracts, then defend how the design extends when the policy or the concurrency changes.
- ✓Build an LRU cache from a hashmap for O(1) lookup and a doubly linked list for recency, with head and tail sentinels to erase edge cases. Store the key on each node so eviction cleans the map, touch a node on every access, and evict from the tail. Putting the eviction policy behind an interface is what makes the LFU follow-up a one-line answer.
- ✓The medium problems are structure-choice questions in disguise. Days-until-a-higher-price is a monotonic stack of waiting-day indices, O(n) because each index is pushed and popped once. Longest-consecutive-run is a hash set with a run-head guard, O(n) instead of the sort's O(n log n) because each value is visited at most twice.
- ✓The fundamentals oral decides this round. Have crisp answers on OOP, memory and ownership across Java and C++, and complexity. Anchor every one to the code on the screen rather than reciting a definition.
BlackRock interview guideKnow what BlackRock asks before you walk in.
13 coding · 3 system design · 15 behavioral questions
Get their questions first.
InterviewLegend is independent and not affiliated with or endorsed by any company it covers.