Naming and readability
Code the interviewer can follow without you narrating.
01The intuition
The problem it solves
Here is a method from an LRU cache written in a hurry. It works. Now imagine the interviewer reading it over your shoulder while you say nothing.
class C:
def __init__(self, n: int) -> None:
self.n = n
self.d: dict = {}
self.l: list = []
def g(self, k):
if k in self.d:
self.l.remove(k); self.l.append(k)
return self.d[k]
return -1
def p(self, k, v):
if k in self.d:
self.l.remove(k)
elif len(self.d) == self.n:
del self.d[self.l.pop(0)]
self.d[k] = v; self.l.append(k)
Every line needs a sentence from you to be understood: what n is, which end of l is recent, why −1. In a design round the code is the explanation, and time spent narrating it is time not spent on the design. Readability is not polish; it is the channel through which the interviewer sees your thinking.
Build it one piece at a time
Step one: names that say what and why. The class is a cache, the limit is a capacity, the list is recency order, and the sentinel is a documented miss. A reader now needs no narrator for the data model.
class LRUCache:
MISS = -1
def __init__(self, capacity: int) -> None:
self.capacity = capacity
self.values: dict[int, int] = {}
self.recency: list[int] = [] # oldest first, newest last
Step two: one idea per method, and a name for each idea. "Mark as recently used" and "evict the oldest" were buried inside get and put; pulling them out lets get and put read as the rules they implement.
class LRUCache(LRUCache): # continued
def get(self, key: int) -> int:
if key not in self.values:
return self.MISS
self._touch(key)
return self.values[key]
def put(self, key: int, value: int) -> None:
if key in self.values:
self._touch(key)
elif len(self.values) == self.capacity:
self._evict_oldest()
self.values[key] = value
if key not in self.recency:
self.recency.append(key)
def _touch(self, key: int) -> None:
self.recency.remove(key)
self.recency.append(key)
def _evict_oldest(self) -> None:
oldest = self.recency.pop(0)
del self.values[oldest]
Step three: make the shape of the code match the shape of the rule, and let types and small constants carry meaning. Booleans named for the question they answer, no magic numbers, no single-letter fields outside a two-line loop, and the public API first in the file so the reader meets the contract before the mechanism. Readability also includes what is not there: a comment that repeats the code is noise, a comment that says why a list and not a deque is signal.
Before and after, line by line:
| Before | After | What the reader gains |
|---|---|---|
| self.n | self.capacity | what the number limits |
| self.l.pop(0) | _evict_oldest() | the rule, not the mechanism |
| return -1 | return self.MISS | that −1 is a protocol, not an accident |
| g, p | get, put | the API the interviewer asked for |
Why it works
A reader builds a model of the code from its names first and its logic second. Good names let the model form in one pass, so the interviewer's attention goes to the design decisions you want judged: the data structures, the invariants, the extension points. Small named methods also make the invariants visible: _touch says in its body exactly what recency means, in one place.
Where it breaks: names that lie. A method called get that also inserts, a field called count that holds a size in bytes, or a flag called done that is set before the work finishes will cost more than a bad name, because the reader trusts it. Keep names truthful under refactoring, and rename when behaviour changes. The other trap is over-naming: a three-line helper for every expression makes the reader jump around instead of reading down.
time an interviewer spends reading your code ≈ time they are not spending on your design; names cut the first so the second can grow
The trade-off you must say out loud
Under time pressure, full names and helper methods feel slower to type than single letters; they are not slower to think with, and they pay back the first time you have to find a bug while talking. Say that you are optimising for the reader, and that you would use a deque for the recency list in production, since list.remove is O(n); naming the limitation is part of being readable. The follow-up that usually comes next is a change request, and well-named code is the code you can change in front of someone without breaking it.
Where it shows up in the 100 Google problems
- LRU cache: the example above, where the move to a doubly linked list keeps the same method names.
- Text editor with undo and redo: commands named for user actions, so the undo stack reads like a history.
- Restaurant waitlist: join, leave and seat as verbs the receptionist would use.
- Tic-tac-toe: place, winner and is_full instead of index arithmetic in the caller.