Mock 08 — Staff Practical

Interview type: Staff/Principal engineer practical coding round Target role: Staff SWE (L6 Google / E6 Meta / Principal Amazon) Time limit: 75 minutes Format: Build a working component (not a LeetCode puzzle) with multiple interacting pieces Hints policy: Hints affect score but rarely fail you outright at staff bar — the bar is judgment, not raw problem-solving. Primary goal: Demonstrate the ability to build a real thing under time pressure, with monitoring/failure-mode awareness baked in.


What This Mock Tests

Staff interviews shift away from “can you solve this puzzle” toward “can you build something we’d ship.” You’re given a problem statement that resembles a small feature spec. Your job:

  • Decompose the problem into modules with clean interfaces
  • Choose data structures that match real production constraints
  • Implement the core fully + skeletons for the rest
  • Discuss monitoring, deployment, failure recovery, evolution
  • Justify every choice you make against alternatives

Scoring weights: Code Quality (#7), Tradeoff Reasoning (#13), Production Awareness (#14) are paramount. Pure algorithmic Optimization is less critical — staff problems rarely have a “trick.”


Pick One Build

Build A — In-Memory Rate Limiter Library

Build a usable Python/Java/Go module that provides:

limiter = RateLimiter(max_requests=100, window_seconds=60)
allowed = limiter.allow(user_id="alice")  # bool, ~10µs p99

Required:

  • Multiple algorithms behind a uniform interface (token bucket + sliding window log + sliding window counter)
  • Configurable per-user vs global limits
  • Thread-safe
  • A stats() method returning rejection rate per user
  • A purge() method to evict idle users from memory
  • Tests covering correctness, thread safety, and the window-edge case (burst at exactly t=W)

Build B — Bounded LRU Cache with TTL and Stats

Build an LRU cache that also supports per-entry TTL:

cache = LRUCache(capacity=10000, default_ttl_seconds=300)
cache.put(key, value, ttl=None)   # uses default
val = cache.get(key)              # returns None if missing/expired
cache.delete(key)
cache.stats()                     # hit rate, eviction rate, expiration rate

Required:

  • O(1) get/put
  • Lazy + active TTL expiration
  • Thread-safe
  • Memory cap (eviction policy: LRU among non-expired)
  • Tests: correctness, expiration races, concurrent put/get, capacity overflow

Build C — Job Scheduler (cron-like)

Build a scheduler that runs jobs at specified intervals:

scheduler = Scheduler()
scheduler.add(name="cleanup", interval_sec=300, fn=cleanup_fn)
scheduler.add(name="report", cron="0 9 * * MON", fn=report_fn)  # nice-to-have
scheduler.start()
scheduler.stop()
scheduler.status()  # last run, next run, last error per job

Required:

  • Multiple jobs running independently
  • Graceful shutdown (don’t kill mid-job)
  • Per-job error isolation (one job’s failure doesn’t crash the scheduler)
  • Catch-up policy on missed runs (skip vs catch-up; configurable)
  • Tests: timing, overlap, panic in job

Expected Communication Style

  1. Restate with assumptions stated upfront: “I’ll assume single-process, multi-threaded, in-memory; if you want me to extend to distributed, that’s a separate discussion.”
  2. Propose the module decomposition before writing any code. Whiteboard the public interface, the internal modules, the data flow.
  3. Identify the 2–3 critical design decisions and discuss alternatives.
  4. Pick one and code it — favor depth over breadth. Skeleton/stub the rest with comments like # TODO: implement token bucket variant with same interface.
  5. Discuss monitoring without prompting: which metrics, why, what alerts.
  6. Discuss failure modes without prompting: thread starvation, memory blowup, race conditions, partial failures.
  7. Test the critical path. Production-style tests, not just smoke.

Solution Sketches

A. Rate Limiter:

class RateLimiter(ABC):
    def allow(self, key: str) -> bool: ...
    def stats(self) -> dict: ...
    def purge(self, idle_seconds: int): ...

class SlidingWindowLogLimiter(RateLimiter):
    def __init__(self, max_requests, window_seconds):
        self._max = max_requests
        self._window = window_seconds
        self._logs = defaultdict(deque)   # key → deque of timestamps
        self._lock = threading.Lock()
        self._rejects = Counter()
        self._accepts = Counter()
    
    def allow(self, key):
        now = time.monotonic()
        with self._lock:
            log = self._logs[key]
            while log and log[0] <= now - self._window:
                log.popleft()
            if len(log) < self._max:
                log.append(now)
                self._accepts[key] += 1
                return True
            self._rejects[key] += 1
            return False

Plus token bucket and sliding-window-counter implementations behind the same interface.

B. LRU + TTL: doubly linked list + hashmap; each node stores (key, value, expires_at, prev, next). get: check expiry, evict if expired, return; else move to MRU. put: insert/update, evict LRU if over capacity. Background thread (or lazy on every get/put) sweeps expired entries.

C. Job Scheduler: thread pool + priority queue of (next_run_time, job). Main loop: peek queue, sleep until next, run job in pool, re-schedule. Catch exceptions per job; record to status. Graceful shutdown: stop accepting new runs, await running ones with timeout.


Common Failure Modes

  1. Built the algorithm without the interface. Staff interviews care about the API as much as the implementation.
  2. No thread safety. Mentioned in the spec; missed → fail.
  3. No mention of monitoring/observability. Critical staff signal.
  4. Used global state. Hard to test, hard to reason about.
  5. Coded all three rate limiter algorithms in 75 min instead of one well + sketches. Depth > breadth.
  6. TTL implementation does periodic full scan. O(N) sweep per second isn’t acceptable; lazy + bounded active sweep is.
  7. Scheduler: jobs share state and races corrupt it. Job functions need to be treated as untrusted code.

Passing Bar

  • Total score: 56/70 (average 4.0)
  • Code Quality #7 ≥ 4
  • Tradeoff Reasoning #13 ≥ 4
  • Production Awareness #14 ≥ 4
  • Working core; documented stubs for the rest
  • At least 3 tests covering: correctness, concurrency, edge timing
  • Monitoring + failure modes discussed substantively

Follow-up Questions

For A:

  • Make it distributed. → Redis with Lua atomic ops; or per-shard local limiter + global reconciliation.
  • Hot-user problem. → Sharded sub-limiters per user; or local L1 cache.
  • Add quota burst (allow 2× for 5 sec then throttle). → Token bucket with two-tier refill.

For B:

  • What’s the GC pressure under high churn? → Allocation per put is the cost; pool nodes if hot path.
  • Persist across restart. → Periodic snapshot to disk; replay log on startup.
  • Add a probabilistic admission filter (TinyLFU). → Prevent cache pollution from one-hit-wonders.

For C:

  • Distribute across N workers. → Leader-elected scheduler that dispatches jobs; or per-shard schedulers.
  • Persistent jobs (survive restart). → Persist queue to durable storage.
  • Jobs with dependencies. → DAG scheduler; topological execution.
  • Job retries with exponential backoff. → Per-job retry state machine.

Required Tests

  • Correctness on the basic case
  • Thread safety (concurrent calls; assert no double-count, no race-induced overflow)
  • Timing edges (window boundary, expiration boundary, scheduling drift)
  • Failure: what happens if the underlying clock jumps backward?
  • Resource cleanup: after purge / shutdown, no leaked threads or memory

Required Discussion (production)

Cover, at minimum:

  • Metrics you’d export (Prometheus-style)
  • Alert thresholds you’d set
  • Memory cap behavior
  • Failure modes and recovery
  • Deployment story (config, rollout, rollback)
  • Evolution: how would you add a new rate-limiting algorithm? (Should be drop-in.)

Self-Evaluation Template

Mock 08 — Staff Practical
Date: _______
Build: _______
Time: ___ / 75 min

Scores (1–5):
___ Total /70

Critical dimensions:
  Code Quality (#7): ___
  Tradeoff (#13): ___
  Production (#14): ___

Interface designed before coding? Y/N
Monitoring discussed unprompted? Y/N
Failure modes discussed unprompted? Y/N
Thread safety verified by test? Y/N

What I left unfinished (and what I'd do with another hour):

Action item:

What to Do If You Fail

  • Production score below 4: Build a real version of one of these systems and run it for a week with metrics. Phase 8 has more.
  • Code quality below 4: Have a senior do a written code review of your submission; act on it.
  • Tradeoff below 4: For every decision, force yourself to write down at least 2 alternatives and a rejection reason.
  • Pass twice consecutively before Mock 09.