Sierra builds an AI platform that enterprise businesses use to deploy autonomous conversational agents, mainly for customer service. Bret Taylor and Clay Bavor co-founded the company. Software Engineers there work where agent workflows meet production engineering: serving LLM inference, integrating third-party APIs with low latency, and building infrastructure that keeps non-deterministic model calls within enterprise reliability and security requirements.
The reported responsibilities are mostly platform work: distributed systems that stream LLM output; tooling to build, test, deploy and monitor production agents; secure authentication (SSO, RBAC, mTLS) for client integrations; CI/CD; cloud resources managed with Terraform; observability; and on-call rotations with root-cause analyses and postmortems. If your experience touches any of these, prepare to discuss it concretely: how you ran services on Kubernetes or Docker, what you traced and alerted on, and how an incident you worked on was detected and closed.
The interview material follows the same mix. In reported questions, candidates call a flaky mock API and build a data structure from its JSON, resolve product IDs through retries and fallbacks, debug a multi-file Python agent framework against a specification diagram, and defend the architecture of a take-home agent. Candidates also report that a test suite may not be provided, so write production-ready code with your own tests and complete error handling, and spend your preparation on engineering fundamentals rather than ML theory.
Technical Screen
reportedCandidates describe the technical screen as focused on real-world API processing and resilient code rather than abstract puzzles. The sources do not tie specific questions to this round, but the reported questions in the Coding and Practical API Engineering category match that focus. One calls a mock API that fails intermittently, retries, parses multi-level JSON, and turns the product list into a linked list. Another resolves fallback product IDs through dependent endpoints, with retries, ordering and caching. Candidates report that a test suite may not be provided in early technical rounds, so write production-ready code with your own unit tests from the start. From the first line of code, treat the endpoint as unreliable and the payload as untrusted, and say so out loud as you go.
What to demonstrate
- Whether your client survives a failing endpoint: bounded retries with backoff, a defined stopping point, and no silent data loss when it stops
- Whether your JSON parsing handles missing keys, nulls, empty arrays and unexpected nesting without crashing or inventing values
- Whether the structure you build from the payload (a linked list, a set of resolved product objects) is correct, including for empty and single-element inputs
- Whether you write unit tests for edge cases and network failures without being asked
How to prepare
- Build a stub endpoint that returns 500s, timeouts and malformed JSON on a schedule. Write a client for it that retries with exponential backoff and jitter and stops after a fixed number of attempts
- Practise the full reported pattern: fetch, retry, parse, sanitize, then turn an array into a singly linked list. Test an empty payload, a single item and a missing field
- Solve a fallback-resolution variant: try the primary IDs, fall back on failure, keep the input order, cache repeated lookups, and stop recursion on dependency cycles
- Practise sanitizing a raw JSON payload: for each missing or null field, decide whether to skip, default or reject the record, and write one test per decision
Onsite Rounds
reportedCandidates report that the onsite combines a live debugging session on a multi-file repository, a presentation of an agent take-home or a system design exercise, and system architecture discussion. Reported debugging questions include a Python agent framework of four to five files plus a functional specification diagram, where the task is to find the logic errors that stop the agent from selecting the right tool. Related debugging questions mention execution loops with race conditions or missing error fallbacks, and an upstream mock API failure that corrupts downstream state. Candidates on agentic or platform infrastructure teams report a take-home that they present to engineering leadership. The sources do not say which design prompts the onsite uses. For practice, the questions reported in the System Architecture and ML Infrastructure category include an agent orchestration platform that calls third-party tools with fault isolation and low-latency token streaming, a multi-tenant system with RBAC, SSO and secure data boundaries, and a caching layer for LLM retrieval that avoids returning stale context.
What to demonstrate
- Whether you trace control flow across files against the specification diagram before editing, and can name the file that owns the broken behavior
- Whether your fix addresses the cause (a dropped parameter, a missing fallback, a race) and comes with a test that fails before the fix and passes after, without breaking existing tests
- Whether you can defend your take-home decisions on tool-calling schemas, retries, state and evaluation, including what you would change
- Whether your design isolates failures per tool call and per tenant, and says how streaming latency and cache staleness are kept in bounds
How to prepare
- Pick an open-source Python project that calls tools or APIs. Have someone plant two or three logic bugs in different files, and practise finding them by reading the call path aloud before running anything
- Write a one-page decision log for your take-home. For each choice, list the alternative you rejected, the failure it protects against, and how you tested it
- Sketch each of the three reported system architecture category prompts once. Name the tenant boundary, the timeout and retry policy for each tool call, and the cache key and invalidation rule
- Work through the webhook fan-out worked exercise on this page for per-endpoint isolation and backoff, then apply the same ideas to isolating third-party tool calls
Behavioral Alignment
reportedThe final stage is described as behavioral alignment with company leadership and cultural fit. Reported behavioral questions cover three areas. The first is owning a project end to end, from design to production, under tight deadlines. The second is resolving a technical disagreement over speed-to-market versus long-term craftsmanship. The third is handling a production issue or outage, including the fix and the postmortem process you put in place. Link each technical decision in your stories to a customer or business outcome, make the customer impact concrete, and keep your own decisions clearly separate from the team's.
What to demonstrate
- Whether your ownership story runs from design to production, with your own decisions clearly separate from the team's
- Whether your disagreement story shows how you weighed speed against long-term code quality and how the disagreement ended, not just who was right
- Whether your outage story covers detection, immediate mitigation, root cause and the process change that followed
- Whether each story names the customer or business outcome your work changed
How to prepare
- Write one story for each reported prompt (ownership, disagreement, outage). For each, note the decision point, the options, what you chose and the effect on the customer
- Prepare a clear career walkthrough and a strengths-and-weaknesses answer; both appear in the question bank for this role
- Practise being cut off mid-answer, since handling interruptions is also in the bank. Give the one-sentence version first and expand only when asked
PracHub editorial advice for the preparation topics above.
Coding against the mock API as if it always returns clean JSON on the first call
Reported coding questions use endpoints that fail intermittently and payloads with dynamic or missing fields. Wrap the call in bounded retries with backoff and decide what the code does after the last failure. Validate each field before you use it, and say out loud which malformed records you skip and which you reject. Write the failing-endpoint test before the happy-path one, so that resilience is part of your solution from the start.
Waiting for a test suite that never arrives
Candidates report that a test suite may not be provided, so write production-ready code with your own tests. Write small, isolated tests as you go: an empty payload, a single item, a missing key, a failure on the first attempt followed by success, and retries running out. Naming these cases before you finish shows that you handled edge cases deliberately rather than by luck.
Editing the multi-file agent codebase before tracing how a request flows through it
In reported debugging questions, the codebase has four to five files and comes with a functional specification diagram. Start at the entry point and follow one request through to the tool invocation, checking each step against the diagram. State a hypothesis before changing anything. Fix the cause, not the symptom: if a parameter is being dropped, find where it is lost rather than adding a default downstream. Then rerun the existing tests, since breaking them is an easy way to lose credit for a correct diagnosis.
Presenting the take-home agent as a feature demo instead of an architecture defense
The reported take-home question asks for the architecture, the tool-calling strategy, trade-offs, reliability and evaluation. Lead with the decisions. Explain how tool input and output schemas are validated, what happens when a tool call errors or times out, how conversation state and context size are managed, and how you measured response quality. Name one thing you would change. A working demo is only the starting point, so lead with why the agent is built the way it is.
Behavioral stories that stop at the technical fix, with no customer outcome or follow-through
The reported prompts ask about end-to-end ownership, a disagreement over speed versus craftsmanship, and an outage with a postmortem. End each story with the customer or business effect and with the lasting change you made, such as a new test, alert, runbook or review step. A story that ends at 'the bug was fixed' leaves out the part these prompts ask about.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
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.
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?
Order a job dependency graph and find its critical path
A workspace defines up to 50,000 jobs with up to 200,000 dependency edges and an estimated duration_seconds per job. Given the edge list, reject the graph if it contains a cycle and name one cycle's nodes; otherwise return a valid execution order, the earliest possible completion time with unlimited workers, and the set of jobs whose slack is zero. Then say which single job to shorten in order to cut the completion time, and by exactly how much. State the complexity of each part.
Approach
- Kahn's algorithm for the order: compute indegrees, seed a queue with zero-indegree nodes, emit and decrement. O(V + E), which at 50,000 and 200,000 is milliseconds. If fewer than V nodes are emitted, the graph contains a cycle.
- Kahn detects a cycle but cannot name one. The nodes left with indegree above zero contain every cycle, so run one DFS restricted to that residual subgraph with three-colour marking and report the stack slice from the grey node the back edge points at. That is the difference between a usable error message and 'dependency cycle detected'.
- Earliest completion with unlimited workers is the longest path, which is NP-hard on a general graph and linear on a DAG. State the precondition, then relax in topological order:
earliest_finish[v] = duration[v] + max(earliest_finish[u] for u in preds(v)), taking the max over an empty predecessor set as zero. The makespan T is the maximum over all nodes. O(V + E). - Second pass in reverse topological order for
latest_finish, thenslack[v] = latest_finish[v] - earliest_finish[v]. Zero-slack nodes form the critical path, and there can be several disjoint critical paths, so return the set rather than one chain.slack[v] = 0is exactly the statement that some longest path runs through v; equivalently, the longest path through v has lengthT - slack[v]. - The speed-up bound is the point of the question, and the obvious form of it is wrong. Shortening a zero-slack job v by d, with 0 <= d <= duration[v], cuts the makespan by
min(d, T - L_avoid(v)), whereL_avoid(v)is the longest path in the graph with v deleted: the longest path that avoids v, not the second-longest path overall. The two coincide only when the runner-up path misses v. Counterexample: A of 10 s feeds both B of 5 s and C of 4 s, so T = 15 s and the second-longest path is 14 s, yet shortening A by 10 s leaves a makespan of 5 s. The realised gain is the full 10 s, because both paths ran through A and shrank together, whilemin(10, 15 - 14)predicts 1 s. The reason is structural: shortening v reduces every path through v by d and leaves every other path alone, so the new makespan ismax(T - d, L_avoid(v)). - Compute
L_avoid(v)the direct way: delete v and re-run the same forward relaxation, O(V + E) per candidate. The cheaper equivalent skips the deletion, sinceL_avoid(v)only ever matters through that max: setduration[v] := 0, recompute the makespan asT0(v) = max(T - duration[v], L_avoid(v)), and the gain ismin(d, T - T0(v)), which is identical for every d <= duration[v]. Only zero-slack jobs are candidates, because shortening a job with positive slack changes the completion time not at all. One relaxation is milliseconds at this size, so ranking a critical set in the hundreds costs O(k(V + E)) and is worth doing exactly; a critical set in the tens of thousands is not, and there you evaluate a shortlist, longest jobs first, and say that the answer is the best of that shortlist rather than the optimum.
Worked solution 30 min
- Build four fixtures. A: 12 jobs, two branches of 100 s and 95 s that share no job. B: fixture A plus one back edge. C: two disjoint paths tied at 100 s. D: the shared-prefix case, one job of 10 s feeding a 5 s job and a 4 s job, so the longest path is 15 s and the runner-up is 14 s.
- Run Kahn; on fixture B confirm it emits fewer than V nodes, then run the residual-subgraph DFS and print the actual cycle.
- Compute
earliest_finishforward andlatest_finishbackward, and list the zero-slack set for each fixture. - For each zero-slack job v, recompute the makespan with
duration[v] := 0to getT0(v), and record both the correct boundT - T0(v)and the wrong one,T - second_longest_path, side by side. - Apply the shortening for real (20 s off the critical branch of A, 10 s off the shared prefix of D) and diff the recomputed makespan against each prediction.
Follow-up
- Only m workers are available. What happens to your answer, and what can you still promise about the schedule you produce?
- Edges arrive incrementally as the customer edits the pipeline. How do you detect a cycle at insert time without re-running Kahn over 250,000 elements?
- Durations are estimates. How would you express completion time as a distribution, and what breaks about the critical path once you do?
Locate a billing reconciliation gap without rescanning ninety million events
A tenant's sealed invoice total is 0.4% below the sum of its raw usage_event rows for the period. That tenant has 90 million events over 30 days in a table partitioned daily on ingested_at, and its rollups carry source_max_ingested_at, revision and sealed_at. Recomputing all 30 days from raw is correct, and you are not going to do it. Give the procedure that locates the divergent (workspace, sku, hour) cell, the cost of each probe, and the one query you run before any of it.
Approach
- Run the free query first. Sum raw quantity for the period restricted to
ingested_at <= source_max_ingested_atof the sealed rollups, and compare that against the unrestricted sum. The rollup stores the watermark precisely so this can be answered without a scan. If the whole 0.4% sits above the watermark, nothing is broken: it is late data, it becomes an adjustment line, and the investigation ends in one query. - Only if the gap survives that test do you bisect, and you bisect by dimension rather than by rows. Compare 30 per-day totals, then inside the offending day compare the 6 SKUs, then the workspaces, then the 24 hours. That is roughly 30 + 6 + W + 24 grouped probes, each an indexed range scan over one daily partition for one tenant, against O(N) per attempt for the naive re-fold.
- Quantify why naive is not merely slow but unusable mid-incident: at a generous 200,000 rows/second sequential, 90 million rows is about 7.5 minutes per attempt, you will want ten attempts, and every one competes for I/O on the same partitions live ingest is writing. The diagnostic worsens the backlog it is diagnosing.
- Before fetching each comparison, state what it would look like under each hypothesis. Two adjacent hours off by equal and opposite amounts is
occurred_atversusingested_atbucketing. A whole day offset by exactly N hours is a timezone applied at the wrong layer. A gap confined to one SKU in one workspace is an environment filter. The same(tenant_id, idempotency_key)present in twoingested_daypartitions is the dedup horizon losing a retry that crossed midnight. - Make the next bisection cheap by storing the aggregate you keep recomputing. A per-
(tenant_id, ingested_day)count and quantity checksum turns step two from thirty probes into one read, and it is the same number the reconciliation job already produces. - Whatever you find, the sealed period does not change value. The correction is an adjustment line pointing at the line it reverses, carrying its own
source_rollup_watermark, because the original invoice is the evidence of what the customer was charged.
Follow-up
- The gap is 0.4% in one direction on one day and 0.4% the other way the next day. What does that shape rule in, and what does it rule out?
- How do you distinguish a duplicate from a restatement, given
revisionandrecomputed_aton the rollup? - Ingest is still running while you investigate. What makes your two numbers comparable at all?
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.
Rebuild an hourly rollup with deduplication and late-arrival accounting
From usage_event (event_id, tenant_id, workspace_id, environment, sku, quantity numeric(20,6), idempotency_key, occurred_at, ingested_at), produce the values usage_rollup_hourly should hold for one tenant over one day: per (workspace_id, sku, hour_start) the deduplicated quantity_sum, event_count and source_max_ingested_at, bucketed by occurred_at. Duplicates share (tenant_id, idempotency_key). Also report, per hour, the running total across the day and the share of quantity that arrived more than two hours after the hour began. Write the query, and state which duplicates a daily unique index cannot catch.
Approach
- Deduplicate in its own CTE before any aggregation, because a SUM cannot be un-summed:
row_number() over (partition by tenant_id, idempotency_key order by ingested_at, event_id) = 1. Include the tiebreaker. Without it the surviving row is non-deterministic when two duplicates share an ingested_at, and a rollup described as deterministically recomputable then disagrees with itself between runs. - Bucket on occurred_at and nothing else, and pin the timezone explicitly.
date_trunc('hour', timestamptz)truncates in the session's TimeZone setting, so the same query run by a session set to a non-UTC zone buckets differently; use the three-argumentdate_trunc('hour', occurred_at, 'UTC')on PostgreSQL 16 or later, ordate_trunc('hour', occurred_at at time zone 'UTC') at time zone 'UTC'before that. Filterenvironment = 'production'explicitly, since metering covers three environments and billing covers one. - Aggregate to the grain with
sum(quantity),count(*)andmax(ingested_at). The last is not decoration: it is the watermark the row consumed up to, and without it there is no way to prove afterwards what a number did and did not include. - Compute the late share inside the dedup-and-aggregate step as a conditional aggregate,
sum(quantity) filter (where ingested_at > hour_start + interval '2 hours'), then divide by the hour's total. Compute the running total as a window over the already aggregated rows:sum(quantity_sum) over (partition by workspace_id, sku order by hour_start rows between unbounded preceding and current row). Running either over raw rows puts the duplicates back. - Answer the index question exactly. The unique constraint is on (ingested_day, tenant_id, idempotency_key), because a unique index on a partitioned table must contain the partition key. It therefore deduplicates only within one ingest day and admits a duplicate whose retry crosses midnight or whose replay runs a week later. That is why this CTE dedups across the whole window being recomputed, and why the dedup horizon is a correctness parameter rather than a retention cost.
- Keep the numeric type all the way through. quantity is numeric so the sums are exact; a cast to double precision anywhere in this pipeline reintroduces drift that surfaces only as a few unreconcilable cents per tenant per month, long after the query is out of anyone's mind.
Follow-up
- A dispute forces the same recompute over 40 days for one tenant. What changes about the dedup CTE's memory use and the chosen plan, and what would you do about it?
- Two runs a minute apart return different quantity_sum values for an hour that is already closed. Give two mechanisms that produce that, and the single query that distinguishes them.
- Express the same rollup incrementally so it does not re-scan the day each time the watermark advances. What does the incremental version stop being able to answer?
Webhook fan-out with per-endpoint isolation and backoff
One domain event fans out to every matching subscription, producing a webhook_delivery row per (subscription_id, event_id, redelivery_seq). Peak unique event rate is 20k/second; attempts run five to ten times that once fan-out and retries are counted. One customer endpoint has returned 503 for six hours and its backlog holds days of events; every other customer must be unaffected. Design the delivery system: how a worker claims work, the backoff schedule, the per-endpoint circuit breaker, the queue partitioning, and whether you offer ordering per subscription. State the delivery guarantee in one sentence.
Approach
- State the guarantee first, because it determines the rest: at-least-once with a stable event_id, and the consumer documented as responsible for idempotency. Exactly-once over HTTP is not deliverable - the 200 can be lost after the customer has already committed - so any design that promises it is either lying or is really offering at-most-once.
- Partition work per subscription rather than into one global pool, with a concurrency cap per subscription. With a shared pool, the endpoint that has been dead for six hours consumes workers on retries that will fail, and every other customer's delivery latency rises: head-of-line blocking across tenants is the exact failure being designed against here.
- Claim by compare-and-set with a fencing token: UPDATE webhook_delivery SET status = 'in_flight', lease_token = $new, leased_until = now() + interval '60 seconds' WHERE delivery_id = $1 AND status IN ('pending','failed_retryable') AND (leased_until IS NULL OR leased_until < now()), and make the terminal write carry AND lease_token = $new so a paused worker's late write is rejected rather than overwriting a newer attempt. Find due work through the partial index on next_attempt_at WHERE status IN ('pending','failed_retryable'), so the scan is proportional to live rows rather than to the terminal rows that outnumber them by orders of magnitude.
- Use full jitter: sleep uniformly in [0, min(cap, base x 2^(attempt-1))]. Plain exponential backoff hands a recovering endpoint its entire backlog as one synchronised herd and knocks it over again; full jitter de-correlates it. Then check the schedule actually spans the retention you promise - with base 1 s and a 3,600 s cap, twenty attempts have an expected total elapsed time of only about 4.6 hours, so a twenty-four-hour promise needs roughly fifty-nine attempts or a larger cap.
- Trip a circuit per endpoint on consecutive failures or a failure ratio over a rolling window: stop dispatching, push next_attempt_at out or mark new deliveries dropped_circuit_open, and half-open with exactly one probe rather than a batch. Bound the backlog explicitly with a per-subscription cap or retention, and decide in advance whether a recovered endpoint receives six hours of events at full rate or a pointer telling it to fetch what it missed.
- Offer ordering only as an opt-in mode of one in-flight attempt per subscription, and price it honestly: with parallel attempts a retried event overtakes a newer one, so ordering requires serialisation, and serialisation means one slow endpoint blocks its own queue entirely. That converts a shared problem into that customer's own problem, which is the right place for it, but it is still a real cost.
Worked solution 35 min
- Compute the attempt rate: 20k unique events/second x mean fan-out x retry multiplier, and size worker pools and the per-subscription concurrency cap from it.
- Write the claim statement and the terminal write, and point at the clause that rejects a resumed worker's stale write.
- Tabulate the backoff for attempts 1 to 20 with base 1 s and cap 3,600 s, take the expected value of each full-jitter sleep as half its ceiling, and sum to get total expected elapsed coverage.
- Decide the policy for a subscription down six hours: events buffered, bytes held, and what the customer actually receives when it returns.
Follow-up
- The endpoint recovers. Does it receive six hours of events at full rate, and what does that do to it?
- Trace the exact code path by which an event belonging to one tenant could be signed and sent to another tenant's endpoint.
- A customer insists they never received an event your row marks delivered. What evidence do you have, and what does payload_digest let you prove?
Design the batch ingest endpoint metering agents retry into
A customer-run agent posts usage events in batches of up to 1,000 to metering-ingest with a 30-second timeout and at-least-once retry of the whole batch. Each event carries event_id, idempotency_key, sku, quantity and occurred_at; the server adds ingested_at, and usage_event is partitioned daily on ingested_at with unique (ingested_day, tenant_id, idempotency_key). Design the endpoint: the request shape, what the response says when 900 events are new, 90 are duplicates and 10 are malformed, the status code, and the agent's algorithm on timeout. Then state the deduplication horizon and justify it against that unique constraint.
Approach
- Fix the per-item outcome taxonomy first, because the status code follows from it: accepted, duplicate, and rejected with a permanent code. A duplicate is a success; reporting it as an error makes the agent either re-send revenue it already delivered or drop it.
- Allow only permanent failures per item. A transient per-item failure inside a 200 invites the agent to discard that event, so anything transient escalates to a 5xx for the entire batch. A 200 is then a promise that every event not marked rejected is committed and durable.
- Return 200 with a results array aligned by index and carrying the event id, so the agent can retry precisely the subset that needs it and quarantine the ten malformed events instead of hard-looping a poison batch forever. Cap the batch at 1,000 items and a byte size, with 413 beyond it and 429 with Retry-After for backpressure.
- Deduplicate per event, never per batch: the agent may split, merge or reorder a retried batch, so a batch-level key matches nothing on the second attempt. The key is (tenant_id, idempotency_key), and the tenant comes from the resolved credential; a tenant id present in the body is compared against it, never trusted.
- Size the horizon as a correctness parameter. The unique index includes the partition key, so it deduplicates only within one day: a retry that crosses midnight, or a replay run a week later, passes straight through it. A separate dedup store keyed (tenant_id, idempotency_key) with a TTL exceeding the agent's maximum retry window plus the longest replay you intend to support is what actually enforces the invariant, which makes its retention a correctness setting rather than a cost knob.
- Order the commit against the response and the acknowledgement: commit then respond at the endpoint, and downstream commit the fold then acknowledge the message. Acknowledging first turns a crash into silently lost revenue with no error raised anywhere.
Follow-up
- The agent times out at 30 seconds having received nothing. What exactly does it do next, and what in your design makes that safe?
- Ten events are rejected every hour for a week and nobody notices. What does the endpoint owe the customer beyond a per-item 4xx code?
- A replay pushes 40 million events through this endpoint in an hour. Which part of your design degrades first?
Invoice detail latency triples after an ORM relationship refactor
An invoice detail endpoint returned in 40 ms at p99 last week. After a refactor replaced a hand-written join with ORM relationship access it returns in 1.4 s, and the regression grows with the number of invoice_line_item rows on the invoice. Database CPU rose, but no statement in the slow-query log exceeds 3 ms. You have request traces with per-span SQL, the ORM statement log, and a staging copy of the data. Produce an ordered diagnostic checklist, the measurement that confirms the cause before any code change, and the fix.
Approach
- Count statements per request before reading any statement duration. A slow-query log hides this class by construction, because every individual query is fast and only their number is wrong; take one trace and count SQL spans.
- Establish proportionality rather than asserting it: sample invoices with 5, 20, 60 and 200 line items and plot statements per request against line count. A straight line of slope 1 through an intercept of one or two identifies a lazy relationship load, and no index or cache would move that line.
- Locate the emitting attribute access in the refactored code and check whether the same shape repeats one level deeper, for instance a tax or adjustment collection hanging off each line, which turns the cost quadratic.
- Fix with a bounded statement count: either one join that fetches invoice and lines together, or two statements where the second is WHERE invoice_id = $1 AND tenant_id = $2. Keep tenant_id in the predicate so the read stays tenant-scoped even though invoice_id already implies it.
- Choose between the two deliberately: the join duplicates the wide parent row across N children on the wire, the two-statement form avoids that for one extra round trip. Prefer the join for narrow parents and the split for wide ones.
- Pin it with a per-request statement-count assertion in a test that varies line count, because a latency assertion passes on a small fixture and would not have caught this.
Follow-up
- The endpoint now also needs per-line tax rows. Show the shape that keeps statement count constant instead of reintroducing the same defect one level down.
- How does this change if a transaction-pooling proxy sits between the service and the database, so each statement may land on a different backend session?
- The same page paginates invoices with LIMIT and OFFSET. Why is that a second, independent defect, and what replaces it?
For someone fluent in a dynamic language who has shipped real work but has never had to say what the runtime is doing underneath. The week is built on measuring and deliberately breaking things, because the questions that expose this background are the ones where the interviewer asks why a second time.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Coding and API engineering: flaky mock API to linked list
- Stub an endpoint that returns a JSON array of products but sometimes fails with a 500 or a timeout, and sometimes omits fields or nests them one level deeper.
- Write a client with bounded retries, exponential backoff with jitter, and a clear error after the last attempt. Then parse and clean the result and build an ordered singly linked list from it.
- Write your own tests before you call it done: an empty array, a single item, a missing field, a failure followed by success, and retries running out.
Deliverable: A working client and linked-list builder with at least five self-written tests, including two network-failure cases.
Practice prompt ↗Practice prompt ↗Worked solution ↗02Coding and API engineering: fallbacks, dependency resolution and grids
- Solve a Resolve Product IDs with Fallbacks variant: retry a failing endpoint, fall back to secondary IDs, keep the input order, and cache lookups so a repeated ID costs one call.
- Extend it to recursive dependency resolution, with a visited set so that a cyclic dependency stops with a clear error instead of recursing forever.
- Work through the dependency-graph worked exercise on this page (topological order, naming a cycle, critical path) to practise cycle detection and graph complexity.
- Write an iterative DFS and a BFS over a 2D grid with blocked cells: count the connected clusters and find a shortest route.
Deliverable: A fallback resolver with caching and cycle protection, plus grid traversal code tested on blocked, empty and single-cell grids.
Practice prompt ↗Practice prompt ↗03Coding breadth from the question bank
- Solve the question bank's algorithm questions without looking up patterns: First Unique Character Index, Binary Search in Sorted Array, Lowest Common Ancestor, Check Hankel Matrix Property and Max Sum of Non-Adjacent Elements.
- Add the two bank questions for this role: splitting Markdown into header-aware chunks, and processing time intervals to find overlaps and gaps. State the complexity before you code each one.
- Review the four pillars of OOP and model a small class hierarchy for a chess game (the bank includes Design a Chess Game and OOP Classes in Practice). Name where you use encapsulation and polymorphism.
Deliverable: Seven solved problems, each with its complexity stated and edge-case tests, plus a class diagram for a chess game.
Practice prompt ↗Practice prompt ↗04Onsite: multi-file debugging
- Pick a small Python project of four or five files that calls external tools or APIs. Have someone plant bugs in different files: a dropped parameter, a missing exception fallback and a shared-state bug.
- Find the bugs while narrating: start at the entry point, follow one call to the tool invocation, and state a hypothesis before each edit.
- For each fix, write a test that fails before it and passes after, then rerun the existing tests to confirm nothing else broke.
- Work through the ORM latency debugging practice problem on this page to practise measuring before you change code.
Deliverable: A log for each bug: the symptom, your hypothesis, the file where it lived, the fix, and the test that pins it.
Practice prompt ↗Practice prompt ↗Worked solution ↗05Onsite: take-home agent architecture defense
- Build or revisit a small tool-calling agent: define input and output schemas for two tools, validate the model's output against them, and retry or fall back when a tool call fails.
- Write a one-page decision log covering how context is kept, the retry and fallback policy, schema validation, and how you evaluate response quality.
- Present it aloud to someone who interrupts with 'why not the alternative?' and 'what happens when this tool times out?', and note any question you could not answer.
Deliverable: A decision log and a presentation outline that leads with trade-offs, reliability and evaluation rather than features.
Practice prompt ↗06System design for agent infrastructure
- Design an agent orchestration platform that calls third-party tools with fault isolation and low-latency token streaming. Cover timeouts for each tool call, isolation between callers, and what the user sees when a tool fails mid-stream.
- Design a multi-tenant system with RBAC, SSO and strict data boundaries: where tenant identity is resolved, how every query and cache key is scoped to a tenant, and how you test for cross-tenant leaks.
- Design a caching layer for LLM retrieval: the cache key, what triggers invalidation, and how you avoid returning stale context.
- Work through the webhook fan-out worked exercise on this page and reuse its per-endpoint isolation, backoff and circuit-breaker reasoning for third-party tool calls.
Deliverable: Three design sketches, each naming the failure isolation boundary, the tenant boundary and one metric you would alert on.
Practice prompt ↗07Behavioral alignment and a full run-through
- Write stories for the three reported prompts: end-to-end ownership, a speed-versus-craftsmanship disagreement, and a production outage with a postmortem. End each with a customer or business outcome.
- Prepare your career walkthrough and your strengths-and-weaknesses answer, and practise giving a one-sentence version of each first.
- Run a mock with one API coding problem, one debugging exercise and one behavioral story. Check that the project figures you quote match across rounds.
Deliverable: Three written stories, a rehearsed career walkthrough, and a list of gaps from the mock to close before the interview.
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 centre on ownership, technical disagreements and production incidents. Prepare one story for each prompt below, covering the decision point, the options you had, what you chose, and a concrete customer or business result. If a project comes up in both a technical round and this one, keep its scale and timeline the same in both.
Resolve a review disagreement over a quota check
A colleague's pull request enforces a per-tenant quota by selecting the current count and then inserting when it is under the limit. You flag it as a race. They reply that the transaction already runs at repeatable read, so the snapshot makes it safe, and the tests pass. Walk through taking that disagreement to a resolution: what you write in the review, what you demonstrate rather than assert, which fix you propose and why, and what you do if they still disagree after all of it.
Approach
- Answer the claim precisely instead of restating your objection, because they have made a specific technical argument. In PostgreSQL, repeatable read is snapshot isolation; this is write skew, which snapshot isolation permits by design. Both transactions read a count that is stable within their own snapshot, insert disjoint rows that the other cannot see, and both commit, so the limit is exceeded by exactly the concurrency.
- Demonstrate rather than cite. Two psql sessions, both BEGIN ISOLATION LEVEL REPEATABLE READ, both select the count, both insert, both commit: it succeeds. Repeat at SERIALIZABLE and the second commit fails with serialization_failure, SQLSTATE 40001. That takes two minutes, ends the argument without anyone conceding a position, and leaves an artefact for the next reviewer.
- Offer the options with their costs rather than a verdict. Serialisable plus a retry loop on 40001 is correct but obliges every caller to retry and degrades under contention. An increment-and-compare on a counter row — update tenant_quota set used = used + 1 where tenant_id = $1 and used < limit returning used — is safe even at read committed, because a blocked updater re-evaluates the WHERE clause against the row version it finally locks, and zero rows returned means full. A unique or exclusion constraint that makes the surplus write fail is the third.
- Name the plausible non-fix explicitly, since it is what usually gets merged instead: folding the count into the insert as insert ... select ... where (select count(*) ...) < limit is still racy under read committed, because the subquery cannot see the other transaction's uncommitted rows. It looks atomic and is not.
- Say what you do if they still disagree: escalate the decision rather than the disagreement. Attach the reproduction, hand it to the service owner or a third reviewer, and state that you will not block the merge if the owner accepts the risk knowingly — and that you want that acceptance written down.
- Close with the general lesson worth leaving in the review thread: a passing suite is weak evidence for a concurrency claim because it runs one request at a time. Ask for a test that runs two.
Follow-up
- Write the counter-row version. Does your answer change if the quota counts child rows rather than a column?
- Under serialisable, who performs the retry, and what does the API client see if the retry also fails?
- This is the third disagreement with the same reviewer this month. What changes in how you review?
Unblock an engineer on a job run that finished twice
An engineer two weeks into the team brings you a job_run row showing status succeeded with an exit_code written by a worker declared dead ten minutes earlier; the retry attempt also shows succeeded. They have spent a day adding logging and are no closer. You have twenty minutes and you do not want to take the keyboard. Describe how you unblock someone: the question you ask first, what you let them find themselves, the concept you name and when, and how you check the next day that they own the fix rather than having watched you produce it.
Approach
- Ask what they expect rather than what they see: which statement set status to succeeded, and what did it check before writing? That question points directly at the update's WHERE clause, which is where the answer lives, and it costs them nothing to answer, so it does not read as a test.
- Let them build the timeline themselves from the row: queued_at, started_at, leased_until, finished_at and worker_id, on both the original run and the retry. Two different worker_ids with a lease expiry between them tells the whole story, and they will see it before you say it.
- Name the concept once the evidence has earned it. A lease bounds time; it does not prevent a write. The store has to reject a stale writer, which means the update carries a fencing token the row compares — update job_run set status = 'succeeded' where run_id = $1 and lease_token = $2 and status = 'running' — and a long garbage-collection pause or a brief partition is enough to produce what they are looking at.
- Point at the second, less obvious half and let them decide it: 'lost' exists in the status enum precisely so a run whose worker vanished is not recorded as failed, because failed asserts an outcome nobody observed and the system then bills and retries on that assertion. Ask them what these two rows should have said.
- Leave them with the next step rather than the patch — a test that kills the first worker after the sandbox exits and before the row is written — and say when you are available again, so the offer is real rather than polite.
- Check ownership the next day by what they produced, not by asking if it went well: a test that reproduces the window proves they understood it; a test that only asserts the new WHERE clause proves they copied it. Ask them to explain it to a third person and listen for whether the explanation is theirs.
Follow-up
- They propose a longer lease instead of a token. What do you say, and what breaks when legitimate runs last thirty minutes?
- How can you tell whether your explanation landed or they simply deferred to you?
- The same engineer hits a variant of this next month. What did you fail to teach the first time?
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?
- 01
Describe a project you owned end to end, from initial design to production, under a tight deadline. Which decisions were yours, and what changed for the customer?
- 02
Tell me about a serious technical disagreement with a peer over speed-to-market versus long-term craftsmanship. How was it resolved?
- 03
Tell me about a production issue or outage you handled: the immediate fix, the root cause, and the postmortem process you put in place.
- 04
Walk me through your background and how it maps to this role.
- 05
What are your main strengths and weaknesses, and what are you doing about the weakness?
- 06
Tell me about a time a stakeholder tried to end a conversation before you had made your case. What did you do?
Is this an official Sierra interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at Sierra. The rounds and questions reflect what candidates have reported, not a process Sierra has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗What does the Sierra technical screen focus on?
Candidates describe it as practical rather than puzzle-based, with a focus on real-world API processing and resilient code. Reported coding questions in the practical API engineering category include calling a mock API that fails intermittently, retrying, parsing nested JSON, building a linked list from the result, and resolving IDs through fallbacks with caching. Standard algorithm practice still helps: the question bank includes First Unique Character Index, Lowest Common Ancestor and Binary Search in Sorted Array, and grid DFS is a reported coding question. Spend most of your time on API clients that fail, and write your own tests.
PracHub interview research ↗What is the onsite loop like?
Reports describe a live debugging session on a multi-file repository, a presentation of an agent take-home project or a system design exercise, and system architecture discussion. They also describe a behavioral conversation about ownership and collaboration with an engineering leader. The format may vary by team, so ask your recruiter which of these your loop includes.
PracHub interview research ↗Do I need extensive machine learning experience?
Production LLM experience helps but is not described as a requirement. The reported questions centre on core engineering: API handling, error handling, debugging, distributed systems and clean code. Be ready to discuss tool calling and agent state at an engineering level, such as input and output schemas, retries, fallbacks and context size, rather than model theory.
PracHub interview research ↗How should I prepare for the multi-file debugging round?
Candidates report a Python agent framework of four to five files with a specification diagram, where logic errors stop the agent from selecting tools correctly. Practise on a small open-source Python project with bugs someone else has planted. Trace one request from the entry point to the tool call, compare each step with the intended behavior, state a hypothesis before each edit, and pin each fix with a test that fails before it and passes after.
PracHub Software Engineer practice ↗What should I cover when presenting the take-home?
The reported question asks you to explain your agent's architecture and tool-calling strategy, including trade-offs, reliability and evaluation. Cover how tool schemas are defined and validated, the retry and fallback policy for failing calls, how context is kept or trimmed over a long conversation, and how you checked response quality. Prepare a short decision log so you can answer 'why not the alternative?' for each choice.
PracHub Software Engineer practice ↗Which language should I prepare in?
The reported debugging codebase is in Python, so be fluent at reading Python, including exceptions and async code. For your own coding, use the language in which you can write tests and handle errors fastest, and check any language constraints with your recruiter.
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