System Design Interview Questions: ID Generation

Reviewed by Mark Dickie · Last updated

13 questions on this page you can answer and see marked on the spot. Go to the first one

ID generation is the process of producing unique identifiers for records across multiple servers or data centers in a distributed system. For this interview, know the main approaches: UUIDs, database auto-increment, Snowflake-style generators, and ticket servers. You should understand the tension between sortability and coordination overhead, and be able to start from stated requirements (expected QPS, 64-bit constraints, ordering guarantees) to reason toward a design. Many interviewers will ask you to design a system that produces millions of IDs per second while keeping them time-sortable and collision-free across node restarts and clock drift.

What does an ID generation interview question test?

These questions test whether you can keep IDs unique and sortable while sustaining high throughput. The core challenge is generating IDs that are unique across multiple nodes without a central coordinator that becomes a bottleneck.

ApproachUniqueness guaranteeTime-sortableCoordination modelThroughput
UUID v4Globally unique (collision-free in practice)NoNoneVery high
DB auto-incrementUnique per databaseYesCentralized (single DB)Low
Ticket serverGlobally uniqueYesCentralized (single server)Medium
SnowflakeGlobally uniqueYes (roughly)Distributed (machine IDs assigned)High
ULID / KSUIDGlobally uniqueYes (lexicographic)NoneVery high

How do I choose between Snowflake, UUID, and database sequences?

  1. Clarify the requirements first: expected QPS, whether IDs must be sortable, and whether they need to fit in 64 bits.
  2. Decide whether you can tolerate a single point of coordination. Database sequences and ticket servers give you ordering but cap your throughput at what one machine can handle.
  3. If you need scale without coordination, UUIDs work but they are not sortable and they waste index space in databases.
  4. Snowflake splits the difference: a 64-bit ID with a timestamp prefix, a machine ID, and a sequence number. You get time-ordering and high throughput, but you must manage machine ID assignment and clock skew.
  5. Address failure modes explicitly. What happens if a node's clock moves backward? What if two nodes share a machine ID? Interviewers want to hear you name these risks and propose mitigations.

Key facts

  • Tarmac has 26 System Design interview questions on this topic, 25 of them on this page, at difficulty 1–5 of 5.
  • Tarmac tracked 3,763 job postings asking for System Design in September 2026.
  • Roles asking for System Design advertise a median base salary of US$180,347, across 1,250 job postings as of September 2026.
  • Tarmac last reviewed these System Design interview questions on 13 October 2026.

At a glance

Questions25 shown · 26 in the bank
Difficulty1–5 of 5
FormatsTrue / false, Multiple choice, Flashcard, Short answer, Fill in the blank, Multiple answer, Ordering, Design exercise

What you'll review

  1. id generation

Practice questions

Try one before you open the answer. Pick an option and press Check; it's marked on the spot.

True or false: A UUID v4 (randomly generated, 128-bit) can be produced on independent servers with no coordination between them, and the probability of a collision across millions of generated IDs is negligible in practice.#

Options

Show answer

True. UUID v4 IDs can be generated independently on separate servers with no coordination. Each ID carries 122 random bits, so the chance of a collision stays negligible even across millions of generated IDs.

Why:

True. UUID v4 allocates 122 of its 128 bits to random values, while the remaining 6 bits encode the version and variant. With 122 bits of entropy, the birthday bound puts the 50% collision point near 2^61 (about 2.3 × 10^18) IDs. Independent machines can therefore generate UUIDs without coordination and face a negligible collision risk in any practical workload.

In a distributed system, which statement correctly describes a primary advantage of using UUID v4 values instead of sequential auto-increment integers as database primary keys?#

Options

Show answer

UUID v4 keys can be generated independently on many servers with no central counter, lock, or coordination round-trip, which is their main advantage over sequential auto-increment keys in distributed systems. The trade-offs are a larger 128-bit size and random insertion order that can fragment B-tree indexes.

Why:

The main distributed-systems advantage of UUID v4 is that each node can create a value from local randomness alone, so no central counter or lock is needed. Sequential auto-increment requires writes to pass through one authoritative counter or a coordination scheme, which becomes a bottleneck and single point of failure. The other statements describe disadvantages: a UUID is 128 bits, larger than a 64-bit integer; random v4 values cause index fragmentation and page splits rather than sorted inserts; and UUIDs are harder for people to read.

In Twitter's Snowflake ID-generation scheme, what is the full bit layout of a 64-bit ID, and why is the design time-sortable and suitable for distributed generation?#

Show answer

It generates 64-bit IDs laid out as: a 1-bit sign/unused bit (always 0, ensuring the ID is a positive integer), a 41-bit timestamp (milliseconds since a custom epoch), a 10-bit worker/machine ID, and a 12-bit per-millisecond sequence number. The timestamp prefix makes Snowflake IDs roughly sortable by creation time, the worker ID enables distributed generation without a central coordinator, and the sequence number disambiguates multiple IDs produced on the same node within the same millisecond.

Why:

Snowflake packs a 1-bit sign/unused field (always 0, occupying the MSB to keep the full 64-bit value positive), a 41-bit millisecond timestamp, a 10-bit worker ID, and a 12-bit sequence into a single 64-bit integer. Because the timestamp occupies the most significant bits after the sign bit, IDs increase monotonically with time and are naturally sortable. Each node is assigned a unique worker ID so no central lock is needed for uniqueness, and the sequence field handles the case of multiple IDs on one node within the same millisecond.

A distributed system must generate unique identifiers across many nodes with three requirements: each ID fits in 64 bits, IDs are roughly sortable by creation time, and no central coordinator is needed for each ID generation. Which approach satisfies all three requirements?#

Options

Show answer

Twitter Snowflake-style IDs satisfy all three requirements. They pack a millisecond timestamp, a worker ID, and a per-millisecond sequence into one 64-bit integer, so IDs sort roughly by creation time and each node generates them locally without a coordinator, provided every node has a distinct worker ID.

Why:

Snowflake-style IDs pack a millisecond timestamp, a per-node worker ID, and a per-millisecond sequence into a single 64-bit integer. The timestamp makes IDs roughly sortable by creation time, and each node can generate its own IDs locally without asking a coordinator, as long as every node has a distinct worker ID. An auto-increment column needs a shared primary database to hand out each value, so it fails the no-coordinator requirement. Random UUID v4 values are 128 bits and carry no time ordering, so they fail two requirements. A central ID service issues every ID through a single coordinator, which is exactly what the no-coordinator requirement rules out.

A team is choosing an ID-generation strategy for a high-write database where the primary index is a clustered B-tree. Briefly explain why UUID version 4 can degrade write throughput in this setting, and name one alternative ID-generation strategy that mitigates the problem.#

Show answer

UUID v4 generates 122 random bits, so each new key lands at an arbitrary position in the clustered B-tree instead of appending at the end. This forces frequent page splits, poor buffer-pool locality, and index fragmentation on every insert. Using time-ordered or sequential identifiers, such as a Snowflake ID, ULID, UUID v7, or an auto-increment column, appends new rows near the existing leaf pages, keeping the B-tree compact and writes fast.

Why:

UUID v4 values are random, so inserts scatter across the entire clustered B-tree rather than appending at the rightmost leaf. This causes page splits, poor buffer-pool cache locality, and index fragmentation, all of which hurt write throughput. A time-ordered or sequential ID scheme such as Snowflake, ULID, UUID v7, or an auto-increment column keeps new rows clustered near the end of the index, avoiding these costs.

Twitter Snowflake divides its 64-bit ID into four fields: 1 sign bit, 41 bits for the _____, 10 bits for the _____, and 12 bits for the _____.#

Show answer

Twitter Snowflake divides its 64-bit ID into four fields: 1 sign bit, 41 bits for the timestamp, 10 bits for the worker ID, and 12 bits for the sequence number.

Why:

The standard Snowflake layout is 1 sign bit (always 0), 41 bits for a millisecond timestamp (giving rough time-sortability and a lifespan of about 69 years), 10 bits for a worker/machine identifier (supporting up to 1024 nodes), and 12 bits for a per-millisecond sequence number (supporting up to 4096 IDs per node per millisecond). The three blanks are therefore the timestamp, the worker/machine ID, and the sequence number, in that order.

Each ID-generation scheme below is paired with a trade-off. Which pairings are correct? Select all that apply.#

Options

Pick every one that applies.

Show answer

Database auto-increment's primary trade-off is a single point of failure, because all writes funnel through one primary. Snowflake requires each node to have a unique worker ID, and UUID v4 is not time-sortable, which scatters B-tree index inserts.

Why:

Database auto-increment is simple and sequential, but every write goes through one primary, which makes it a single point of failure. Snowflake avoids a central coordinator, but each generating node needs a unique worker ID, typically assigned through ZooKeeper, etcd, or configuration. UUID v4 needs no coordination at all, but it is not time-sortable, so its random values scatter B-tree inserts across pages. The pairings that claim UUID v4 needs a coordinator and that Snowflake IDs are 128 bits are wrong: UUID v4 is generated locally, and Snowflake IDs are 64-bit.

Place these events in the lifecycle of a single Snowflake ID generation call on a worker node, from the moment the request arrives to the moment the ID is returned, in chronological order.#

Put these in order

Show answer

The lifecycle of a Snowflake ID generation call runs: read the current millisecond timestamp, compare it with the last timestamp used, increment or reset the sequence, pack the timestamp, worker ID, and sequence into a 64-bit integer, then return the ID. Each step needs the result of the one before it, so the order is fixed.

Why:

Each step depends on the output of the previous one. The worker must first read the clock, since nothing else can be decided without the current millisecond. It then compares that value with the last timestamp it used, because the comparison determines whether the sequence is incremented or reset. Only after the sequence is settled can the worker bit-pack the timestamp, worker ID, and sequence into the 64-bit value. Returning the ID is the final step, after the value has been composed.

True or False: In Twitter Snowflake IDs, the 41-bit millisecond timestamp field provides over 100 years of IDs before overflowing.#

Options

Show answer

No, the 41-bit millisecond timestamp in Twitter Snowflake covers only about 69.7 years. 2^41 milliseconds is roughly 2.2 trillion ms, which works out to about 69.7 years. Snowflake deployments set a custom epoch, such as Twitter's November 4, 2010, so the range extends well into the future.

Why:

2^41 = 2,199,023,255,552 milliseconds, which is about 2.2 trillion ms. Dividing by 1000 gives about 2,199,023,256 seconds, roughly 69.7 years. The field therefore covers about 69 years, not over 100. Snowflake implementations define a custom epoch (Twitter used November 4, 2010) instead of the Unix epoch of January 1, 1970, so the usable range extends into the future. Starting from the Unix epoch, the field would overflow around 2039.

Which alternative ID-generation scheme produces 64-bit identifiers composed of a timestamp, a worker ID, and a sequence number, so that many machines can generate IDs independently?#

Options

Show answer

Snowflake is the ID-generation scheme that packs a timestamp, a worker ID, and a sequence number into a 64-bit integer, so many machines can generate IDs independently. ULID and UUID v7 are 128-bit identifiers, and auto-increment is issued by a single database rather than generated in a distributed way.

Why:

Snowflake compresses a millisecond timestamp, a worker (machine) ID, and a per-millisecond sequence counter into a single 64-bit integer, which lets many nodes generate time-sortable IDs without a central coordinator for each ID. ULID and UUID v7 both use the 128-bit UUID-sized format rather than 64 bits, and auto-increment produces sequential integers from one database, which creates a coordination bottleneck instead of distributed generation.

In a Snowflake-style ID generator, true or false: if two worker nodes share the same worker ID and their clocks read the same millisecond, they can produce duplicate IDs.#

Options

Show answer

True. Two Snowflake-style nodes that share a worker ID and emit IDs in the same millisecond can produce identical (timestamp, worker_id, sequence) tuples. Each node keeps its own sequence counter starting at 0, so nothing prevents the collision. Unique worker IDs are required for uniqueness.

Why:

Snowflake's uniqueness rests on two invariants: each node has a distinct worker ID, and each node's sequence counter is private to that node. If two nodes share a worker ID and their clocks read the same millisecond, both start their sequence at 0 and can emit the same (timestamp, worker_id, sequence) triple, which is a genuine collision. This is why worker IDs must be assigned uniquely, through coordination such as ZooKeeper, etcd, or static configuration, and why clock skew must be bounded.

In Twitter's original Snowflake ID generation algorithm, the 10-bit field that identifies the generating node is called the _____, and the 12-bit field that disambiguates multiple IDs generated within the same millisecond on the same node is called the _____.#

Show answer

In Twitter's original Snowflake ID generation algorithm, the 10-bit field that identifies the generating node is called the worker ID, and the 12-bit field that disambiguates multiple IDs generated within the same millisecond on the same node is called the sequence number.

Why:

In Twitter's original Snowflake specification, the 10-bit worker ID (also widely called machine ID or node ID) uniquely identifies each generating node and supports up to 1024 nodes. The 12-bit sequence number (also called sequence or counter) increments within each millisecond on a given node, supporting up to 4096 IDs per node per millisecond before the generator must wait for the next millisecond. Pinning the question to Twitter's original Snowflake narrows the dialect, but several synonyms remain in common use for each field, so all widely-accepted alternatives are accepted.

A service must generate IDs that can be sorted by creation time. For each of these strategies (Snowflake, ULID, UUID v4, auto-increment integers), state whether it is suitable for this requirement and briefly explain why.#

Show answer

Snowflake: suitable, because each ID starts with a millisecond timestamp, so IDs sort by creation time. ULID: suitable, because it also starts with a timestamp and is lexicographically sortable. UUID v4: not suitable, because it is random and carries no creation-time information. Auto-increment: suitable, because values are sequential and roughly track insertion order, though it requires a single coordinating database to issue them.

Why:

Snowflake and ULID both place a timestamp in the high-order bits of the ID, so lexicographic or numeric order matches creation order, which suits time-ordered storage and range queries. Auto-increment integers are assigned sequentially and therefore roughly follow insertion order, so they are suitable when a single database issues them. UUID v4 is generated from random bits with no time component, so it gives no creation-time ordering and is unsuitable for this requirement.

True or False: UUID version 4 requires coordination between distributed nodes to avoid identifier collisions.#

Options

Show answer

False: UUID version 4 does not require coordination between nodes. Each node fills 122 bits with random values independently, so collisions are astronomically unlikely without shared state. Uniqueness is probabilistic rather than a strict mathematical guarantee.

Why:

UUID v4 fills 122 of its 128 bits with random values (the remaining 6 bits encode version and variant). Each node generates identifiers independently from that 2^122 space, so no inter-node coordination is needed. The collision probability follows the birthday bound, approximately N^2 / (2 * 2^122); for N = 10^9 this is about 10^18 / (2 * 5.3 * 10^36), roughly 10^-19. That is negligible but not zero, which is why UUID v4 is described as practically unique rather than guaranteed.

In a distributed system where IDs are stored in a database table with a clustered index, which TWO of the following are valid reasons to prefer a Snowflake-style ID over a UUID v4?#

Options

Pick every one that applies.

Show answer

Snowflake-style IDs are preferable to UUID v4 in a clustered index because they are 64-bit instead of 128-bit, which shrinks storage and index size. Their roughly time-ordered prefix also makes new rows append near the end of the index rather than causing random page splits and fragmentation.

Why:

Two advantages of Snowflake-style IDs hold in this scenario. First, they are 64-bit rather than 128-bit, which halves the per-value storage in columns, foreign keys, and index pages. Second, their timestamp prefix makes them roughly time-ordered, so new rows append near the end of the clustered B-tree instead of splitting pages at random positions the way random UUID v4 values do. The claim that UUID v4 values are time-sorted is false, since v4 is generated from random bits and has no timestamp. The claim that Snowflake IDs are unpredictable is also false: they embed a timestamp, worker ID, and sequence counter, so they are guessable and are not a security feature. Coordination is not a differentiator either, because both schemes can be generated without a central coordinator once worker IDs are unique.

You are designing a service that generates unique identifiers for events ingested by a microservices platform. Each event needs a globally unique id that is monotonically increasing per shard (events from the same source shard should preserve ordering). The platform ingests up to 50,000 events per second across hundreds of shards. Describe your approach to generating these ids, the trade-offs you considered, and how your design handles clock drift and shard failures.#

Show answer

I would use a Snowflake-style 64-bit id: a millisecond timestamp, a worker/shard id (for example 10 bits, about 1024 workers), and a per-millisecond sequence counter (for example 12 bits, 4096 ids per ms per worker). Each worker generates ids locally with no per-id coordination, and the capacity per shard (roughly 4 million ids per second) far exceeds the 50k/s total requirement. Ids from one shard increase because the timestamp dominates the ordering and the sequence orders ids within the same millisecond. The shard id is assigned at startup from a config service such as ZooKeeper and persisted, so a restarted worker does not reuse an id space it used before. For clock drift, the worker stores the last timestamp it issued and never issues an id with an earlier timestamp; if the clock jumps backward, it waits until the clock catches up (or refuses to issue ids and alerts) instead of generating stale or duplicate values. For shard failover, a standby may take over a shard id only after the original is confirmed dead, using a lease or heartbeat with a fencing token so two live workers never share the same worker id. I considered random UUIDv4 (coordination-free but not sortable and 128 bits) and a centralized database sequence (ordered but a single point of failure and a throughput bottleneck). Snowflake balances monotonicity, throughput, and operational simplicity.

Why:

This is an open design question, so it is graded by whether the answer covers the required elements rather than matching one fixed answer. A strong answer names a concrete scheme (a Snowflake-style id of timestamp, worker/shard id, and per-millisecond sequence), checks that its bit budget supports at least 50,000 ids per second across hundreds of shards, and explains why ids from one shard stay ordered. It should compare against non-sortable random UUIDs and a centralized sequence, and it should handle at least one operational hazard in detail, such as a backward clock jump (persist the last timestamp and wait) or shard failover (confirm the old worker is dead and prevent duplicate worker ids). Vague statements such as 'it scales well' do not earn credit on their own.

Which of the following statements about Twitter's Snowflake distributed ID generation scheme are true? Select all that apply.#

Options

Pick every one that applies.

Show answer

Snowflake-style IDs are 64-bit integers with a 41-bit millisecond timestamp in the high bits, a 10-bit worker ID, and a 12-bit sequence. Each worker generates IDs locally without a central database per ID. Ordering is only rough, not strict, and a backward clock jump left unhandled can produce duplicate IDs.

Why:

The true statements are that the 41-bit timestamp in the high-order bits makes IDs roughly sortable by creation time, that the layout includes a 10-bit worker ID and a 12-bit per-worker sequence, and that a backward clock jump left unhandled can make a worker reuse a timestamp and sequence pair, producing duplicates. The statement about a central database per ID is false because each worker generates IDs locally without coordination for every ID. The statement about strict global ordering is false because two workers in the same millisecond are ordered by worker and sequence bits rather than true generation order, so only rough ordering is guaranteed.

Design a distributed service that generates globally unique 64-bit IDs for a high-scale application (100k+ IDs/second across multiple data centers). The IDs must be roughly time-ordered (newer IDs sort after older ones) and fit in a 64-bit signed integer. Walk through your bit allocation, how you assign worker identifiers, how you handle clock drift and worker restarts, and how the design degrades under failures.#

Show answer

I would implement a Snowflake-style ID generator. The 64-bit ID is split into three fields: 41 bits for a millisecond timestamp (giving ~69 years of range from a custom epoch), 10 bits for a worker ID (up to 1,024 workers), and 12 bits for a per-millisecond sequence number (4,096 IDs/ms per worker, ~4M IDs/sec per worker). The timestamp occupies the high-order bits so sorting IDs numerically approximates sorting by creation time. Worker ID assignment: each worker acquires one of the 1,024 available worker-ID slots (0–1,023) through a coordination service (e.g., ZooKeeper) at startup. Specifically, the worker attempts to create an ephemeral node at a well-known path for each slot it tries (e.g., /workers/worker-<id>), starting from slot 0 and incrementing until it succeeds at creating a node that does not already exist. Because a slot node is only held while the worker's ZooKeeper session is alive, a crashed worker's ephemeral node disappears, freeing that exact slot for a replacement. The worker caches its assigned slot ID in memory during its session. If ZooKeeper is unavailable at startup, the worker refuses to serve IDs until it obtains a valid, non-conflicting worker ID—using a stale cached ID risks collision if the original holder is still alive. This slot-based scheme never reuses a slot that is still held, so the monotonic ZooKeeper sequential counter cannot cause two live workers to collide on the same ID. Clock drift handling: the worker tracks the last timestamp for which it generated IDs. Before generating a new ID, it reads the system clock; if the clock has moved backward (e.g., NTP step), the worker refuses to generate IDs with a timestamp earlier than the last-used timestamp, returning an error or sleeping until the clock catches up. This prevents duplicates from reusing a previous (timestamp, sequence) pair. Sequence exhaustion: if a worker generates 4,096 IDs in one millisecond, it does not wrap around—instead it busy-waits until the next millisecond. This bounds per-worker throughput at ~4M IDs/sec, well above the 100k/sec target. Failure degradation: if ZooKeeper goes down after a worker has its ID, the worker continues using its cached worker ID (the slot remains valid until its session expires). If ZooKeeper is down for a long period and workers restart, there is a collision risk if a session expires while the worker is still alive; a mitigation is to also encode a datacenter ID or use a persistent worker-ID registry (e.g., a database table with a unique constraint) alongside the ephemeral slot approach.

Why:

This design exercise evaluates whether the candidate can architect a Snowflake-like distributed ID generator. The key components are: (1) a 64-bit layout with a timestamp in the high-order bits for sortability, a worker ID for uniqueness across machines, and a sequence for uniqueness within a millisecond on a single worker; (2) a dynamic worker-ID assignment mechanism using a coordination service where each worker acquires one of 1,024 fixed slots via ephemeral nodes so crashed workers' IDs are reclaimed without collision risk from a monotonically increasing counter; (3) backward-clock-drift detection by tracking the last-used timestamp and refusing to go backward; and (4) graceful degradation when sequence bits exhaust (busy-wait to next ms) and when the coordination service is down (cached ID fallback with collision-risk awareness).

Design a service that generates unique 64-bit integer IDs for every event in a distributed system with ~10,000 writes/sec across 1,000 nodes. IDs must be roughly time-ordered so that events can be sorted by creation time without a separate timestamp column. Discuss your approach, how you guarantee uniqueness without a single point of failure, and the trade-offs compared to at least one alternative (e.g., UUID v4 or database auto-increment).#

Show answer

Use a Snowflake-style approach: each ID is a 64-bit integer composed of a 41-bit millisecond timestamp (custom epoch), a 10-bit worker/machine identifier, and a 12-bit per-millisecond sequence number. Worker IDs are assigned by a lightweight coordination service (e.g., ZooKeeper or a database table with a unique constraint) so each of the 1,000 nodes gets a distinct 10-bit ID (up to 1,024 workers). Within a single millisecond on a single node, the 12-bit sequence number disambiguates concurrent requests (up to 4,096 per ms per node, well above the 10 writes/sec/node average). Uniqueness holds because no two nodes share a worker ID and the sequence resets each millisecond, so the (timestamp, worker, sequence) triple is unique. Time-ordering holds because the high-order bits are the timestamp, so IDs sort numerically by creation time at roughly millisecond granularity. There is no single point of failure for ID generation itself: each node generates IDs locally, needing only clock synchronization (NTP). The coordination service is used only at startup to obtain a worker ID; if it is unavailable, a node can reuse a cached or range-based assignment. Compared to UUID v4 (122 random bits, 16 bytes, not sortable, but zero coordination and negligible collision risk), Snowflake yields compact 8-byte sortable IDs at the cost of worker-ID allocation and roughly synchronized clocks. Compared to database auto-increment (simple and strictly monotonic, but a write bottleneck and single point of failure at 10k/s), Snowflake avoids centralized writes. Edge cases: clock skew (a node whose clock moves backward should pause generation until it catches up), worker-ID exhaustion (2^10 = 1,024 workers is enough here), and timestamp rollover (41 bits ≈ 69 years from the epoch).

Why:

This is an open design question, so it stays a short-answer item graded against a keyword set and a reference answer. A strong answer proposes local generation (Snowflake-style) that avoids a central coordination bottleneck, identifies worker-ID allocation as the one piece needing external coordination, and places a timestamp in the high bits so IDs sort by creation time. It must also contrast the design with UUID v4 (larger and unsortable) or database auto-increment (a write bottleneck and single point of failure) to show understanding of the trade-off space. The keywords check for the core components (timestamp, worker, sequence) and at least one named alternative.

You're sharding a table across many database nodes and need globally unique primary keys generated at high throughput without a per-insert coordination round-trip, and you'd like them to be roughly time-sortable for index locality. Which approach best fits?#

Options

Show answer

Use Snowflake-style ids that pack a timestamp, a node id, and a per-node sequence counter — coordination-free, unique, and naturally time-ordered. Every node mints ids locally with no round-trip, and the timestamp prefix gives the index locality you want. A central sequence reintroduces the coordination and single point of failure; UUIDv4 is coordination-free but fully random, so it scatters B-tree inserts; and hashing row contents lets identical rows collide.

Why:

Snowflake-style ids compose a millisecond timestamp + a per-node id + a per-node monotonic counter, so every node mints unique, roughly time-sortable ids locally with no coordination — exactly the stated requirements. A central auto-increment sequence each node calls before every insert is unique but reintroduces the coordination round-trip and a SPOF the question rules out. UUIDv4 is genuinely coordination-free and a valid choice, but its full randomness defeats the time-sortability/index-locality goal — that's the honest trade that makes it second-best here. Hashing the row's own column values into the id is broken for keys: two rows with identical values collide, so it cannot guarantee uniqueness.

Design a URL shortener (think Bitly / TinyURL).#

Show answer

Requirements. Two operations dominate: create (write a mapping, optionally with a custom alias and expiry) and redirect (look up a code and 30x to the long URL). The system is overwhelmingly read-heavy (100:1), so the redirect path is what we optimise. I'd confirm: are custom aliases required (yes), do links expire (optional TTL), and how precise must click counts be (approximate/eventually-consistent is fine). I'd use a 302 (temporary) redirect so we keep serving redirects through our system and can still count clicks; a 301 would let browsers cache and bypass us.

Capacity. ~40 writes/sec average, ~200/sec peak; ~4K reads/sec average, ~20K peak. Storage: ~6B links over 5 years; at ~500 bytes/row (code, URL, metadata) that's ~3 TB — comfortably shardable. Keyspace: base62 with 7 characters gives 62⁷ ≈ 3.5×10¹² codes, far more than 6B, so 7 chars (often padded to a fixed length) is plenty. The hot working set (recently/ popularly accessed links) is a small fraction of 6B, so a cache of tens of GB covers the bulk of redirect traffic.

Data model. A single mapping table/collection keyed by the short code: code (PK) → long_url, created_at, expires_at, owner_id. Point lookups by primary key suit a key-value store (DynamoDB/Redis-backed) or a sharded relational table sharded by code. Click counts live separately — incrementing a counter on every redirect would put write load on the read path — so clicks are emitted as events and aggregated asynchronously.

Short-code generation. Generate a unique 64-bit id (a distributed counter handed out in ranges per server, or a Snowflake-style id) and base62-encode it to get the short code; this guarantees uniqueness with no collision checks and no hotspot from a single shared counter. Custom aliases are written directly with a uniqueness check and stored in the same table, reserving that code. (A hash-of-URL scheme is the alternative but needs collision handling and breaks idempotency for duplicate URLs.)

Read/write paths. Write: allocate id → base62 → insert mapping → return the short URL. Redirect: a load balancer / CDN fronts stateless app servers; the server does a cache-aside lookup (code → long_url) in Redis, falling back to the store on a miss and populating the cache, then returns a 302. This keeps p99 well under 100 ms for cache hits. Expiry is enforced by TTL on both the row and the cache entry. Each redirect fires a lightweight click event onto a queue (Kafka) that a consumer aggregates into per-link counts.

Bottleneck & scaling. The bottleneck is the redirect read path at peak. We scale it with cache layers (most reads never touch the store), read replicas, and sharding the mapping store by code so lookups stay single-shard. Id generation scales by handing each server an independent id range (or using Snowflake), avoiding a single global counter as a SPOF. At 10× traffic, the cache absorbs most of it; we add cache nodes and replicas and, if needed, push redirects further to the edge.

Why:

A URL shortener is the canonical warm-up design: it forces a clean separation of a heavily read-optimised redirect path from a comparatively rare write path. The two pivotal decisions are short-code generation (a base62-encoded distributed id avoids both collisions and the hotspot of a single shared counter) and caching (cache-aside on code→URL, fronted by a CDN/LB, is what meets the latency budget when reads outnumber writes 100:1). Click counting is deliberately moved off the redirect path and made asynchronous so analytics never slow a redirect. The dominant bottleneck is the redirect read path, scaled by caching, read replicas, and sharding the mapping store by code.

Design a distributed unique ID generation service that satisfies all of the following requirements: (1) produces 64-bit integer IDs; (2) IDs are monotonically increasing and lexicographically sortable by generation time; (3) the system must generate over 100,000 IDs per second across multiple data centers; (4) IDs must be globally unique with no single central bottleneck. Describe your architecture, the ID bit-layout, how worker/node IDs are assigned (including across data centers), and how you handle clock drift.#

Show answer

Use a Snowflake-style 64-bit integer layout: 1 unused sign bit (kept 0), 41 bits of millisecond timestamp (about 69 years from a chosen custom epoch), 10 bits of worker ID (1,024 generators), and 12 bits of per-millisecond sequence (4,096 IDs per ms per generator, roughly 4 million IDs/sec per generator). Generation is fully local: each application node calls its own in-process generator, so there is no central ID server on the hot path. Worker IDs must be unique across the whole fleet, including all data centers, so they are leased from a coordination service such as ZooKeeper or etcd (or allocated from a database table with a UNIQUE constraint on worker_id) at startup and renewed as leases; a common split is a 5-bit datacenter ID plus a 5-bit worker ID within it. Each generator keeps a last-issued timestamp and sequence: within the same millisecond it increments the sequence, and if the sequence overflows it waits for the next millisecond instead of wrapping. For clock drift, the generator keeps a watermark of the last timestamp it used; if the wall clock (for example after an NTP correction) reads earlier than the watermark, it refuses to issue IDs (or waits) until the clock passes the watermark, so its IDs never decrease. Because worker IDs are unique, two generators that share a millisecond still produce different IDs. Note that the IDs are strictly increasing per generator and time-ordered at millisecond granularity across generators, not a strict global sequence.

Why:

This is an open design question, so it stays a short-answer item graded against the keyword list and the reference answer rather than a fixed key. A strong answer must cover the four asked-for parts: the 64-bit layout with a time component in the high-order bits (which gives time ordering), unique worker IDs that are leased from a coordination service or a unique-constrained table and that must be unique across data centers, a per-millisecond sequence with overflow handled by waiting rather than wrapping, and clock-drift handling via a last-timestamp watermark that stops IDs from going backwards. The reference answer's throughput arithmetic is correct: 12 sequence bits give 4,096 IDs per ms per generator, about 4.1 million per second, far above the 100,000 per second target. The answer also avoids overclaiming: the layout gives strict monotonicity per generator and time-ordering across generators at millisecond granularity, and sortability holds for the numeric value of the 64-bit integer.

You are choosing an ID scheme for a globally distributed system where IDs serve as primary keys in a sharded MySQL cluster. Consider the following statements about Snowflake-style 64-bit IDs and UUID v4 (random) IDs. Which statements are correct? Select all that apply.#

Options

Pick every one that applies.

Show answer

Snowflake-style 64-bit IDs put a timestamp in their high-order bits, so numeric order follows creation time, and each generator needs a distinct worker ID assigned out of band. A UUID v4 has 122 random bits, since 6 of its 128 bits are fixed version and variant markers. A UUID v4 does not fit in a 64-bit BIGINT.

Why:

Snowflake-style IDs put the timestamp in the high-order bits, so ordering the numbers orders them by creation time, and this is correct. Uniqueness depends on each generator having a distinct worker ID, which must be assigned out of band (for example through ZooKeeper or a config table), so the statement that Snowflake needs distinct worker IDs is correct. A UUID v4 is 128 bits with 4 version bits and 2 variant bits fixed, leaving 122 random bits, so that statement is correct. The statement that UUID v4 fits in a BIGINT is false because BIGINT is 64-bit and UUID is 128-bit; it is the 64-bit Snowflake ID that fits. The statement that UUID v4 needs worker ID coordination is false because random generation needs no coordination, and its collision risk is negligible at 122 bits.

Design a globally unique ID generation service for a large distributed system with these requirements: (1) generate 64-bit IDs that are roughly sortable by creation time; (2) sustain at least 10,000 IDs/second across 1,000+ application servers; (3) have no single point of failure, so unique IDs keep being generated when individual nodes or a coordination service such as ZooKeeper or etcd are temporarily unavailable; (4) keep per-ID latency minimal, ideally sub-millisecond. Describe the architecture, the bit layout of the ID, how you handle clock skew and sequence exhaustion, and the trade-offs you considered.#

Show answer

I would use a Snowflake-style 64-bit layout: 1 sign bit (always 0), 41 bits of millisecond timestamp since a custom epoch (2^41 ms is about 69 years), 10 bits of worker/machine ID (1,024 workers), and 12 bits of per-millisecond sequence (4,096 IDs per ms per worker). Each application process generates IDs locally in memory, so there is no network call per ID and latency is well under a microsecond to a few microseconds. Throughput is far above the requirement: one worker produces about 4.1 million IDs/s, and the 10,000 IDs/s target needs only a tiny fraction of one worker. Worker IDs are leased at process startup from a coordination service (ZooKeeper, etcd, or a database row) and cached locally; if the coordinator is down, workers that already hold a lease keep generating IDs, so the coordinator is not on the hot path. For sequence exhaustion, if a worker has issued 4,096 IDs in the current millisecond it spins or waits until the next millisecond before issuing more. For clock skew, the worker records the last timestamp it used; if the wall clock moves backward by a small amount, it refuses to generate (or waits) until the clock catches up to the last timestamp, and for larger regressions it raises an alert and stops issuing IDs rather than risk duplicates. Trade-offs: IDs are only roughly time-ordered across machines because of clock skew and per-worker sequences; the design depends on NTP-disciplined clocks; the 10-bit worker field caps the fleet at 1,024 concurrent worker IDs, so worker-ID reuse and lease expiry must be managed; and the IDs leak approximate creation time and worker identity. Alternatives such as UUIDv7 or ULID avoid worker assignment but are 128-bit, and a centralized sequence (ticket server or database ranges) is simpler but reintroduces a single point of failure and a throughput bottleneck.

Why:

This is an open-ended architecture question, so it is graded by a keyword check plus reading the answer. A strong answer must describe a 64-bit time-sortable layout with a timestamp, worker ID, and sequence field, generate IDs locally so the coordinator is off the hot path, handle sequence exhaustion by waiting for the next millisecond, handle backward clock movement explicitly, and weigh trade-offs against alternatives such as UUIDv7/ULID or a centralized sequence. The sample answer computes the capacity correctly: 4,096 IDs per millisecond per worker is about 4.1 million IDs per second per worker, far above the 10,000 IDs/second target.

In Twitter's original Snowflake ID scheme, the 64-bit ID is structured as: 1 sign bit, 41 bits for a millisecond timestamp, 10 bits for a machine ID, and 12 bits for a sequence number (4,096 values per ms per machine). If a single machine generates more than 4,096 IDs within the same millisecond, what does the generator do? Describe its behavior and why it is necessary for uniqueness.#

Show answer

It blocks (waits or spins) until the clock advances to the next millisecond, then resets the sequence to 0 and resumes generating IDs. Wrapping the sequence within the same millisecond would reproduce an existing (timestamp, machine, sequence) tuple and cause ID collisions, so the generator waits for a new timestamp instead.

Why:

Snowflake's sequence field has 12 bits, so only 4,096 IDs can be distinguished per machine within one millisecond. When the sequence overflows in the current millisecond, the generator does not wrap around, because the same (timestamp, machine, sequence) tuple would then be reissued and collide. Instead it blocks until the system clock reaches the next millisecond, then resets the sequence to 0. This preserves uniqueness under burst load, since the timestamp component is different after the wait.

Related interview questions

Job market

See system-design salaries and hiring demand from live job postings.

The other 1 question

This page shows 25 and marks what you pick. That's as far as a page can go. A free account opens the other 1 and keeps every answer. What you miss comes back until it's right: after a day, then at longer gaps.

Start with this topic

Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan

Interview booked? Practise the job ad instead

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.