The Design Playbook
Design a unique ID generator
Thousands of machines, each minting IDs independently, and no two IDs may ever collide — anywhere on Earth, ever. You cannot call a central authority; there is no time. Yet this problem is solved, in production, with 64 bits of careful bookkeeping.
- Globally unique: no collisions across machines, ever — and no central coordinator to enforce it.
- Roughly time-sortable: newer IDs are numerically larger than older ones, so indexes and pagination stay cheap.
- Compact and fast: a 64-bit integer, and about 10,000 IDs generated per second without breaking a sweat.
UUID is the easy answer: generate anywhere, no coordination, done. But it costs 128 bits — double your key size, heavier indexes — and random values scatter inserts across your B-tree, fragmenting pages and destroying sortability. A single database auto-increment is compact and perfectly ordered, but every insert funnels through one writer: a bottleneck, and a single point of failure. A ticket server softens this — one DB hands out ranges to many machines — yet the range-issuer is still one box whose death stops the world. Every centralized option buys simplicity and pays in availability.
Split the 64 bits into sections, and each section buys you one property. 41 bits of timestamp — milliseconds since a custom epoch — give you ~69 years of life and put time in the high bits, so IDs sort chronologically for free. 5 bits for the datacenter and 5 for the machine give you 1,024 independent generators with zero coordination. 12 bits of sequence — a per-millisecond counter on each machine — give you 4,096 IDs per millisecond per machine, roughly four million per second, against your requirement of ten thousand. Uniqueness stops being something you check; it becomes something the structure guarantees.
NTP can step a machine's clock backwards — a leap second, a correction, a restart. Now your timestamp rewinds into territory where you already issued IDs, and duplicates leak out. You have three moves: wait until the clock catches up with the last timestamp you used, refuse to generate IDs until time moves forward again, or keep using the last timestamp and just advance the sequence. Most production systems refuse and alert — a brief outage in one generator beats a silent duplicate in your primary key. Say which you choose, and why.
The deep idea: you never made uniqueness — you partitioned it. Time, space, and sequence carve the bit space so thoroughly that two machines physically cannot produce the same number. Coordination is replaced by geometry.
Why does the timestamp occupy the high bits of a snowflake ID — and what exactly goes wrong when a machine's clock steps backwards? (Because integer comparison reads high bits first, a leading timestamp makes newer IDs sort larger for free; a clock rollback re-enters timestamp values already used with the same machine ID, so the sequence counter repeats and you can emit a duplicate primary key — hence the wait, refuse, or reuse-last-timestamp strategies.)
UUID when convenience outranks size and order; a counter when one writer is honestly enough; snowflake when you need all three properties at once. And whichever you ship, decide your clock-rollback policy before NTP decides it for you.
Find where your system generates IDs today — open the code, don't guess. If it is a random UUID, check whether your primary-key index is fragmenting under scattered inserts. Then sketch the snowflake bit layout for your scale: how many machines, how many IDs per millisecond, how many years of timestamp you need. Ten minutes, one page, and you will never answer this interview question the old way again.
The Design Playbook