← Coinbase Interview Insights

Coinbase·Software Engineer·Technical Phone Screen·Senior

Senior
Jul 2026

Summary

Coinbase software engineering interview focused entirely on designing a mini in-memory database with cursor-based pagination. The problem sounds straightforward but the follow-ups get into some genuinely tricky territory around cursor encoding and duplicate scores.

Questions Asked (5)

Q1

Design an in-memory database that stores rows with an id, score, and payload, and implement a query that returns all rows where score is at or above a given minimum, sorted by score ascending then id ascending.

System DesignAlgorithms & Data StructuresData Modeling
Author's notes

The sorting part is easy enough, composite sort on score then id.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying requirements (e.g., data size, query frequency, concurrency) and then propose a data model with an index on score to support efficient range queries. Implement the query by scanning the index for scores >= min, sorting the results by score then id, and returning the rows.

Pro tip: Mention that for large datasets, a balanced BST or skip list can provide O(log n + k) query time, but for small in-memory data, a sorted array with binary search is simpler and faster due to cache locality. Also, discuss trade-offs between update and query performance.

1. Clarify Requirements

Ask about expected data size, query frequency, update frequency, concurrency needs, and whether the data fits in memory. This guides the choice of data structures.

2. Design Data Model

Define a Row class with id, score, and payload. Choose an in-memory storage structure (e.g., array, hash map, or tree) and an index on score to accelerate range queries.

3. Implement Query Logic

Use the index to efficiently find all rows with score >= min. If using a sorted structure, binary search to find the start position; otherwise, filter and then sort.

4. Sort and Return Results

Ensure results are sorted by score ascending, then id ascending. If the index already maintains score order, only sort ties by id; otherwise, sort the filtered results.

5. Analyze Complexity and Trade-offs

Discuss time and space complexity of the chosen approach, and compare alternatives (e.g., sorted array vs. balanced BST) in terms of update and query performance.

Key Points to Mention

  • Choice of data structure: sorted array with binary search, balanced BST, skip list, or B-tree, and their trade-offs.
  • Indexing on score to avoid full scans and support efficient range queries.
  • Sorting stability and tie-breaking: ensure secondary sort by id when scores are equal.
  • Time complexity: O(log n + k) for query with index, O(n log n) for sort if needed; space complexity O(n).
  • Concurrency considerations: read-write locks or copy-on-write for thread safety if needed.
  • Scalability: when data exceeds memory, consider disk-based structures or external sorting.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

Implement cursor-based pagination for the query above: the client sends a cursor representing the last row seen, and the server returns the next page of results plus a nextCursor, or null if there are nothing left.

System DesignAPI & IntegrationsTechnical Trade-offs
Author's notes

This is where things got interesting.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the query and data model, then explain how to encode a cursor (e.g., base64 of the last row's sort key) and use it in a WHERE clause to fetch the next page. Emphasize the importance of a stable sort order and how to handle edge cases like deleted rows or concurrent inserts.

Pro tip: Mention that cursor-based pagination is preferred over offset-based for large datasets because it avoids the performance penalty of OFFSET and provides consistent results even when data changes. Also, discuss how to make the cursor opaque and tamper-proof by signing it or using a unique identifier.

1. Clarify requirements and constraints

Ask about the query, sort order, page size, and whether the cursor should be opaque. Confirm if the data can change between requests and if consistency is required.

2. Design the cursor

Decide what the cursor represents (e.g., the last row's sort key and unique ID) and how to encode it (e.g., base64 JSON). Ensure it's opaque and optionally signed to prevent tampering.

3. Construct the database query

Use the cursor to filter results: WHERE (sort_key, id) > (cursor_sort_key, cursor_id) ORDER BY sort_key, id LIMIT page_size + 1. Fetch one extra row to determine if there's a next page.

4. Build the response

Return the page of results (excluding the extra row) and compute nextCursor from the last row if there are more results, else null. Ensure the cursor is properly encoded.

5. Discuss trade-offs and edge cases

Address performance (indexes on sort columns), handling of deleted rows, concurrent inserts, and why cursor-based is better than offset for large datasets.

Key Points to Mention

  • Use of a unique, sequential column (e.g., auto-increment ID or timestamp) as part of the cursor to ensure stable ordering.
  • Encoding the cursor as an opaque string (e.g., base64) to hide implementation details and allow future changes.
  • Query pattern: WHERE (sort_key, id) > (last_sort_key, last_id) ORDER BY sort_key, id LIMIT page_size + 1.
  • Fetching page_size + 1 rows to determine if a next page exists without an extra count query.
  • Handling edge cases: deleted rows, concurrent inserts, and ensuring the cursor remains valid.
  • Performance benefits: avoiding OFFSET for large datasets and leveraging indexes on the sort columns.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

How do you encode the cursor so it is stable and unambiguous across requests?

API & IntegrationsTechnical Trade-offs
Author's notes

Answered this right after fixing my earlier mistake.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Explain that cursor-based pagination requires encoding a stable, unique sort key (or composite key) into an opaque token, typically using base64-encoded JSON or a signed token. Emphasize that the cursor must be deterministic and resistant to data changes, and that you should avoid exposing raw database IDs or offsets. Discuss trade-offs between simplicity and security, and how to handle edge cases like duplicate sort values.

Pro tip: Mention that you sign or encrypt the cursor to prevent tampering and to hide internal implementation details, and that you include a version field for future-proofing. Also, note that using a composite key (e.g., timestamp + ID) ensures stability even when the primary sort key has duplicates.

1. Identify the stable sort key

Choose a column or combination of columns that uniquely and immutably identifies each row, such as a creation timestamp plus a unique ID. Avoid mutable fields like 'updated_at' or non-unique fields like 'status'.

2. Encode the cursor

Serialize the sort key values into a compact, opaque string, e.g., base64-encoded JSON. Optionally sign or encrypt it to prevent tampering and to hide internal details.

3. Ensure stability and unambiguity

Guarantee that the cursor remains valid across requests by using immutable data and a deterministic encoding. Handle duplicates by including a tiebreaker (e.g., ID) in the sort key.

4. Validate and decode on the server

On each request, decode the cursor, verify its integrity (if signed), and use the values to construct a WHERE clause that fetches the next page. Reject invalid or expired cursors gracefully.

5. Discuss trade-offs and edge cases

Address trade-offs like cursor size vs. security, performance implications of composite keys, and how to handle deletions or insertions that might affect pagination consistency.

Key Points to Mention

  • Use of composite keys (e.g., timestamp + ID) to ensure uniqueness and stability.
  • Base64 encoding of JSON or similar serialization for opacity and compactness.
  • Signing or encrypting the cursor to prevent tampering and information leakage.
  • Including a version field in the cursor for backward compatibility.
  • Avoiding offsets due to performance and consistency issues with large datasets.
  • Handling edge cases like duplicate sort values and data mutations between requests.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

When many rows share the same score, how do you ensure no rows are duplicated or skipped across page boundaries?

Algorithms & Data StructuresSystem Design
Author's notes

The tie-breaker id in the sort order is exactly what saves you here.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Explain that pagination with duplicate scores requires a deterministic total ordering, so you must include a unique tiebreaker (e.g., primary key) in both the ORDER BY and the cursor. Then describe how to use keyset pagination (seek method) with a composite cursor to guarantee no duplicates or skips.

Pro tip: Mention that offset-based pagination is fundamentally broken for this case because concurrent inserts or updates shift offsets, and that keyset pagination is the only robust solution. Also note that the tiebreaker must be immutable to avoid rows moving between pages.

1. Identify the problem with offset pagination

Explain that when many rows share the same score, OFFSET/LIMIT can duplicate or skip rows because the database's ordering is non-deterministic and offsets shift with data changes.

2. Define a deterministic total order

Add a unique, immutable tiebreaker column (e.g., primary key) to the ORDER BY clause so that every row has a stable, unique position in the sorted sequence.

3. Use keyset pagination with a composite cursor

Instead of OFFSET, use a WHERE clause that compares the (score, id) tuple against the last seen values, e.g., WHERE (score, id) > (last_score, last_id) ORDER BY score, id LIMIT n.

4. Handle edge cases and concurrency

Discuss how to handle inserts/deletes during pagination (e.g., using a snapshot or accepting that new rows may appear) and ensure the tiebreaker is immutable to prevent rows from moving between pages.

5. Optimize with proper indexing

Mention that a composite index on (score, id) is essential for performance, allowing the database to efficiently seek to the correct position without scanning.

Key Points to Mention

  • Offset pagination is non-deterministic with duplicate scores and can cause duplicates/skips.
  • Keyset pagination (seek method) uses a cursor based on the last row's sort key.
  • A unique tiebreaker (e.g., primary key) must be included in ORDER BY and the cursor.
  • Composite index on (score, id) is needed for efficient queries.
  • Concurrent inserts/deletes can still affect results; consider snapshot isolation or accept eventual consistency.
  • The tiebreaker should be immutable to avoid rows shifting between pages.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q5

What is the time complexity of your solution, and how would you scale it for datasets with up to a million rows?

Algorithms & Data StructuresTechnical Trade-offsSystem Design
Author's notes

Said O(n log n) for the sort plus O(n) for the scan, which is honest.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clearly stating the time and space complexity of your solution using Big O notation, then explain how it would perform with a million rows. Discuss potential bottlenecks and propose scaling strategies such as distributed processing, indexing, or algorithmic optimizations, tailored to Coinbase's high-volume transaction environment.

Pro tip: Demonstrate awareness of real-world constraints like memory limits and network latency, and mention how you'd validate performance with benchmarks or profiling. This shows you think beyond theoretical complexity and consider production readiness.

1. State Complexity Clearly

Provide the time and space complexity of your solution in Big O notation, specifying the variables (e.g., n = number of rows). Briefly explain why it's that complexity.

2. Analyze for 1M Rows

Estimate the actual runtime and memory usage for 1 million rows based on the complexity. Identify if it's feasible on a single machine or if it would cause performance issues.

3. Identify Bottlenecks

Pinpoint the parts of your solution that would become bottlenecks at scale, such as O(n^2) operations, high memory consumption, or I/O limitations.

4. Propose Scaling Strategies

Suggest concrete optimizations: algorithmic improvements (e.g., using hash maps, sorting), data structure changes, or system-level scaling (e.g., sharding, parallel processing, distributed computing).

5. Validate and Iterate

Explain how you would test the scaled solution, such as through load testing, profiling, or using smaller datasets to extrapolate performance.

Key Points to Mention

  • Big O notation for time and space complexity
  • Trade-offs between time and space (e.g., using more memory for faster access)
  • Specific data structures that improve efficiency (e.g., hash maps, heaps, tries)
  • Distributed processing frameworks (e.g., MapReduce, Spark) for horizontal scaling
  • Database indexing and query optimization for large datasets
  • Caching and memoization to avoid redundant computations

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.