Gemini runs a cryptocurrency exchange, and PracHub's notes place Software Engineers across the systems behind it: low-latency order matching, secure wallet services, account management, and dashboards and reporting tools for asset management. The role is described as owning features from design review through deployment and monitoring, working with product managers on requirements and with DevOps or SRE teams so that services can be deployed and observed.
The reported questions follow that work closely. Coding items include processing trade orders under constraints, a small utility for tracking account balances, a medium problem on arrays or hash maps, optimizing a given snippet, and explaining data structure trade-offs for a storage problem. Design items include processing and logging high-volume transaction data, handling concurrent wallet balance updates, rate limiting, keeping data consistent across microservices, and the considerations for a secure trading API. The question bank also lists SQL (currency conversion, joins with API calls and logging), a C++ scheduler, market data structure design, troubleshooting a scheduler and web service, and a mixer-style coding challenge that one bank item describes as a take-home.
The role description lists proficiency in at least one major language (Java, Python, Go or C++ are the examples), data structures and algorithms, and distributed systems as the core requirements, with fintech or crypto experience, AWS or GCP, and high-concurrency work listed as nice to have. For preparation, that means practising correctness under concurrency and retries, choosing exact types for money, and being able to explain every trade-off out loud, since the source notes stress how you reach an answer as well as the answer itself.
Preparation focus
editorialNo round sequence has been reported for the Gemini Software Engineer loop. Treat the reported categories (coding, system design, SQL, troubleshooting and behavioral) as preparation areas, ask your recruiter which of them each round covers, and check whether a take-home is part of your process, since a mixer-style take-home appears in the question bank.
What to demonstrate
- Clean, efficient code on arrays, hash maps and data structure trade-offs, including utilities that track balances or process trade orders
- System design for transaction-heavy services: throughput, failure modes, data integrity and security of a trading API
- Correct handling of shared financial state under concurrent requests and retries
- Clarifying constraints before starting and explaining the reasoning behind each decision
How to prepare
- Solve the reported coding prompts (trade order processing, account balance utility, currency exchange logic) using exact decimal or integer types for money
- Design high-volume transaction logging and concurrent wallet balance updates end to end, stating delivery guarantees and how you deduplicate
- Practise SQL joins and currency conversion queries, and one troubleshooting walkthrough for a cron job or single-threaded service
- Prepare answers to the five reported behavioral prompts, including a specific answer to why Gemini and why crypto
PracHub editorial advice for the preparation topics above.
Updating a wallet balance with a read, then a compute, then a separate write, so two concurrent requests both succeed on the same funds
When a concurrent wallet balance question comes up, name the lost-update race before you draw anything. Then pick a control and state its cost: a single conditional update (debit only where balance >= amount, and check the affected row count), a row lock held for the shortest possible transaction, or optimistic concurrency with a version column and a retry. Add an idempotency key so a client retry after a timeout cannot debit twice, and say whether the balance is a stored value or derived from ledger entries.
Holding balances or exchange rates in floating point, or rounding at several points in a conversion
Use integer minor units or an exact decimal type throughout the account balance utility, the trade order function and the currency conversion SQL. Binary floating point cannot represent 0.1 exactly, so sums drift. Decide where rounding happens and which mode applies, round once at that point, and say it out loud. In SQL, cast to numeric before multiplying by a rate, not after.
Starting to code the trade order function before pinning down what the constraints actually are
The reported prompt says 'with specific constraints', and those constraints decide the data structure. Ask about order types, whether partial fills are allowed, how ties at the same price are broken, what happens to an order that would overdraw a balance, and the input size. Then name the structure the answers point to (for example, a heap or sorted map per side of the book) and walk one small example through it before writing the full function.
Designing high-volume transaction logging as queue, then workers, then database, without saying what happens on a crash or a duplicate
State the delivery guarantee and where the acknowledgement sits relative to the durable write. Acknowledging after the commit gives at-least-once delivery, so deduplicate with a transaction or idempotency key and say how long that key is kept. Say how you keep per-account ordering if it matters, what you partition on, and what an auditor would query. The Metering ingest worked exercise below practises exactly this reasoning.
Answering 'Why Gemini and the cryptocurrency space?' with a generic line about interesting technical challenges
PracHub's notes flag this as a point where candidates separate themselves. Use the platform as a user before the interview, pick one concrete engineering problem an exchange has to get right (custody, order matching, account security), and connect it to something you have built. You do not need to be a blockchain expert, but you should be able to explain how an exchange works at a basic level.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Design a small utility function for account balance tracking.
Design a small utility function for account balance tracking.
Approach
- Name the brute-force solution and its complexity before improving on it.
- Walk one small example through your approach before writing the whole thing.
- State the target complexity and say which constraint rules the naive version out.
Follow-up
- What is the worst case, and how likely is it on real data?
- How does this change if the input no longer fits in memory?
Implement a function to process trade orders with specific constraints…
Implement a function to process trade orders with specific constraints.
Approach
- State the target complexity and say which constraint rules the naive version out.
- Walk one small example through your approach before writing the whole thing.
- Restate the input: its shape, its size, and what is guaranteed about it.
Follow-up
- How does this change if the input no longer fits in memory?
- What is the worst case, and how likely is it on real data?
Find peak concurrent sandbox usage from run intervals
Given up to 5 million job_run rows for one tenant over one day, with run_id, started_at, finished_at, status and wall_clock_limit_seconds, report the maximum number of sandboxes running at once, the earliest instant that maximum is reached, and the first run_id that would breach a per-tenant cap of C. started_at is null while a run is queued; finished_at is null both for runs still executing and for runs in status lost. Treat a run as occupying [started_at, finished_at). Give the complexity and state how you handle each null.
Approach
- Turn each run into two sweep events,
(started_at, +1)and(end, -1), then sort the 2n events by timestamp with-1ordered before+1at equal timestamps. That tie-break is what makes the interval half-open, so a run finishing at 10:00:00 and one starting at 10:00:00 never overlap. - Decide each null out loud before sweeping, because each choice moves the answer. A null
started_atmeans queued and contributes nothing. A nullfinished_atwith statusrunningorleasedis clipped to the window end. Statuslosthas no observed end at all, so clip it atstarted_at + wall_clock_limit_secondson the grounds that the supervisor owns the timeout, and record that you did. The table'scheck (finished_at is null or started_at is not null)guarantees you never see an end without a start. - Sweep once, maintaining a running counter, the maximum, and the timestamp at which the maximum was first attained (update
peak_atonly on a strict increase, or you will report the last such instant instead of the earliest). Capture the firstrun_idwhose+1takes the counter to C+1 during the same sweep rather than in a second pass. - Complexity: O(n log n) dominated by the sort, O(n) space. If rows already arrive ordered by
started_at, a min-heap of end times gives O(n log k) time and O(k) space with k the peak concurrency, which is the better shape when the rows come from an index scan on(tenant_id, started_at). - If second resolution is acceptable, counting-sort the endpoints into an 86,400-slot delta array and prefix-sum it: O(n + T) time and O(T) space, which beats the comparison sort at 5 million rows. It answers only at second granularity, so state which resolution the cap is defined in.
Worked solution 20 min
- Write the null policy as three lines of prose first, one per case, and keep them beside the output.
- Emit 2n endpoint tuples
(timestamp, delta, run_id)and sort on the key(timestamp, delta)so-1precedes+1. - Sweep, tracking
cur,peak,peak_atupdated only on a strict increase, and the firstrun_idwhose+1takescurto C+1. - Build a fixture with two runs where one ends exactly when the next starts, three genuinely overlapping runs, one run with a null
finished_atand statusrunning, and one with statuslostand a 300-secondwall_clock_limit_seconds. - Re-run with every timestamp shifted by a constant and confirm the peak is unchanged while
peak_atshifts by the same constant.
Follow-up
- Now report peak concurrency per tenant for 10,000 tenants from one globally sorted stream. What changes about memory and about the sort?
- The cap has to be enforced at dispatch rather than reported afterwards. What does the admission check look like, and where does it race?
- How would you answer 'peak concurrency within any 5-minute window' without re-sorting?
Migrate a live partitioned event table without blocking ingest
usage_event is range-partitioned daily on ingested_at, holds roughly 250M rows per day across 400 live partitions, and is written at 10-40k rows/second. Two changes are required: quantity must move from double precision to numeric(20,6), and a new environment column must become NOT NULL with a default of 'production'. Ingest cannot stop. Give the ordered plan, naming for each step the lock it takes, what that lock blocks, and roughly how long it is held. Identify the one step that cannot be rolled back cleanly once traffic depends on it.
Approach
- Classify the two changes before planning anything. Adding a column with a non-volatile default has been metadata-only since PostgreSQL 11, so it is cheap. Changing double precision to numeric is not binary-coercible, so
alter column ... typerewrites every partition under ACCESS EXCLUSIVE and rebuilds its indexes; on this volume that is hours of blocked ingest and is simply not an option, which is why the plan is expand-and-contract rather than one statement. - Expand: add
quantity_numeric numeric(20,6)andenvironmentwith its default on the parent. Both are catalogue-only but both take a brief ACCESS EXCLUSIVE that cascades to partitions, so run each withlock_timeoutset to a second or two and retry on failure. A queued ACCESS EXCLUSIVE request blocks every reader behind it, which is how a metadata-only change turns into an outage. - Dual-write: deploy producer code that populates both columns on every insert, and leave it running before anything reads the new column. This is the step that cannot be reverted cleanly. Once readers depend on quantity_numeric, reverting the writer leaves rows with a null there, and the gap is only discoverable by re-reading the old column, which the readers have stopped doing.
- Backfill older partitions in batches keyed on the primary key, oldest first, committing every few thousand rows with a pause between batches, and skipping the partition still receiving writes until it rotates. Each batch is an ordinary UPDATE taking row locks only. The cost is bloat and WAL rather than blocking, so watch dead tuples and let autovacuum keep pace instead of wrapping 400 partitions in one transaction.
- Make NOT NULL cheap with the three-step form:
add constraint ... check (environment is not null) not valid(brief ACCESS EXCLUSIVE, no scan), thenvalidate constraint(SHARE UPDATE EXCLUSIVE, scans while reads and writes continue), thenset not null, which from PostgreSQL 12 uses the validated check and skips its own full scan. Do this per partition, then on the parent. - Switch and contract: move reads to the new column behind a flag, verify over a full period that both columns agree on freshly written rows, drop the old column (metadata-only), and only then remove the dual-write. Any index on the new column goes on with CREATE INDEX CONCURRENTLY per partition, since CIC is not supported on a partitioned parent: create the parent index with ONLY, build each child concurrently, then ALTER INDEX ... ATTACH PARTITION until the parent index becomes valid.
Follow-up
- A CREATE INDEX CONCURRENTLY fails halfway through the partition list. What state is the table in, how do you detect it, and what do you run?
- The producer computes quantity itself. What happens to a request already in flight when the dual-write deploy lands, and does it matter?
- Give two queries that prove the backfill is complete: one cheap enough to run every minute, one authoritative.
Explain why the metering dashboard scans every daily partition
usage_event is range-partitioned daily on ingested_at and holds tenant_id, workspace_id, environment, sku, quantity numeric(20,6), occurred_at and ingested_at. The only relevant index is on (occurred_at). A dashboard runs select sku, sum(quantity) from usage_event where tenant_id = $1 and date_trunc('hour', occurred_at) >= $2 and environment = 'production' group by sku, and EXPLAIN shows a sequential scan of every partition. Give each distinct reason, rewrite the predicate so an index can serve it, propose the index, and state the write cost its column order adds.
Approach
- Separate the three causes rather than blaming one. First,
date_trunc('hour', occurred_at)wraps the column, so the predicate is not sargable against a btree on the bare column. Second, pruning keys off ingested_at while the query constrains occurred_at, so no partition can be excluded. Third, even made sargable, (occurred_at) is not tenant-leading, so for one tenant among thousands the scan reads the whole time range and discards nearly all of it. - Rewrite the bound carefully, because the obvious rewrite is only conditionally equivalent.
date_trunc('hour', x) >= $2equalsx >= $2only when $2 is already hour-aligned; for an arbitrary $2 it meansx >= date_trunc('hour', $2) + interval '1 hour'. Normalise the parameter in the caller and leave the column bare. - Restore pruning with a second, redundant predicate on the partition key:
ingested_at >= $2 - interval '<late-data horizon>'. State both sides of it. It prunes to a handful of partitions, and it silently omits any event whose ingest lagged past that horizon, which is precisely what a producer replay produces. Either document the horizon as a stated bound, or partition on occurred_at and move the problem into the dedup window instead. - Propose
(tenant_id, occurred_at) include (sku, quantity)per partition. A partial indexwhere environment = 'production'mostly saves size rather than selectivity, since production dominates the three environments; take it if non-production is a meaningful share and skip it otherwise. - Price the write path honestly. At roughly 250M rows/day each extra index is another insert plus WAL per row, and a tenant-leading key scatters inserts across one hot leaf per active tenant instead of appending to a single rightmost leaf, so page dirtying and random I/O both rise. An INCLUDE payload widens every leaf entry and enlarges the index accordingly.
- Add the index-only-scan caveat before someone reports it as a regression: on a freshly appended table the visibility map is not yet set for recent pages, so the INCLUDE columns still cost heap fetches until autovacuum has been through, and the newest hour is exactly the data the dashboard reads.
Worked solution 30 min
- Build 30 daily partitions with skewed tenants, one holding about 40% of the rows, then ANALYZE.
- Run
explain (analyze, buffers)on the original query and record how many partitions were scanned and the rows removed by filter. - Apply the rewritten predicate and the index, re-run, and confirm the plan lists only the partitions inside the ingested_at bound.
- Re-run with $2 set to a non-hour-aligned timestamp and confirm the rewritten and original predicates return identical rows.
- Insert an event with ingested_at six hours past occurred_at and check whether the pruning predicate excludes it.
Follow-up
- CREATE INDEX CONCURRENTLY is not supported on a partitioned parent. Give the sequence that gets this index onto 400 existing partitions without blocking ingest.
- One tenant holds 200 times the median row count and the dashboard still times out for them with the index in place. What changes?
- Should this read hit
usage_rollup_hourlyinstead? State what that costs in freshness and what the watermark lets you promise.
Design a system for processing and logging high-volume transaction dat…
Design a system for processing and logging high-volume transaction data.
Approach
- Choose a partition key and say what query it makes expensive.
- Fix the scope first: who calls this, how often, and what they do when it fails.
- State the consistency you need, and where you are willing to be stale.
Follow-up
- How does this behave when that dependency is down for an hour?
- What breaks first when traffic grows ten times?
Metering ingest that survives a six-hour producer replay
metering-ingest consumes usage events at-least-once - 250M/day, 10-40k/second at peak - and folds them into usage_rollup_hourly keyed (tenant_id, workspace_id, sku, hour_start). usage_event is partitioned daily on ingested_at with unique (ingested_day, tenant_id, idempotency_key). A producer outage ends in a six-hour replay that re-sends events already ingested, some of whose originals crossed midnight. Design the consumer: partitioning, where the acknowledgement sits relative to the commit, the deduplication horizon and its storage cost, and how the rollup watermark advances. Nothing may be double-counted and nothing may be silently dropped.
Approach
- Choose the acknowledgement position deliberately and name what each choice costs. Acknowledging after the fold commits makes the consumer at-least-once: a crash between the two replays the batch and produces duplicates, which are ordinary and absorbable. Acknowledging first makes it at-most-once: a crash between the two drops revenue with no error raised anywhere and no way to detect it later. Take at-least-once and design everything downstream to absorb duplicates.
- Put the dedup and the fold in one transaction so there is no window between them. Insert the batch into usage_event with ON CONFLICT DO NOTHING, take the rows actually inserted, and fold only those into usage_rollup_hourly with an upsert on (tenant_id, workspace_id, sku, hour_start) bucketed by occurred_at, not ingested_at. A batch of about 2,000 rows is one round trip and one index probe per event.
- Attack the partition-key flaw head on: the unique index includes ingested_day because a unique index on a partitioned table must contain the partition key, so the same (tenant_id, idempotency_key) re-sent after midnight is a different index entry and passes. Deduplicate instead against a store keyed (tenant_id, idempotency_key) with no date component, whose horizon exceeds the producer's maximum retry window plus the longest replay you intend to support. At 14 days that is 250M x 14 = 3.5 billion keys, which is a dedicated key-value store, not a larger index on the same table. The alternative - partitioning usage_event on (tenant_id, occurred_day) so the natural key is stable - fixes dedup but loses pruning on ingest time and makes retention by dropping partitions awkward.
- Partition the consumer by hash of tenant_id so one tenant's replay stalls only its own partitions, and give replay traffic a separate lower-priority lane so live ingest keeps its latency. The cost is explicit: that tenant's watermark lags while the replay drains, and everything gated on the watermark waits for it.
- Define the watermark as a property of committed work, not of wall-clock time: per partition it is the largest occurred_at such that every event with a smaller occurred_at has committed, and the sealing decision uses the minimum across partitions. Record source_max_ingested_at on every rollup row so any number can prove what it did and did not include, and keep restatement legal only while status = 'open' - after sealed_at the value is frozen and a late event becomes an invoice adjustment instead.
Worked solution 40 min
- Write the consumer loop in pseudocode with the acknowledgement after the commit, then annotate each line with what is lost or duplicated if the process dies exactly there.
- Size the dedup store: events/day x horizon_days keys, bytes per key including the tenant prefix, and the resulting memory or disk. Compare that cost against simply extending retention on the partitioned table and say why the latter does not fix the problem.
- Take one event ingested at 23:59:58 and replayed at 00:00:04 and work out its fate under (a) the partitioned unique index alone and (b) the separate dedup store.
- Write the per-partition watermark formula, then what the seal uses, then what a single stalled partition does to sealing.
Follow-up
- The dedup store is lost entirely. What can you still guarantee, and how do you rebuild it from what remains?
- A replay delivers events for an hour that is already sealed. Trace exactly what happens to them, row by row.
- One partition is stuck on a poison message, so the minimum-across-partitions watermark never advances and no tenant can be sealed. What is your escape hatch and what does it cost in correctness?
Metering partition crash-loops and the sealing watermark freezes
One metering-ingest partition has stopped advancing. Lag grows linearly, the consumer restarts about every 40 seconds, and the same offset appears in every startup log while other partitions stay healthy. Events are committed in batches of a few thousand and the acknowledgement follows the commit. Sealing is six hours away and source_max_ingested_at for that partition's tenants is frozen. Give an ordered checklist, a containment action available within minutes, and the durable fix, saying what each does to exactly-once accounting.
Approach
- Distinguish a poison record from a capacity problem in one measurement: compare the offset and the exception across restarts. An identical pair every time is deterministic failure on one record, whereas a throughput problem still advances the offset between crashes.
- Read the record from a separate consumer group so the bytes can be inspected without perturbing the stuck consumer, then classify the defect: schema violation, a quantity failing the non-negative check, a null workspace, an unmappable SKU enum, or a payload past a size limit. That classification decides whether this is a producer bug or a missing consumer guard.
- Account for batch granularity before acting. With commits of a few thousand, one bad record fails thousands of good ones, so the blast radius is the batch. Halve the batch around the offset to isolate the record, or move to per-record error isolation so the radius becomes the record.
- Contain by diverting that record to a dead-letter store with its raw bytes and offset, then resume. This is safe here precisely because the acknowledgement follows the commit: the good records from the failed batch are re-consumed and absorbed by the uniqueness check on (tenant_id, idempotency_key) rather than counted twice.
- Make the fix durable with per-record error isolation, a bounded poison counter, and an alert on dead-letter rate rather than on lag alone, since lag only reveals this after the sealing margin has already been eaten.
- Check the horizon before replaying anything. The unique index lives on a daily-partitioned table and therefore includes the partition key, so it deduplicates within a day only; a replay landing on a later ingest day needs the separate dedup store or it double-counts into a tenant's bill.
Follow-up
- Move the acknowledgement before the commit and describe exactly what is lost and what is duplicated in each of the two crash windows.
- Sealing is in six hours and the partition will not drain in time. What do you seal on, and what does the invoice have to record so the difference is explainable later?
- A producer replays two weeks of events next month. Which part of your fix stops holding, and what is the dedup horizon you would actually configure?
For a candidate senior enough that the loop turns on design and judgement rather than on whether the coding round gets finished. Five days build one system properly and then stress it; coding gets a single maintenance day, on the assumption that the risk at this level is an unexamined tradeoff rather than a missed algorithm.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Numbers and requirements for one transaction system
- Pick the reported design question on processing and logging high-volume transaction data and carry it through days 1-5 as your one system.
- Spend ten minutes writing only requirements: functional scope, a p99 latency target, a durability expectation, the consistency each read needs, and an explicit out-of-scope list.
- Produce a capacity estimate from stated assumptions (transactions per day, peak-to-average factor, bytes per record) and derive peak write rate and a year of storage, writing every assumption down.
Deliverable: A one-page requirements list with numeric targets and a capacity estimate whose assumptions are all written beside it.
Practice prompt ↗Practice prompt ↗Worked solution ↗02Interface, schema and money types
- Define the endpoints for the system before any boxes, marking which are idempotent and how a client supplies an idempotency key.
- Write the data model for transactions and account balances using integer minor units or an exact decimal type, and decide whether a balance is stored or derived from ledger entries.
- Write out the considerations for a secure trading API against that interface: authentication, per-key permissions, request signing and replay protection, rate limiting, and validation of order parameters.
Deliverable: An endpoint list, a schema with the access pattern that justifies it, and a secure API checklist for the same design.
Practice prompt ↗Practice prompt ↗03Concurrency and consistency
- Add concurrent wallet balance updates to the design: identify the lost-update race, then compare a conditional update, a row lock and optimistic versioning, noting what each costs under contention.
- Write a strategy for data consistency across microservices for one multi-service write, naming where you accept eventual consistency and where you do not.
- Work the 'Find peak concurrent sandbox usage from run intervals' exercise below to practise reasoning about overlapping work and tie-breaking at equal timestamps.
Deliverable: A written concurrency design for wallet updates with an idempotency key, and a one-paragraph consistency decision for a multi-service write.
Practice prompt ↗Practice prompt ↗04Failure is the design
- Decide where the acknowledgement sits relative to the durable write, state the resulting delivery guarantee, and design deduplication with a stated key retention.
- Work the 'Metering ingest that survives a six-hour producer replay' exercise below to practise acknowledgement placement and deduplication horizons.
- Rehearse the reported scheduler and web troubleshooting topic: a cron job that silently stops and a single-threaded service that stalls under one slow request, with the first three checks for each.
- Work the 'Metering partition crash-loops and the sealing watermark freezes' question as an ordered checklist: diagnosis, containment and the durable fix.
Deliverable: A failure section for your design (guarantee, dedup, retry policy) plus two ordered troubleshooting checklists.
Practice prompt ↗Practice prompt ↗Worked solution ↗05Scaling, reading and observing the hot path
- Design a data structure for high-volume, multi-symbol market data, stating what is read most often and what that implies for memory layout and updates.
- Sketch a rate-limiting service in front of your API and state what the caller is told when it engages.
- Write SQL for currency conversion by joining transactions to a rates table on the correct effective date with exact numeric types, then work the 'Explain why the metering dashboard scans every daily partition' exercise below.
- List the logs, metrics and alerts you would add so the failures from day 4 show up before customers notice.
Deliverable: A scaling and observability section for the design, two tested SQL queries, and a market data structure write-up with its trade-off stated.
Practice prompt ↗06Coding maintenance day
- Implement the reported account balance utility: deposit, withdraw and balance queries in integer minor units, rejecting overdrafts and invalid amounts at the boundary.
- Write down the constraints you would ask about (order types, partial fills, tie-breaking, overdraw rules), then implement a trade order processing function against them.
- Solve one medium array or hash map problem in thirty minutes, then explain the data structure trade-offs behind your choice and write tests for empty input, the boundary, and the case most likely to break.
Deliverable: Two financial-state functions and one medium problem, each with tests and the clarifying questions you would ask written beside it.
Practice prompt ↗07Defend it while being interrupted, and behavioral
- Run a design mock on your day 1-5 system with an interviewer briefed to change a requirement halfway and to push on one number you estimated.
- Write STAR answers for the five reported behavioral prompts: a technical disagreement, why Gemini and crypto, a project you are proud of, tight deadlines or shifting requirements, and how you learn a new domain.
- Use the Gemini platform as a user and write down two engineering problems it has to solve well, to use in your 'why Gemini' answer.
Deliverable: Mock notes recording how your design changed under the new requirement, and five written behavioral answers.
Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
The behavioral questions reported for this role cover disagreement, ownership, pressure, learning and motivation for the crypto space. Structure each answer with STAR and say plainly which part you personally drove. For the motivation question, anchor your answer in a specific engineering problem an exchange has to get right and in something you have built, not in general interest in crypto.
Disclose a cross-tenant webhook delivery to affected customers
An enqueue path took the subscription from one lookup and the payload from another. For nineteen minutes, webhook_delivery rows were created whose tenant_id did not match the subscription's tenant, and eleven payloads were signed and sent to four endpoints belonging to other customers. You hold payload_digest, delivery timestamps and response codes. Describe how you handle a disclosure of this kind: what the records prove, what they cannot prove, what you say before you know everything, the one code change that closes it, and which parts you personally drove.
Approach
- Bound the population before saying anything externally. The affected set is deliveries in the window where the event's tenant and the subscription's tenant differ; the ones that actually left are those with delivered_at set and a 2xx in last_response_code. Attempted and delivered are two different counts and a disclosure has to use the right one in the right sentence.
- Separate what the records prove from what they do not, and say both halves rather than the flattering one. They prove which payloads were signed, where they went, and — through payload_digest — exactly which bytes. They do not prove what the receiving system did with them, and they do not bound the window more precisely than your deploy timestamps do.
- Communicate on the facts you hold, with the scope stated as an upper bound: 'at most eleven payloads, four recipient endpoints, these fields, this window' is more useful and more honest than waiting a day for certainty. The field list matters more than the event count, because a customer cannot assess exposure from 'an event'.
- Name the code change precisely, because this class never originates in the delivery worker. Compare the event's tenant against the subscription's tenant at enqueue and again immediately before the payload is signed, and make the second comparison drop the delivery rather than log a warning. Say why one check is insufficient: the enqueue check protects against the bug you know about, the pre-signing check protects the boundary itself.
- Run the history question in parallel and say so: a query over historical deliveries for the same mismatch tells you whether this was nineteen minutes or a year, and you would rather find the second case yourself than have a customer find it after your disclosure.
- Split the response into workstreams with owners — recipients asked to delete, affected customers notified, the check landed with a test, history swept — and say which you personally drove and which you handed off. Claiming all four is not credible and claiming none is not ownership.
Follow-up
- The historical sweep finds two more instances from last year. What changes in what you have already told people?
- Who approves the wording, and what do you do when you are asked to soften the scope?
- A customer asks you to prove a redelivery contained the same bytes as the original. What do you show them?
Reverse a webhook ordering decision after measuring its cost
You argued for strict per-subscription ordering in webhook-delivery, which means one in-flight attempt per subscription. It shipped. Three months later a single unresponsive endpoint holds one subscription's queue at a six-hour backlog, and two customers report events arriving out of order anyway once their own retries are counted. Describe a decision you reversed: what you originally optimised for, the measurement that changed your mind, what the reversal cost in engineering time and customer change, and how you told the people who had already built on the original guarantee.
Approach
- State the original decision as a trade you made knowingly. Ordering across a network requires a single in-flight attempt per subscription, and its price is head-of-line blocking whenever one endpoint is slow. 'We priced it wrong' is a much stronger opening than 'we did not realise', and it is usually the true one.
- Bring the measurement that flipped it, not the anecdote: backlog age at the ninety-ninth percentile per subscription, the share of subscriptions where one slow endpoint gated an otherwise healthy queue, and the delivery throughput lost to serialisation. A reversal justified by complaints is indistinguishable from a reversal justified by fatigue.
- Name what you learned about the guarantee itself, which is the engineering content of this story. At-least-once delivery means a retried event already arrives after newer ones and the consumer already must be idempotent, so a guarantee the customer has to defend against anyway was never worth what it cost to provide.
- Describe the migration, because reversing a published contract is the hard half and the part candidates skip. Parallel attempts behind a per-subscription flag, a monotonically increasing sequence number added to the envelope so order-sensitive consumers can sort or discard, documentation that states at-least-once and unordered in those words, and a deprecation measured in quarters because the client is a pinned SDK inside a build pipeline you cannot see or redeploy.
- Give the cost in the two currencies that matter: engineer-weeks, and how many customers had to change code. Then say who you told before it shipped rather than in a changelog afterwards, and which large customer you left on the old behaviour and for how long.
- Close with the signal you now weight differently, stated as something you would do earlier next time: measuring the blocking cost on the slowest decile of endpoints before committing to the guarantee, rather than after a customer noticed.
Follow-up
- A customer insists they need ordering. What do you offer them that is not global serialisation?
- How did you choose the deprecation window given that you cannot see or redeploy the clients?
- What would have to be true for you to reverse back?
Ship metered billing with a named deduplication horizon
Metered billing must be on in three weeks. usage_event is partitioned daily, so its unique index must include the partition key and deduplicates only within a day: a producer retry that crosses midnight, or a replay run a week later, gets through. A cross-partition dedup store is two weeks you do not have. Describe shipping with debt you named in advance: what you shipped, what you wrote down, the detector you added, the trigger and date for paying it off, and what you would have refused to ship under the same pressure.
Approach
- Show you can separate the two kinds of debt, because that distinction is what the question actually probes. Debt that costs engineering time later is shippable on a deadline. Debt that silently corrupts a number a customer gets charged for is not shippable unless the corruption is detectable, and detectability is the whole negotiation.
- Make the exposure narrow and measured rather than gestural. The hole is duplicates whose occurrences straddle a UTC day boundary, plus any replay older than partition retention. Measure it before arguing about it: how often an idempotency_key recurs at all, and the distribution of the gap between first and last occurrence. If the ninety-ninth percentile of that gap is four minutes, the residual risk is a small band around midnight and you can say so numerically.
- Add the detector before the feature, not after. A nightly job counting keys that appear in more than one partition is one grouped scan over recent partitions, and it converts a silent overcount into a page. State what it costs to run and what it fires on.
- Buy the cheap half of the real fix immediately: extend partition retention so the dedup horizon exceeds the producer's maximum retry window plus the longest replay you intend to support. That reframes retention as a correctness parameter rather than a storage cost, which is the sentence you need on record before someone optimises the bill.
- Make repayment mechanical instead of aspirational: a dated entry with a named owner, plus a threshold that pulls the date forward — first detector hit above N events, or first customer dispute. Debt with a trigger gets paid; debt with only a date does not.
- Answer the second half honestly by naming what you would refuse under identical pressure: the sealing path, because a sealed row is frozen and a wrong number there stops being a bug and becomes an adjustment line, a dispute and an audit question.
Follow-up
- The detector fires on forty duplicate events for one tenant, and two of their invoices have already sealed. What happens next?
- Whom did you tell that the billing numbers had a known hole, and in what words?
- Finance asks you to cut storage by shortening partition retention. What do you say, and to whom?
- 01
Tell me about a time you had a technical disagreement with a teammate; how did you resolve it?
- 02
Why are you interested in joining Gemini and the cryptocurrency space?
- 03
Describe a project you are particularly proud of and your specific contribution to it.
- 04
How do you handle tight deadlines or shifting project requirements?
- 05
What is your process for learning new technologies or domains?
Is this an official Gemini interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at Gemini. Rounds and questions reflect what candidates have reported, not a process Gemini has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗How long does the interview process typically take?
PracHub's notes put most processes at roughly three to six weeks, varying by team. Ask your recruiter for the expected timeline and which question categories each stage covers, then pace your preparation so system design is solid before the later stages.
PracHub interview research ↗Are the coding questions extremely difficult?
The reported coding prompts lean practical rather than trick-based: a trade order processing function, an account balance utility, a medium array or hash map problem, and optimizing a given snippet. Some bank items are harder, such as a C++ scheduler and a mixer-style challenge. Prepare to write clean, tested code and explain the complexity, not just to reach an answer.
PracHub interview research ↗What is the best way to prepare for the behavioral questions?
Use STAR (Situation, Task, Action, Result) and prepare a story for each of the five reported prompts: a technical disagreement, why Gemini and crypto, a project you are proud of, tight deadlines or shifting requirements, and learning a new domain. Pick stories that show collaboration, how you handled a failure, and your own contribution.
PracHub interview research ↗Should I know a lot about crypto to succeed?
You do not need to be a blockchain expert, but you should understand at a basic level how an exchange operates and be able to explain why the space interests you. Using the Gemini platform as a user before your interviews makes product and design answers more concrete.
PracHub interview research ↗Should I expect SQL as a Software Engineer?
The question bank lists SQL items for this role, including currency conversion with SQL and a question combining SQL joins, API calls, error handling and logging. Practise joins against an effective-dated rates table, aggregation with exact numeric types, and explaining how an index would serve your query.
PracHub Software Engineer practice ↗Is there a take-home assignment?
One bank item is described as a Bitcoin mixer take-home, and a related Jobcoin mixer coding challenge also appears. Ask your recruiter whether a take-home is part of your process. If it is, treat it like production code: clear module boundaries, tests, error handling, and a short README that states your assumptions and what you would do with more time.
PracHub Software Engineer practice ↗Sources & methodology 3 sources ↗
Official role evidence, timestamped platform data and clearly labeled preparation advice.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
platform · Accessed 2026-09-24 - 02PracHub Software Engineer practice ↗
Cross-company practice questions for this role.
platform · Accessed 2026-09-24 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
platform · Accessed 2026-09-24