Low-level design
Same product. Two different design altitudes.
High-level design asks how many machines share the work: services, stores, traffic, and failure domains. Low-level design asks how objects inside one program share the work: types, methods, invariants, and threads. Same product name on the board — two different jobs.
Mix those altitudes and you will name Redis while they wanted a class with get and put. Confirm whether you are designing process boundaries or object responsibilities before you draw.
Distributed altitude
High-level design
is about , data stores, traffic, and failure. “Design a URL shortener” here means collisions at a billion links, a cache in front of the store, and a region going down. You draw boxes that can die independently.
The parking lot at this altitude is gates in two cities, a shared inventory service, and what happens when the payment provider is slow. That is a useful conversation. It is not this track.
In-process altitude
Low-level design
is about types, methods, rules that must stay true, and two threads inside one program. “Design a URL shortener” here means encode, decode, and a that a HashMap can fill this afternoon. You are unless they say otherwise.
The parking lot at this altitude is park, exit, occupancy, and a fee policy. Two gates are two threads, not two data centers.
A simple test you can use in the first minute
If the next sentence needs a network hop, you are in HLD. If it needs a class that should not know about SQL, you are in LLD. Say the test out loud. Get a nod. Then stay on that altitude for the rest of the round.
Several problems in this track share a name with system-design notes — URL shortener, rate limiter, parking lot. The LLD page is the in-process version: objects in one program. The HLD page is the distributed version. Keep those two talks apart even when the title on the board is identical.
| Signal | You are in HLD | You are in LLD |
|---|---|---|
| Unit of work | Service, queue, region, replica | Class, interface, method, thread |
| Store | Postgres, Redis, S3, shard key | UrlStore.put / get; a HashMap is legal |
| Failure | AZ down, timeout, retry, fallback | Lot full, unknown ticket, two threads, one spot |
| Scale talk | QPS, p95, partition, cache hit | O(n) scan of twenty spots is usually fine |
| Change | Add a region, change consistency | Add EV pricing without editing the lot |
| Drawing | Boxes and arrows across machines | Types and who calls whom inside one process |
Same product, two altitudes
URL shortener: do not mix the altitudes
In high-level design you worry about unique codes at a billion links and a cache in front of the database. In this round you write encode, decode, and a store. The HLD URL shortener note is the distributed version. This sketch is the in-process object model.
interface UrlStore {
void put(String code, String longUrl);
String get(String code);
}
class InMemoryUrlStore implements UrlStore {
private final Map<String, String> byCode = new HashMap<>();
public void put(String code, String longUrl) {
byCode.put(code, longUrl);
}
public String get(String code) {
return byCode.get(code);
}
}
class UrlShortener {
private final UrlStore store;
private final CodeGenerator codes;
UrlShortener(UrlStore store, CodeGenerator codes) {
this.store = store;
this.codes = codes;
}
String encode(String longUrl) {
String code = codes.next();
store.put(code, longUrl);
return code;
}
String decode(String code) {
String longUrl = store.get(code);
if (longUrl == null) {
throw new UnknownCodeException(code);
}
return longUrl;
}
}If you drop Redis into this round, you jumped altitudes. Unless they asked for HLD, a cache is a class with get and put. It is not a box on a region map. You can still say, “Later this store could wrap Redis.” That sentence shows you know the boundary. Implementing the product infrastructure is not the assignment.
A short conversation that saves the round
Strong candidates confirm the altitude before they draw. This is not delay. It is how you avoid building a distributed system when they wanted an object model.
You
Before I draw, I want to confirm scope. Are we designing objects in one program, or services across machines?
Interviewer
One program. Machine coding. HashMap is fine.
You
Good. Then encode and decode against a UrlStore. I will not place a load balancer. If two threads encode at once I will say how codes stay unique.
Interviewer
What if we need to scale later?
You
Then the store interface is the seam. Today InMemoryUrlStore. A later adapter could talk to a database. That is a different round.
The nod in minute two is worth more than a pretty region map. Once they say “one program,” stay there. Two threads are still in scope. Kafka is not.
Rate limiter: class versus cluster
The same title shows up in both tracks. HLD talks about a global counter, sliding windows across gateways, and what happens when Redis is partitioned. LLD talks about allow(key) in one process, and whether two threads can both think they have budget.
If they asked for LLD, write the policy object. Do not start with “we put a token bucket in Redis.” That sentence belongs on the HLD rate limiting page.
interface RateLimiter {
boolean allow(String key);
}
class TokenBucketLimiter implements RateLimiter {
private final Map<String, Bucket> buckets = new HashMap<>();
private final Clock clock;
private final long capacity;
private final long refillPerSecond;
public synchronized boolean allow(String key) {
Bucket bucket = buckets.computeIfAbsent(key, k -> new Bucket(capacity, clock.millis()));
bucket.refill(clock.millis(), refillPerSecond, capacity);
if (bucket.tokens < 1) {
return false;
}
bucket.tokens -= 1;
return true;
}
}When a product name is a trap
They said Redis and you are in an LLD round
They asked for classes and you drew Kafka
They want a little of both
Parking lot in two cities
What still counts as LLD even when it feels “systems-y”
Two threads sharing a spot is LLD. A lock, a synchronized method, or an atomic occupy is LLD. A repository interface that hides SQL is LLD. Idempotent park if the same vehicle is already inside is LLD. None of those need a region map.
What does not count: naming Kubernetes, picking Kafka partitions, or estimating QPS for a million gates. Those are good skills. They are the other job. Bring them only if the interviewer walks you into distributed scope on purpose.
// LLD: hide the database, do not design the cluster
interface TicketBook {
Ticket issue(Vehicle vehicle, ParkingSpot spot, Instant at);
Ticket require(TicketId id);
void close(Ticket ticket);
}
class SqlTicketBook implements TicketBook {
private final DataSource db;
public Ticket require(TicketId id) {
// SELECT ... WHERE id = ?
// The SQL stays here. ParkingLot never sees ResultSet.
}
}
// Not LLD: "we will put tickets in a sharded Cassandra ring
// with a cache in each AZ and a queue for receipt emails."Keep going from here
- What LLD is asking — splitting responsibilities among objects.
- How to approach an LLD round — forty-five minutes of talking before typing.
- Interview scorecard — what they grade besides pattern names.
- URL shortener as machine coding — the in-process version of the same product.
Takeaway
Same product, two altitudes. If the next sentence needs a network hop, you are designing distributed systems. If it needs a class that should not know about SQL, you are designing objects in one program. Confirm the altitude, keep the store as an interface, and leave Redis as a later adapter. Next we run a forty-five-minute talking order so the last thirty minutes are code instead of panic.