cswork / libraryOpen practice workspace

Interview guides · Intuit

Intuit Software Engineer Interview Guide

Intuit Software Engineer interview guide: online assessment with Bash and SQL, Build Challenge, Technical Screen, Craft Demo panel and a 7-day prep plan.

Interviewing at Intuit

  • Practice bank: 18+ questions
  • Rounds: 2
  • Typical prep: 1–2 weeks
  • Interview reports: 5

Interview rounds

RoundQuestions
Online Assessment6
Technical Screen12

How hard is the Intuit Software Engineer interview?

DifficultyShareQuestions
Easy22%4
Medium67%12
Hard11%2

What Intuit actually tests for

TopicShareQuestions
Coding & Algorithms50%9
ML System Design22%4
Software Engineering Fundamentals17%3
Behavioral & Leadership6%1
Machine Learning6%1

The 60-second brief

Intuit's products named in the source notes for this role are TurboTax, QuickBooks, Credit Karma and Mailchimp. The notes describe Software Engineers building backend and full-stack services for financial workflows, working in stacks such as Java with Spring Boot, TypeScript and Python on AWS, and integrating generative AI features into applications. Several reported questions are framed in product terms, such as ordering TurboTax tasks with a topological sort or merging QuickBooks Online time windows. Expect this framing, but the underlying skills are standard: graphs, intervals, SQL aggregation, REST services and LLM integration.

Intuit candidates report 5 rounds · ≈ 4-6 weeks. The stages below are what candidates describe, not a published process.

What a Software Engineer does at Intuit

Candidates report five stages: Online Assessment, Recruiter Screening Call, Build Challenge, Technical Screen and a Craft Demo & Virtual Onsite Panel. The stages are closely linked. The Build Challenge is a hands-on assignment, either take-home or live machine coding. The Technical Screen reviews the decisions you made in it, and the Craft Demo has you present a project to an engineering panel. Code you write early in the process gets discussed again later, so build it as if you will have to defend every line.

The reported questions cover more than algorithms. Alongside medium-range data structures and algorithms problems such as topological sort, a running median over a sliding window, a thread-safe LRU cache and nested-string decoding, candidates report SQL with JOIN, GROUP BY, HAVING and window functions, a Bash log-parsing task, a production-ready REST API with tests, a parking lot design, and AI questions on prompt refactoring, RAG and agents, and guardrails for AI-generated code. Spread your preparation across all of these categories. Practising only LeetCode-style problems leaves most of the loop uncovered.

The Intuit interview process

Intuit candidates report 5 rounds · ≈ 4-6 weeks. The stages below are what candidates describe, not a published process.

Online Assessment

Candidates describe an automated assessment that tests problem-solving across several domains. The source notes point out that it includes a Bash scripting section alongside data structures and algorithms and SQL, and that many candidates don't prepare for command-line text processing. Practise all three. The bank's reported coding, SQL and Bash questions are the right material: a topological sort with cycle detection, decoding an encoded nested string, merging overlapping time windows, a longest range with at most k categories, SQL joins with GROUP BY and HAVING, per-group ranking with a window function, and a Bash script that totals the integers in multi-line log output. With an automated grader, the worked examples in the prompt are the specification. Read them for output format and edge behaviour before you write code.

What it evaluates

  • Whether your algorithm solutions stay correct on inputs the examples do not show: empty input, a single element, duplicates, and a cycle in a dependency graph
  • Whether your SQL gets grouping and filtering right: JOIN, GROUP BY, HAVING in place of WHERE on aggregates, and window functions for ranking within groups
  • Whether you can process text in Bash: read lines, extract fields or integers, and print a computed result without fighting the syntax

How to prepare

  • Practise three Bash tasks from memory until each runs on the first try: total the integers in a multi-line log, print the largest file name from ls -l output, and find the largest file in a directory. Cover while-read loops, awk field splitting, sort -n and $(( )) arithmetic.
  • Work through the SQL set: aggregate transactions by status and failure reason, a JOIN with GROUP BY and HAVING, and ranking employees within each company with a window function. Test each against data that has ties and NULLs.
  • Implement Kahn's topological sort that reports a cycle, a stack-based decoder for nested encoded strings, and interval merging. Test each on empty and single-element input before you consider it done.

Recruiter Screening Call

The source notes describe this call as a discussion of your technical background, communication skills and AI tool usage. The AI part is easy to under-prepare. "I use Copilot sometimes" tells the recruiter very little. Have a concrete account ready: which tools you use, what you use them for, and how you check their output before it ships. Also use the call to settle the logistics of later stages: whether the Build Challenge is a take-home or a live machine-coding session, which languages are accepted, and who runs each early stage.

What it evaluates

  • Whether you can summarise your background clearly: the stack you work in, a system you built, and what you personally owned in it
  • Whether your account of AI tool usage is specific: which tools, for which tasks, and how you verify generated code or tests
  • Whether you answer open questions plainly instead of reciting your resume

How to prepare

  • Prepare a short spoken summary that names your main language and framework, one system you built and one decision you made in it. Say it aloud until it stops sounding read.
  • Prepare one concrete AI-workflow example, such as generating unit tests with an assistant, and include the moment you caught a wrong or missing case in its output
  • Write down what you need to know about the Build Challenge format, language choice and the final panel, and ask on this call

Build Challenge

The source notes describe a hands-on assignment on real-world application development, concurrency or API integration, done either as a take-home or as a machine-coding session. The notes advise treating it as production code, with a sensible directory structure, clear naming, exception handling and unit test coverage. Keep in mind that the Technical Screen reviews the implementation decisions you make here, so expect to defend every shortcut you take.

What it evaluates

  • Whether endpoints behave correctly for both valid and invalid input, with validation, meaningful HTTP status codes and handled exceptions
  • Whether the code is split into clear layers such as controller, service and persistence rather than one large handler
  • Whether tests cover each user story and its failure paths, and whether any concurrent or asynchronous code is safe

How to prepare

  • Practise a reported practical-engineering scenario: from a skeleton repository with two user stories, build a complete REST endpoint with input validation, persist state to an in-memory database, and write unit tests. Use Java with Spring Boot or Python, and repeat until the scaffolding is quick.
  • Practise one API-integration task, such as fetching data from an API and rendering it as a web table, and handle loading, empty and error states
  • Keep a short decision log while you build: each choice, the alternative you rejected, and why. The next round reviews exactly these decisions.
  • Implement a thread-safe LRU cache and state which lock or structure protects each piece of shared state

Technical Screen

The source notes describe this round as a review of the implementation decisions you made during the Build Challenge. They also say to expect deep follow-ups on CS fundamentals in technical screens, such as transaction isolation levels, multithreading synchronisation and object-oriented design principles. Prepare to explain why your own code is shaped the way it is, not only whether it is correct. Before the call, re-read your submission as if a colleague wrote it, and prepare an answer for its weakest part before anyone points at it.

What it evaluates

  • Whether you can justify your design choices by naming the alternative you rejected and why, not just "it was faster to write"
  • Whether you can see the weaknesses in your own submission and explain how you would fix them with more time
  • Whether your fundamentals hold up under follow-ups: ACID and isolation levels, locking and thread synchronisation, and interfaces versus inheritance

How to prepare

  • List the three weakest parts of your Build Challenge submission and write down the fix for each
  • Prepare how the submission would change under one extension: concurrent writes, a real database instead of an in-memory one, or much larger data volume
  • Prepare short spoken answers on ACID and isolation levels, the four OOP pillars with interface abstraction versus class inheritance, thread synchronisation and deadlock, and token bucket versus fixed-window rate limiting

Craft Demo & Virtual Onsite Panel

The source notes describe this stage as a set of sessions covering presentations, live coding and behavioral interviews. In the Craft Demo you present a coding project to an engineering panel and answer deep-dive questions about your design, choice of data structures, async patterns, test coverage, trade-offs and scalability. The notes suggest structuring the presentation around the problem statement, system architecture, key trade-offs, security considerations and live test output. Because the panel mixes formats, change mode on purpose between sessions. The presentation rewards trade-offs, live coding rewards a finished, correct solution, and behavioral answers reward specific decisions and the people involved.

What it evaluates

  • Whether the presentation explains why the system is built the way it is, including trade-offs and security, rather than walking through features
  • Whether live coding ends with code that runs and has been checked against edge cases
  • Whether a design discussion starts from requirements and constraints, and whether each component is justified, including its scaling and cost trade-offs
  • Whether behavioral answers name your own decision, the disagreement or failure involved, and what changed as a result

How to prepare

  • Build the demo in the order the notes suggest: problem statement, architecture, trade-offs, security considerations, live test output. Rehearse it with someone who keeps interrupting with "why not X?"
  • For design practice, work the reported design questions: a parking lot system with classes, state and persistence; an LLM setup and architecture; and an AI agent that routes financial requests to microservices and validates the returned payload
  • Prepare AI-concept answers on RAG versus agents, classification models versus LLMs, and handling token limits, latency and output validation when calling generative AI APIs
  • Prepare behavioral stories about a technical disagreement, a production failure you handled, and an architecture you designed, each built around a decision you made

Questions Intuit candidates report

Data structures and algorithms

Every fast solution here throws work away, and the claim you have to defend is that the discarded work could not have held the answer. A two-pointer walk over sorted values advances the left index only because every remaining pair using that element is also too small. A monotonic deque drops an element because a larger one sits to its right and will stay in the window longer. Say that sentence out loud. A weak answer names the technique and shows it passing the given examples, which is indistinguishable from a wrong technique that happens to pass the given examples.

  • Design an AI agent capable of taking user financial requests, routing them to underlying microservices, and validating the returned payload.
  • Given a basic system prompt that returns inaccurate responses from an LLM, refactor the prompt to improve output accuracy and consistency.
  • Explain how you would prevent AI-generated code or automated response mechanisms from causing failures in a live production environment.
  • Given a string representation of encoded nested data, write a function to decode and expand the string correctly.

Behavioural and engineering judgement

A reversal is the easiest of these to tell badly, because the honest version sounds like a mistake and the flattering version sounds like nothing happened. Give the trigger: the specific observation that made continuing worse than stopping, dated to when it arrived rather than when it became undeniable. Then give what stopping cost, whether that was work discarded, a migration left half-finished, or a team told the dependency they had built against was moving. The answer that stands out names a stopping condition set before it was needed, or says plainly that none had been set.

  • Describe a situation where you had a major technical disagreement with a team member and how you reached a resolution.
  • How do you handle token limits, context window constraints, and latency overhead when integrating generative AI APIs into backend microservices?

Other topics

Most prompts in this group are underspecified, and the honest answer is conditional. Find the variable that flips the decision, such as read-heavy against write-heavy or data that fits in memory against data that does not, name it, then answer for the case you picked. A weak answer states a preference as though it held everywhere. A strong one gives the rule and the point where the rule changes. Say the assumption out loud and commit to it: an assumption can be corrected by the person asking, while an answer hedged in every direction at once gives them nothing to correct.

  • Write a Bash script to parse multi-line log output, extract integer values, and output the computed total.
  • Explain the four pillars of Object-Oriented Programming (OOP) and contrast interface abstraction with class inheritance.

Practice exercises with worked solutions

The prompts above tell you what gets asked.

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.

How a strong candidate works it

  • 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 ... type rewrites 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) and environment with its default on the parent. Both are catalogue-only but both take a brief ACCESS EXCLUSIVE that cascades to partitions, so run each with lock_timeout set 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), then validate constraint (SHARE UPDATE EXCLUSIVE, scans while reads and writes continue), then set 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.

Expected result (45 min): An ordered expand, dual-write, backfill, switch, contract plan with the lock named per step: metadata-only ADD COLUMN under a brief ACCESS EXCLUSIVE taken with lock_timeout; batched UPDATE backfill under row locks; CHECK NOT VALID, then VALIDATE under SHARE UPDATE EXCLUSIVE, then SET NOT NULL; CREATE INDEX CONCURRENTLY per partition with ATTACH PARTITION; and the dual-write-to-read switch identified as the step that cannot be reverted cleanly.

Check yourself

  • No step in the plan holds ACCESS EXCLUSIVE for longer than the configured lock_timeout.
  • The writer's error rate stays at zero through the rehearsal apart from the deliberate lock timeout.
  • After VALIDATE, SET NOT NULL completes in milliseconds on a table where the naive form scanned every row.
  • Freshly written rows agree on both columns for a full verification period before the old column is dropped.

Follow-ups

  • 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.

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.

How a strong candidate works it

  • 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-argument date_trunc('hour', occurred_at, 'UTC') on PostgreSQL 16 or later, or date_trunc('hour', occurred_at at time zone 'UTC') at time zone 'UTC' before that. Filter environment = 'production' explicitly, since metering covers three environments and billing covers one.
  • Aggregate to the grain with sum(quantity), count(*) and max(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-ups

  • 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?

Parse and verify a timestamped multi-signature webhook header

An inbound webhook carries a signature header of at most 1 KiB shaped t=<unix seconds>,v1=<64 hex chars>, with up to five v1 values during secret rotation and possibly unknown scheme keys. You hold the raw request body bytes and the currently active signing secrets. Write the parser and the verifier: accept when any active secret reproduces a signature and the timestamp is within a five-minute tolerance in either direction, reject otherwise. Single left-to-right pass over the header, no regular expression. State what is inside the MAC and why.

How a strong candidate works it

  • Parse in one scan: split on ,, then on the first = only, since a value may itself contain = under a future scheme. Accept t exactly once and treat a second t as a reject rather than last-wins. Push every v1 onto a short list and ignore any other key, so a v2 can be introduced later without breaking this verifier.
  • Say what is signed: HMAC-SHA256 over the exact byte string <t>.<raw body bytes>, yielding 32 bytes or 64 hex characters. The timestamp sits inside the MAC because otherwise an attacker replays yesterday's body with its still-valid signature and only has to edit the header timestamp.
  • Hash the bytes as received. Verifying against a re-serialised JSON body is the usual defect: key order, whitespace and number formatting all change the bytes while the parsed objects compare equal, so signatures fail for honest senders and the popular 'fix' is to stop checking.
  • Compare in constant time over fixed-length digests. Decode the hex to 32 bytes, accumulate acc |= a[i] ^ b[i] across the whole length, and test acc == 0 at the end. Evaluate every candidate without an early exit; at five candidates that is five HMACs over the body, linear in body size and negligible beside the network.
  • Apply the tolerance as a two-sided bound, rejecting when |now - t| > 300 seconds. A sender whose clock runs ahead of yours is an ordinary case, and an unbounded future timestamp is a free replay window.
  • Complexity: O(L) over the header producing k candidates, plus k HMACs at O(|body|) each. Space is O(k) beyond the body itself. Do the cheap rejections, including the tolerance check, before any cryptography runs.

Expected result (15 min): The valid case accepts. Both out-of-tolerance cases reject, including the future one. The unknown v2 field is ignored and the v1 still verifies. The re-serialised body fails, which is correct and is exactly why the raw bytes must be retained.

Check yourself

  • Timing the comparison over 10,000 mismatching signatures that differ at the first character versus the last shows no separation you can measure.
  • Removing t from the MAC input makes an old captured body with its original signature pass. Write that test, watch it pass, then put t back.
  • Every rejection except the signature mismatch itself returns before an HMAC is computed.

Follow-ups

  • The body is 40 MB. What changes about where you verify, and what can you do before the whole body has arrived?
  • A customer reports that signatures fail for exactly the requests whose body contains a non-ASCII character. What is your first hypothesis?
  • How do you rotate the signing secret with no failed deliveries, and how long do both secrets stay live?

An idempotent create endpoint that returns a one-time secret

POST /v1/api-keys inserts a tenant_api_key row (tenant_id, workspace_id, name, key_prefix, secret_hash, scopes, status, auth_version, created_at, expires_at) and returns the plaintext secret exactly once, since only its SHA-256 is stored. Write volume is tens per second and clients retry on timeout. Design the idempotency mechanism: what the key is scoped by, where the record lives, its retention, what happens when the same key arrives with a different body, what happens when a retry arrives while the first request is still in flight, and what a replay returns for the secret.

How a strong candidate works it

  • Scope the key by tenant, not globally: uniqueness is on (tenant_id, idempotency_key), or one tenant's key collides with another's and the second caller receives a stored response for a request it never sent. Store a fingerprint of the request alongside it - method, path and a hash of the canonicalised body - so a mismatch is detectable.
  • Let the unique constraint decide the race instead of application logic. In the same transaction as the credential insert, run INSERT INTO idempotency_record (tenant_id, key, request_fingerprint, status) VALUES (...) ON CONFLICT DO NOTHING RETURNING id; no returned row means this is a replay, and the existing record is then read. A select-then-insert here loses to itself under concurrency in exactly the way this endpoint is meant to prevent.
  • Write the three replay outcomes as a decision table rather than as prose: same fingerprint and completed returns the stored response; same fingerprint and still in flight returns 409 with Retry-After, without blocking and without executing; different fingerprint returns 422, because replaying a stored response for a mutated body would tell the caller a credential was created for parameters it never sent.
  • Handle the secret as the part that makes this endpoint different from an ordinary idempotent create. The plaintext cannot be regenerated from secret_hash, so either the stored response holds it - making the idempotency record a secret at rest whose retention is now the secret's exposure window - or a replay returns the key metadata without the secret and the documentation says a lost response is resolved by listing keys and revoking the orphan. The second is the safer default precisely because credentials are listable and revocable.
  • Set retention from the client's retry budget, not from a round number: the record must outlive the SDK's maximum total retry duration, so twenty-four hours is defensible if that budget is minutes. After expiry the key is reusable and a very late retry creates a second credential, which is acceptable here only because the object is listable and revocable, and would not be for an unlistable side effect. Expire with a scheduled delete on an index over created_at.

Expected result (20 min): Unique (tenant_id, idempotency_key); the record inserted in the same transaction as the credential with ON CONFLICT DO NOTHING deciding the winner; 200 with the stored response, 409 for in-flight, 422 for a fingerprint mismatch; a documented rule that the plaintext secret is not replayed and a lost response is resolved by listing and revoking; retention exceeding the SDK retry budget, for example twenty-four hours.

Check yourself

  • Two concurrent requests with the same key must leave exactly one row in tenant_api_key. If the logic is select-then-insert, they will not.
  • A retry with a mutated body must not return 200. If it does, the fingerprint is not being compared or is not covering the fields that matter.
  • The retention you chose must be strictly longer than the client library's maximum total retry duration. Look that number up rather than assuming it.

Follow-ups

  • Two requests with the same key arrive concurrently. Show the exact statements and say which one loses, and how it finds out.
  • The client receives a timeout, retries, and gets 409 in-flight. What should the client library do next, and for how long?
  • How does the design change if the created object is not listable - a one-off payout, say - so an orphan cannot be found afterwards?

Gateway p99 spikes on a five-minute cadence

edge-gateway caches each credential-to-authorisation-context decision for five minutes. p99 sits at 6 ms except for a spike to 900 ms roughly every five minutes, worst in the region with the most pods, and control-plane CPU and read latency rise in step with it. The error rate stays near zero. Customers are told a revoked credential stops authorising within 60 seconds. Give the ordered checklist that identifies the mechanism, and a fix that removes the spike without weakening the 60-second bound.

How a strong candidate works it

  • Test periodicity before anything else: take the spike timestamps modulo the TTL in seconds. A tight cluster at a fixed offset means expiry phase, while traffic-driven spikes scatter.
  • Overlay pod start times. Entries filled at first request inherit the phase of the pod that filled them, so a cohort of pods deployed together expires together and the amplitude should track cohort size rather than tenant count.
  • Separate a herd from a capacity shortfall by measuring control-plane requests per second during a spike against baseline. A stampede shows a step of roughly (pods x hot keys) for one interval with hit rate collapsing to near zero, not a gradual climb that would indicate the dependency is simply undersized.
  • Apply three independent controls: randomise each key's TTL by a factor drawn uniformly from something like 0.8 to 1.0 so cohorts de-phase; coalesce concurrent misses per key per pod so exactly one refresh is in flight; and serve the stale value while that refresh runs so a miss costs the stale read rather than the dependency's queue.
  • Bound staleness against the published contract rather than against comfort: serve-stale is admissible only up to the 60-second revocation bound, so the TTL floor and the stale window together must stay inside it, and the published invalidation must delete the entry rather than schedule a refresh.
  • Decide in advance what a miss does when the control plane is unreachable, because that is now the only uncached path: failing closed converts a dependency outage into a total outage, while extending stale service past the bound breaks the revocation promise. Pick one and configure it explicitly.

Follow-ups

  • Publish-subscribe invalidation is lossy under a partition. Given that, what actually enforces the 60-second bound, and what number would you put in the contract if asked to defend it?
  • One tenant's key is hot enough that a single pod's coalesced refresh still matters. What changes?
  • Would a shared cache tier in front of the control plane help or shift the problem, and what new failure does it add?

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.

How a strong candidate works it

  • 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-ups

  • 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?

Where candidates lose points

Preparing only algorithms for the online assessment and meeting the Bash section cold

The source notes say the assessment includes a Bash section alongside algorithms and SQL, and that candidates lose avoidable points there. Before the assessment, write the reported Bash tasks from a blank terminal: total the integers in multi-line log output, find the largest file name in ls -l output, and find the largest file in a directory. Make sure you can do line reading, field splitting with awk or cut, numeric sort and shell arithmetic without looking anything up.

Treating the Build Challenge as a race to working features and skipping tests, validation and error handling

The notes advise treating the Build Challenge like production code: a clear directory structure, good naming, exception handling and unit test coverage. Cover each user story with a passing test and a failure-path test before adding scope. Validate input at the boundary and return meaningful status codes. A smaller submission that is tested and cleanly layered is easier to defend than a larger one that isn't.

Being unable to explain your own Build Challenge decisions when the Technical Screen reviews them

This round reviews the implementation decisions you made, so keep a decision log while you build: each choice, the rejected alternative and the reason. Before the call, re-read the code, list its three weakest points and prepare the fix for each. "It was quicker" is a fair answer only if you can also say what you would change and when it would matter.

Describing AI tool use vaguely, or hiding it, and having no way to show generated output was verified

The source notes warn against hiding your reliance on AI tools or your gaps in understanding them. Prepare a concrete workflow example with the tool, the task and the check you ran on its output. For LLM integration questions, cover guardrails every time: schema-validated output such as JSON-only responses, a fallback when the model fails or times out, limits on token and context size, and tests that stop AI-generated code from reaching production unchecked.

Presenting the Craft Demo as a tour of features rather than an account of decisions

Structure the demo the way the notes suggest: problem statement, architecture, key trade-offs, security considerations, live test output. For each component, be ready to say why you chose it and what you gave up. Run the tests live rather than describing them, and prepare for deep-dive questions on async patterns, choice of data structures and how the design scales.

Evenings only, compressed around a full-time job

Roughly ninety minutes on weeknights with one longer weekend block. The plan cuts scope rather than compressing everything, on the assumption that one thing finished per night beats four half-started.

Day 1 — Online assessment: Bash and SQL

  • Write the reported Bash tasks from a blank terminal: sum the integers in multi-line log output, print the largest file name from ls -l output, and find the largest file in a directory
  • Solve the SQL set: aggregate transactions by status and failure reason, a multi-table JOIN with GROUP BY and HAVING, and a per-company salary ranking with a window function
  • Rerun each SQL query against data you built with ties, NULLs and an empty group, and fix any query whose output surprised you
  • Format a VARCHAR amount column as currency with M and B suffixes, and handle NULL and non-numeric values explicitly

Deliverable: Working Bash scripts and SQL queries for every task above, each with one note on the edge case that almost broke it.

Day 2 — Online assessment: algorithms and data structures

  • Implement Kahn's topological sort for a task-ordering problem that reports when circular dependencies make an order impossible
  • Implement a stack-based decoder for encoded nested strings, interval merging for overlapping time windows, and a sliding window for the longest range with at most k categories
  • Implement bracket validation and increment a digit-array integer by one, including the all-nines carry case
  • Work through the worked exercise on parsing and verifying a timestamped multi-signature webhook header, as practice at single-pass parsing with explicit rejection cases

Deliverable: Solutions with their stated complexity, each tested on empty, single-element and duplicate-heavy input before you call it done.

Day 3 — Build Challenge rehearsal

  • Build a REST service from an empty repository in Java with Spring Boot or in Python: two resources, input validation, an in-memory database, exception handling and unit tests
  • Add one asynchronous or concurrent path, such as a thread-safe LRU cache or a Spring Flux or promise-based call with error handling, and state what protects the shared state
  • Keep a decision log as you build: each choice, the rejected alternative, and why
  • Work through the worked exercise on an idempotent create endpoint that returns a one-time secret, then add idempotent retry handling to one of your endpoints

Deliverable: A small, tested REST service in a clean directory structure, plus a decision log you could hand to a reviewer.

Day 4 — Technical Screen: defend your code and the fundamentals

  • Have someone review yesterday's service by asking why each decision was made, and answer only from memory
  • List the three weakest parts of the submission and write down the fix for each
  • Prepare short spoken answers on ACID and isolation levels, the four OOP pillars with interface abstraction versus inheritance, paging and context switching, and token bucket versus fixed-window rate limiting
  • Work through the worked exercise on migrating a live partitioned event table without blocking ingest, to practise explaining database locks and how to change a live table safely

Deliverable: A written list of the submission's weak points with fixes, and spoken fundamentals answers you can give without notes.

Day 5 — System design and AI integration

  • Design a parking lot system: entities, class interfaces, state transitions and persistence, and name the trade-off behind each choice
  • Design an AI agent that takes user financial requests, routes them to microservices and validates the returned payload, including what happens when validation fails
  • Refactor a system prompt that returns inaccurate or non-JSON output so it reliably produces schema-valid JSON, and explain how you would test it
  • Prepare answers on RAG versus agents, classification models versus LLMs, handling token limits and latency, and scaling versus operational cost trade-offs

Deliverable: Two designs, each with requirements, components, data model and named trade-offs, plus a before-and-after prompt with its test cases.

Day 6 — Craft Demo and behavioral

  • Build a presentation of your day-3 project in this order: problem statement, architecture, key trade-offs, security considerations, live test output
  • Rehearse it aloud with someone who interrupts with deep-dive questions on async patterns, data structures and scaling
  • Prepare stories about a technical disagreement, a production failure you handled, an architecture you designed, and how AI tools fit into your workflow
  • For each story, write the decision you made and what changed afterwards

Deliverable: A demo you have rehearsed end to end with live tests, plus four behavioral stories built around your own decisions.

Day 7 — Mixed rehearsal and debugging

  • Run a mock that switches between a coding problem, a design discussion and a behavioral question, and note where you stayed in the wrong mode
  • Practise a debugging walkthrough for a Kubernetes app that is blocked or not serving: pod status, events, logs, readiness probes, resource limits and network policy, in that order
  • Re-solve one problem from day 2 from a blank file, then stop opening new material
  • Write down the logistics for each remaining stage: format, environment, language and what to have open

Deliverable: Mock notes naming where you lost the thread, a written debugging checklist, and a one-page logistics card.