← Back to blog ZipRecruiter 2026 NG OA: In-memory Database 4-Level Walkthrough (TTL + Backup/Restore)
ZipRecruiter

ZipRecruiter 2026 NG OA: In-memory Database 4-Level Walkthrough (TTL + Backup/Restore)

2026-05-14

ZipRecruiter's 2026 New Grad OA doesn't test algorithms—it's all about design discipline. One big problem, four progressive levels: CRUD, Scan & Prefix, TTL expiry, Backup / Restore. Each level alone is easy, but all four must share one data structure—taking shortcuts in earlier levels triples your refactoring cost. This post lays out the optimal data structures, full Python solutions, and CodeSignal-specific defenses for each level.

ZipRecruiter NG OA Overview

Dimension Detail
Platform CodeSignal Test
Duration 90 minutes
Questions 1 problem, 4 levels
Difficulty LeetCode Easy~Medium, design-heavy
Pass threshold 80% tests green
Rubric Correctness + level completion

Problem: In-memory Database

Build an in-memory key-value store with nested fields:

db[key][field] = value

Operations either act on a key whole or a (key, field) pair.

Level 1: Basic CRUD

Operations:

class Database:
    def __init__(self):
        self.db: dict = {}

    def set(self, key, field, value):
        self.db.setdefault(key, {})[field] = value

    def get(self, key, field):
        return self.db.get(key, {}).get(field)

    def delete(self, key, field):
        if key in self.db and field in self.db[key]:
            del self.db[key][field]
            if not self.db[key]:
                del self.db[key]
            return True
        return False

Level 1 keys:

Level 2: Scan & Prefix Filtering

Operations:

def scan(self, key):
    if key not in self.db:
        return []
    return [f"{f}({v})" for f, v in sorted(self.db[key].items())]

def scan_by_prefix(self, key, prefix):
    if key not in self.db:
        return []
    return [
        f"{f}({v})"
        for f, v in sorted(self.db[key].items())
        if f.startswith(prefix)
    ]

Hidden cases:

Level 3: TTL (Time-to-Live)

Operations:

Core design: store each field as a (value, start, ttl) tuple; ttl=None means permanent.

def _is_alive(self, entry, timestamp):
    value, start, ttl = entry
    if ttl is None:
        return True
    return timestamp < start + ttl

def set_at(self, key, field, value, timestamp):
    self.db.setdefault(key, {})[field] = (value, timestamp, None)
    return ""

def set_at_with_ttl(self, key, field, value, timestamp, ttl):
    self.db.setdefault(key, {})[field] = (value, timestamp, ttl)
    return ""

def get_at(self, key, field, timestamp):
    if key not in self.db or field not in self.db[key]:
        return None
    entry = self.db[key][field]
    if not self._is_alive(entry, timestamp):
        return None
    return entry[0]

def delete_at(self, key, field, timestamp):
    if key not in self.db or field not in self.db[key]:
        return False
    entry = self.db[key][field]
    if not self._is_alive(entry, timestamp):
        return False
    del self.db[key][field]
    if not self.db[key]:
        del self.db[key]
    return True

def scan_at(self, key, timestamp):
    if key not in self.db:
        return []
    alive = [
        (f, e[0]) for f, e in self.db[key].items()
        if self._is_alive(e, timestamp)
    ]
    return [f"{f}({v})" for f, v in sorted(alive)]

Three Level-3 traps:

  1. scan_at must filter, not delete expired fields—deletion breaks subsequent set_at_with_ttl semantics
  2. delete_at on expired field returns False—expired ≡ "not present"
  3. TTL is strict less-than: timestamp < start + ttl, not <=

Level 4: Backup / Restore

Operations:

def __init__(self):
    self.db: dict = {}
    self.backups: list = []

def _snapshot(self, timestamp):
    snap = {}
    for key, fields in self.db.items():
        snap_fields = {}
        for f, (v, start, ttl) in fields.items():
            if ttl is None:
                snap_fields[f] = (v, None, None)
                continue
            remaining = start + ttl - timestamp
            if remaining > 0:
                snap_fields[f] = (v, timestamp, remaining)
        if snap_fields:
            snap[key] = snap_fields
    return snap

def backup(self, timestamp):
    self.backups.append((timestamp, self._snapshot(timestamp)))
    return str(sum(len(fs) for fs in self.backups[-1][1].values()))

def restore(self, timestamp, target_timestamp):
    candidate = None
    for bk_ts, snap in self.backups:
        if bk_ts <= target_timestamp:
            candidate = (bk_ts, snap)
    if candidate is None:
        self.db = {}
        return ""
    bk_ts, snap = candidate
    self.db = {}
    for key, fields in snap.items():
        for f, (v, start, ttl) in fields.items():
            if ttl is None:
                self.db.setdefault(key, {})[f] = (v, timestamp, None)
            else:
                self.db.setdefault(key, {})[f] = (v, timestamp, ttl)
    return ""

Level 4 designs:

Strategy

1) Level priority: 1 → 2 → 3 → 4

Each level is ~25% of total score. Hitting 100% on Levels 1+2 already puts you in CodeSignal's top 30%. Level 3 is the difficulty cliff—TTL semantics, if wrong, fail the entire level. Level 4 is the stretch goal.

2) Pre-commit to the data structure

Don't store db[key][field] = value in Level 1 and refactor to (value, start, ttl) in Level 3—you'll rewrite everything. Start with the tuple from Level 1, defaulting to (value, 0, None) for permanent fields.

3) Return types matter

CodeSignal scoring is strict about None vs "" vs True/False. Read the prompt: get_at returns None, set_at returns "", delete_at returns boolean.

FAQ

What's the ZipRecruiter OA pass rate?

Overall ~40-50%. Candidates who nail Levels 1+2 pass at ~70%. Level 3 is the elimination zone—TTL semantic slips lose 25% of total score.

Can I debug on CodeSignal?

Yes. CodeSignal supports stdout printing and custom test runs. After each level, run 3-5 boundary cases: empty key, empty prefix, TTL boundary, backup-then-delete.

How long to finish all four levels?

A trained candidate: 60-75 minutes. Recommended split: 10 min planning → 20 min L1+L2 → 30 min L3 → 15 min L4. If L4 doesn't fit, securing full marks on L3 still lands you in the final round.

Are there similar problems elsewhere?

Yes—4-level CodeSignal database problems are nearly identical across Snowflake, Optiver, Capital One, Roku. The "prefix + TTL" template is CodeSignal's standard; only the surface story changes (DB → warehouse → user system).

What comes after the OA?

OA → HR phone screen → 2-3 technical onsite rounds (1 Behavioral + 1-2 Coding + 1 System Design for senior roles). Typical OA-to-onsite turnaround: 2-4 weeks.


Preparing for the ZipRecruiter NG OA?

oavoservice covers the entire CodeSignal in-memory database family: level decomposition, TTL design templates, backup/restore algorithms. We have full walkthroughs for ZipRecruiter, Snowflake, Optiver, and similar 4-level design problems, and can customize practice to your target company.

Add WeChat Coding0201 to book ZipRecruiter OA coaching.

#ZipRecruiter #ZipRecruiterOA #NewGrad #CodeSignal #InMemoryDB #OARealQuestions


Contact

Email: [email protected]
Telegram: @OAVOProxy