cswork / libraryOpen practice workspace

Interview guides · AIRBUS U.S. Space & Defense

AIRBUS U.S. Space & Defense Software Engineer Interview Questions & Guide 2026

System design practice for AIRBUS U.S. Space & Defense software engineer candidates: reach a data model and an API contract, not a box diagram.

The 60-second brief

As a Software Engineer at AIRBUS U.S. Space & Defense, you play a central role in delivering high-reliability, mission-critical software solutions across air, space, and defense platforms. You will work at the intersection of cutting-edge software design and specialized aerospace systems, building applications ranging from ground control networks and satellite communications software to embedded flight systems, telemetry data platforms, and cloud-based analytics frameworks like Skywise.

The title spans product, platform and infrastructure work, and which of those the seat actually is decides whether design or algorithms carries more weight in your preparation. The posting rarely settles it; what the team is on call for usually does.

Focus areas

  • Model legal holds before modelling record destruction
  • Make every determination replayable from a frozen snapshot
  • Key idempotency on business facts, not requests

Overview

This position directly impacts defense readiness, space exploration, and commercial aviation safety. The software you design, write, and deploy must operate under extreme operational requirements where security, deterministic performance, and fault tolerance are mandatory. Whether you are implementing real-time algorithms in embedded C/C++, architecting microservices in Python or Java, or designing cloud deployment pipelines on AWS, your contributions directly protect crew, assets, and operational success.

Joining AIRBUS U.S. Space & Defense offers you the unique opportunity to solve complex, multi-disciplinary engineering challenges within a globally respected aerospace power. You will collaborate closely with cross-functional teams of systems engineers, avionics specialists, cloud architects, and program managers. Success in this role requires rigorous software engineering practices, deep curiosity, and an unwavering commitment to quality and safety.

The AIRBUS U.S. Space & Defense interview process

AIRBUS U.S. Space & Defense candidates report 3 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.

Online Application

Most of the time lost in this format is not lost to thinking. It goes to a standard-library call you half-remember, an off-by-one in a loop bound, and a debugging loop that mutates code at random until something passes. When output is wrong, stop re-reading the whole function: take the smallest input that reproduces it and walk the state through by hand, printing intermediates if the environment allows. Guessing at a fix without a failing case you understand is how a five-minute bug becomes twenty, and the clock does not pause while you do it.

What to demonstrate

  • Whether you reach the right structure without a detour, and can write it from memory rather than only recall that one exists
  • Whether overflow is considered where the language has fixed-width integers, since a signed 32-bit value stops at 2,147,483,647 and then wraps in Java, is undefined behaviour in C++, and does not arise in Python, whose integers grow instead
  • Whether recursion depth is treated as a constraint on large inputs, given that CPython's default limit is 1000 frames and a deep recursion can exhaust the stack in any language where an iterative version would not
  • Whether a failing case is isolated and explained before any edit is made to the code

How to prepare

  • From an empty file and with no references open, implement the pieces you lean on most: a heap push and pop, an iterative DFS with an explicit stack, and a binary search whose midpoint is written lo + (hi - lo) / 2, which avoids the overflow that (lo + hi) / 2 can hit in a fixed-width integer type
  • Time yourself on the ten library calls you look up most, such as sorting with a custom comparator, splitting and joining strings, and finding the next key at or above a value in an ordered map, until the lookup is gone
  • Take a solution you know is broken and, before touching it, write one sentence naming the input, the expected value and the actual value. Repeat until you do it without deciding to.

HR Screening Interview

Half of this call is the part candidates treat as small talk: start date, notice period, work authorisation and its timing, location and time zone, on-call, and the number. Those are what kill offers late, after several engineers have each spent a day. Surfacing a hard constraint now costs you nothing and occasionally buys you something, since a loop compressed to fit a competing deadline can usually only be arranged if it is asked for early. The common failure is deflecting the compensation question twice, then discovering at offer stage that the band never reached your number.

What to demonstrate

  • Whether your hard constraints are compatible with the role before a loop gets booked: earliest start, notice period, what authorisation you hold and when it needs action, days on site, willingness to carry a pager
  • Whether you give a compensation range with something behind it, such as current total compensation or a competing timeline, rather than leaving the band untested
  • Whether your stated timeline is real, since a competing deadline raised now is something scheduling can sometimes work around and the same deadline raised at offer stage usually is not

How to prepare

  • Write each constraint down in one line before the call and state them as facts rather than negotiating them live under a question you were not expecting
  • Set your range from two or three current data points for that level and location, and name the structure you are quoting in, so the number is comparable to the one they are holding
  • If another process is running, say where it stands and by when, and ask directly whether this loop can be scheduled inside that window

Technical Interviews

Input bounds are the part of the prompt most often skimmed, and they usually contain the answer. They tell you which complexity class is admissible, which narrows the search before you have thought about the problem itself. As a rough planning figure, a compiled language does on the order of 10^8 simple operations per second and an interpreted one roughly an order of magnitude less. So n up to about twenty admits enumerating subsets, a few thousand admits a quadratic pass, and a million admits neither: you need near-linear, or linear with a log factor. If the bounds are missing, ask for them.

What to demonstrate

  • Whether the approach is justified by the stated input size rather than by whichever pattern you recognised first
  • Whether you ask about the properties that change the algorithm: whether the input arrives sorted, whether duplicates occur, whether values are bounded integers, whether it all fits in memory
  • Whether you can name the bottleneck in your own solution and what would remove it, even when you deliberately leave it in place
  • Whether a claimed speedup is real, since memoising a recursion only helps when subproblems genuinely overlap and the state can be keyed cheaply

How to prepare

  • For each algorithm you rely on, write down the largest n it handles in roughly a second, then check two of those figures by timing them in the language you will actually type in
  • For two weeks, write one line naming your target complexity and the bound that justifies it before you write any code, then compare that line with what you ended up submitting
  • Practise the conversion backwards: given a required O(n log n), list the mechanisms that get you there (sorting, a heap, an ordered map, divide and conquer) and choose by what the problem needs to query, not by what you used last

Where candidates lose points

Computing a statutory due date by adding an interval to a UTC timestamp

The deadline is a civil date in a jurisdiction that observes daylight saving, and a civil day is not always 86,400 seconds; the same arithmetic also ignores weekends and a holiday list that is amended annually and differs by jurisdiction. The result is a due date off by a day near a transition, which reports a case as on time when it is late by law - the direction of the error that produces findings rather than complaints. Store the event as an instant, convert to the case's civil time zone, and walk the business-day calendar as versioned data with its own effective dates, then test the transitions and the holidays explicitly rather than the happy path in July.

Treating an integration partner as if it were a synchronous API when the system of record is a nightly batch

The effective consistency model is 'as of last night', so a screen that shows a constituent their updated status immediately after a submit is telling them something the authoritative system will not agree with for hours, and a caseworker who acts on it creates a conflict that surfaces as a reconciliation exception nobody owns. Read-after-write expectations also encourage retry-until-consistent loops that hammer a mainframe window sized for one pass. Make staleness explicit in the model and in the interface - show the as-of time, keep the local record in a pending state until the batch confirms, and design the reconciliation job as the place where disagreement is resolved rather than as an exception report.

Assuming the bug is in the framework

Suspect your own code first: read the stack trace top to bottom, check which versions are actually installed rather than which ones you believe are, and reproduce in isolation before blaming a library that thousands of people run daily. When the fault really is upstream, you need that minimal reproduction to say so credibly anyway.

Naming no test cases at all

State what you would test before being asked: empty input, a single element, all elements equal, the maximum permitted size, and the input that exercises the branch you just wrote. It costs thirty seconds and is much of what separates someone who has shipped code from someone who has only solved puzzles.

Questions & practice

Replay sixty million determinations after a retroactive rule correction

Hard · bulk-replay · inverted-index · determinism

A regulator corrects three clauses of a ruleset with effect six years back. Sixty million determination rows fall under the affected rule family, each carrying rule_version_id, evidence_snapshot_hash and a trace of the clauses that fired. Evaluation is a pure function costing about 8 ms of CPU, but each replay must rehydrate the frozen evidence snapshot from a content-addressed store capped at 500 fetches per second shared with live traffic. Replaying everything is correct. Explain why it is not viable, and give the algorithm that is.

Approach

  1. Cost the naive run before optimising it. CPU is not the constraint: 60 million evaluations at 8 ms is about 133 CPU-hours and parallelises freely. The store is: 60 million fetches against a 500 per second ceiling is 120,000 seconds, roughly 33 hours of wall clock while consuming the entire budget live traffic also needs. The run is correct because replay is deterministic and unaffected determinations converge on the existing row through the partial unique index, so it is purely a cost problem.
  2. Build an inverted index from clause to determination. Either a separate determination_clause table indexed on clause_id, or a GIN index over the trace array. The three corrected clauses then yield a candidate set by index scan instead of a full scan, typically well under one percent of the 60 million rows.
  3. Recognise that the fired-clause filter alone is unsound. A correction that widens a guard makes a clause newly reachable, so determinations that never reached it before can change now, and those are precisely the denials the correction was issued to reverse. If the trace only records clauses that fired, the filter silently omits the population the regulator cares about.
  4. Close the gap with what the trace can actually support, and be explicit that the target is a sound superset, not an exact set. If the trace records evaluated-and-false clauses as well as fired ones, include both: every determination that can change is then selected, along with some that cannot — a determination that already passed the widened guard is selected and replays identical, because widening a guard cannot exclude an input it already admitted. That asymmetry is the correct trade. A false positive costs one rehydration; a false negative is an uncorrected determination. If the trace records only fired clauses, fall back to a wider superset: every determination under the affected rule_versions whose effective period intersects the corrected period, which is far larger than the clause filter and still orders of magnitude under 60 million.
  5. Deduplicate the rehydration, not the evaluation. Distinct evidence_snapshot_hash values are the unit of store work, and a single snapshot attached to several cases in a household or an appeal chain is fetched once and evaluated per determination. Order the candidate scan by hash so the cache hits are adjacent rather than scattered across the run.
  6. Make the run resumable and rate-limited by construction: a durable cursor over the candidate set, a token bucket sized to the share of the 500 per second ceiling you are permitted, and writes that rely on the uniqueness key rather than a read-then-insert, so a restart after a crash converges instead of producing a second current determination.

Follow-up

  • The correction changes an amount rather than an outcome. Does your candidate filter change, and does the notice obligation?
  • A replayed determination comes back identical. Do you write anything at all, and what does an auditor need to see that you checked it?
  • Halfway through the run the regulator issues a correction to the correction. What happens to the work already done?

Detect bulk-write bursts in a backdatable case event stream

Medium · sliding-window · two-pointer · event-time

One day of case_event holds 1.4 million rows, of which about 210,000 carry actor_type='staff' with a non-NULL actor_id. Each row has occurred_at (the asserted time of the act, which staff routinely backdate by days or weeks) and recorded_at (when the row was written). Flag every actor_id that records more than 200 transitions inside any rolling 10-minute window, so an unannounced bulk script can be told from caseworkers clicking. Choose which clock the window slides on, justify it, and state the time and memory bounds.

Approach

  1. Slide on recorded_at, and separate the two independent reasons. Mechanically, the two-pointer invariant requires the stream key to be non-decreasing so the left pointer only ever advances; rows arrive in recorded_at order, so windowing on occurred_at while consuming them in arrival order breaks that invariant and you would first have to sort the day by occurred_at, an O(n log n) precondition the streaming form does not need. Semantically, even after that sort the occurred_at answer is the wrong one: a script that backdates its writes spreads its asserted times over weeks and fills no window, while the rows it wrote in one second are adjacent in recorded_at. The question is who wrote these rows in a short span, and that is a recorded_at question.
  2. One pass with a deque of (recorded_at, actor_id) and a hash map from actor_id to in-window count. Advance the right pointer, increment; advance the left pointer past anything older than 10 minutes, decrement, and delete a key at zero so the map stays proportional to active actors. Each event is pushed and popped once: O(n) amortised.
  3. Emit on threshold crossing, not on every event above it. A single actor holding 900 events in the window would otherwise produce hundreds of near-identical alerts; latch the actor on crossing and clear the latch only when its count falls back below the threshold.
  4. Memory is the widest window, not the day. Filtering to actor_type='staff' first keeps the deque at the staff rate rather than the 1,800 rows per second the overnight batch reaches; storing 16 bytes per entry rather than the row keeps the worst realistic window inside tens of megabytes. State the bound as a function of peak staff rate times 600 seconds so it survives a change in traffic.
  5. Refine the discriminator beyond count. Rows written by one statement share a recorded_at to the microsecond, while a caseworker's clicks land seconds apart, so the intra-window timestamp dispersion separates a bulk script from a fast human far more cleanly than volume alone, and it does not need a threshold tuned per unit.
  6. Keep occurred_at for the separate report it actually answers: the gap between occurred_at and recorded_at is entry lag, and it spikes predictably at reporting boundaries when staff enter a backlog.

Follow-up

  • recorded_at defaults to now(), which in this database is transaction start time, so commit order and recorded_at order can differ. What does a reader tailing by recorded_at miss, and how do you bound it?
  • A legitimate nightly reassignment job runs under a staff actor_id because it was given a service account with a person's role. How does your detector avoid alerting on it, without an allowlist that grows forever?
  • Run this over an unbounded stream rather than a day. What is the new memory bound and what is the first thing that breaks?

Match a rail return file onto payment instructions in one pass

Easy · hashing · reconciliation · idempotency

You hold 400,000 disbursement_instruction rows for a 90-day window (instruction_id, determination_id, benefit_period_start, amount_minor, rail_reference, status) and a return file of 12,000 rows carrying rail_reference, amount_minor, return_code and returned_at. The rail guarantees rail_reference is unique only within 90 days and recycles values afterwards, and it may resend a return it already sent. Produce three buckets: returns matched to exactly one instruction, returns matching none, and returns matching more than one. One pass over each input. State your time and space bounds.

Approach

  1. Build one hash map from rail_reference to a small list of instruction_ids in a single pass over the instruction rows. Lists, not scalars: recycling means a reference can legitimately collide across the window edge, and you need to observe that rather than let the last writer win. O(n) time, O(n) space for n=400k references.
  2. Stream the return file and probe. Zero hits goes to unmatchable, one hit to matched, more than one to ambiguous. Do not disambiguate an ambiguous hit by amount_minor: a returned payment and a recycled reference can carry the same amount, and guessing turns a reportable exception into a wrong ledger entry.
  3. Deduplicate resends on the business fact, not on the file row. A return is uniquely identified by (instruction_id, return_code, returned_at) once matched; a second identical row applies the same compensating state and must be a no-op, which is what makes replaying a redelivered file safe.
  4. Do not filter the instruction side by rail_reference IS NOT NULL. A NULL reference means the acknowledgement has not been processed, not that nothing was sent, so those rows belong in the map's absence set and drive the separate unacknowledged-but-submitted report.
  5. Memory: 400k entries of a ~20-byte reference key plus an 8-byte id is roughly 11 MB before hash overhead, comfortably in process. If the window ever widens past what fits, range-partition by the reference prefix and run k passes at k times the read cost rather than reaching for a database join per return row.

Worked solution (20 min)

  1. Build an eight-row instruction fixture: six with distinct references, and two sharing reference R because the rail recycled it 91 days apart.
  2. Build a five-row return file: three matching distinct references, one naming reference R, one naming a reference never assigned.
  3. Run the two passes and record which bucket each return lands in.
  4. Append a byte-identical copy of one already-matched return row and re-run, confirming the applied state is unchanged.

Expected result: Three returns matched, one ambiguous (reference R, two candidate instructions, no amount-based tiebreak attempted), one unmatchable. The duplicated row changes no instruction state and increments a duplicate-return counter.

Verify it yourself

  • The three bucket sizes sum to the return file row count, with no row counted twice.
  • Shuffling the return file changes nothing in the bucket membership.
  • Removing the instruction rows whose rail_reference is NULL does not change any bucket, which proves those rows were never being used as match candidates in the first place.

Follow-up

  • The rail sends a partial return for less than amount_minor. What does your matcher do, and what does the instruction row become?
  • A return arrives for an instruction that has already been reversed by a compensating row. How do you tell a duplicate return from a genuine second event?
  • You must run this continuously rather than per file. What bounds the map now, and what is the eviction rule?

Add a uniqueness rule to a live payments table with duplicates

Hard · online migration · concurrent index build · lock queue · compensating rows

disbursement_instruction(instruction_id BIGSERIAL PK, determination_id BIGINT, benefit_period_start DATE, amount_minor BIGINT, status TEXT, reversal_of_instruction_id BIGINT NULL) has 40M rows and takes writes continuously. It was built without UNIQUE (determination_id, benefit_period_start); a retried batch created 11,400 duplicate pairs, and 3,900 of those duplicates reached settled. Money that moved is never deleted and never voided — it is unwound with a compensating reversal row carrying reversal_of_instruction_id, which repeats the same business key. status values include voided. Produce the migration: the query that finds the duplicates, what you do with the ones that settled, the DDL that adds the constraint without blocking writers, and the deploy ordering. Name the lock each statement takes and what happens when it cannot be acquired.

Approach

  1. Find the duplicates with a window rather than a self-join: SELECT instruction_id, status, ROW_NUMBER() OVER (PARTITION BY determination_id, benefit_period_start ORDER BY instruction_id) AS n FROM disbursement_instruction and inspect every row with n > 1 together with its status, because the remediation differs entirely by status. The n = 1 row is the one that stays live; the rest are what you unwind.
  2. Do not make the data fit the constraint by deleting rows. A duplicate that reached settled moved real money and the rail's own ledger will not forget it; deleting the row makes your records disagree with the counterparty. The 7,500 unsent duplicates may be set to voided. The 3,900 settled ones get compensating reversal rows referencing them via reversal_of_instruction_id — and stay.
  3. That remediation is what rules out the obvious predicate. After it, each settled duplicate key still holds three non-voided rows: the original, the settled duplicate and its reversal. CREATE UNIQUE INDEX CONCURRENTLY ... WHERE status <> 'voided' therefore aborts with 23505 on the first of the 3,900. The fact the predicate needs — 'this row has been unwound' — cannot be a subquery either: a partial index predicate may reference only columns of the row being indexed and must be IMMUTABLE. So the fact has to be written onto the row.
  4. Add it as a nullable column with no default, which is a catalog change and not a table rewrite (PostgreSQL 11 extended that to non-volatile defaults as well): ALTER TABLE disbursement_instruction ADD COLUMN reversed_by_instruction_id BIGINT. It still takes ACCESS EXCLUSIVE for the catalog write, so it carries the same lock_timeout guard as any other ACCESS EXCLUSIVE step; add the self-referencing foreign key separately as NOT VALID and then VALIDATE CONSTRAINT, which takes only SHARE UPDATE EXCLUSIVE.
  5. The constraint is then a partial unique index over the rows that are live originals: CREATE UNIQUE INDEX CONCURRENTLY uq_instr_business_key ON disbursement_instruction (determination_id, benefit_period_start) WHERE status <> 'voided' AND reversal_of_instruction_id IS NULL AND reversed_by_instruction_id IS NULL. Each conjunct does one job — never sent, is itself an unwind, has been unwound. Accept the consequences: a partial index cannot back an ALTER TABLE ... ADD CONSTRAINT ... UNIQUE USING INDEX, so there is no named constraint and no foreign key can reference this key, and INSERT ... ON CONFLICT must repeat all three conjuncts in its inference clause.
  6. Prove the data fits before you build. Run SELECT determination_id, benefit_period_start FROM disbursement_instruction WHERE <the index predicate> GROUP BY 1,2 HAVING count(*) > 1 and require zero rows. A unique build that hits a violation is not free: CREATE INDEX CONCURRENTLY detects it in the second pass, aborts after scanning 40M rows twice, and leaves an index with indisvalid = false that must be dropped with DROP INDEX CONCURRENTLY before retrying.
  7. Name the locks. CREATE INDEX CONCURRENTLY takes SHARE UPDATE EXCLUSIVE, so readers and writers continue; it makes two passes plus waits for concurrent transactions and cannot run inside a transaction block. A plain CREATE INDEX or an ALTER TABLE variant takes ACCESS EXCLUSIVE, which queues behind any running query and blocks every reader that arrives behind it in the queue — a five-millisecond statement becomes an outage while one long report finishes. Guard every ACCESS EXCLUSIVE step with SET lock_timeout = '2s' and retry in a loop, so a failed acquisition ends as a failed migration step instead of a stalled table.
  8. Order the deploy so the window is never open: add the column; ship the application code that derives the idempotency key from (determination_id, benefit_period_start), treats 23505 as a converged retry, and stamps reversed_by_instruction_id whenever it writes a reversal; remediate the existing duplicates in chunks; re-run the residual-duplicate check; build the index concurrently; verify indisvalid in pg_index. Building the index before the code can handle a violation converts every legitimate retry into a user-visible error.

Follow-up

  • The concurrent build fails at 90% because of a deadlock with a nightly batch. What is on disk now, and what exactly do you run before retrying?
  • A duplicate settled and then the original was returned by the rail. What does the compensating row look like, and what is the account's net position?
  • Once the partial index exists, how does the batch writer's ON CONFLICT inference clause have to be written against a three-conjunct predicate, and what happens if the predicate is omitted?

Supersede a determination atomically under two concurrent writers

Easy · isolation · partial index · check-then-act · supersession

determination(determination_id BIGINT PK, case_id BIGINT, reopen_cycle SMALLINT, supersedes_determination_id BIGINT NULL, rule_version_id INT, outcome TEXT, award_amount_minor BIGINT, is_current BOOLEAN NOT NULL) records one row per decision version; corrections are new rows. A reviewer saves a corrected decision at the same instant a rule-correction batch re-evaluates the same case. Write the SQL that retires the existing current row and inserts the replacement so exactly one current row survives, and name the constraint that enforces it. State the isolation level you assume and what your code does when the second writer loses.

Approach

  1. Name the anomaly before writing SQL: both writers run SELECT determination_id FROM determination WHERE case_id=$1 AND reopen_cycle=$2 AND is_current, both see the same row, both flip it and both insert. Under READ COMMITTED the SELECT's result is not held, so nothing in the check-then-act sequence is atomic and both writers believe they won.
  2. Put the invariant in the database: CREATE UNIQUE INDEX determination_one_current ON determination (case_id, reopen_cycle) WHERE is_current. A partial index is the right tool because the uniqueness only applies to the sliver of rows where is_current is true; superseded rows share the same key freely.
  3. Write the retire and the insert as one transaction, taking the superseded id from the UPDATE rather than a prior SELECT: UPDATE determination SET is_current = false WHERE case_id=$1 AND reopen_cycle=$2 AND is_current RETURNING determination_id, then INSERT ... (supersedes_determination_id, is_current) VALUES ($returned, true). The RETURNING value is read under the same row lock that the UPDATE took, so the chain cannot point at a row someone else already retired.
  4. Trace what the loser sees under READ COMMITTED: its UPDATE blocks on the row lock, re-evaluates is_current against the newly committed version, matches zero rows, then its INSERT collides with the partial index and raises SQLSTATE 23505. Treat 23505 as a normal outcome — reload the now-current determination and decide whether to re-apply, not a 500.
  5. Contrast the isolation levels honestly. Postgres REPEATABLE READ is snapshot isolation: it stops the lost update on the UPDATE (SQLSTATE 40001) but would not by itself stop two inserts when no current row exists yet, because that is write skew. SERIALIZABLE detects it and aborts with 40001, costing you a retry loop. The unique index closes both cases at any isolation level, which is why it is the primary mechanism and isolation is the backstop.
  6. Update the denormalised case.current_determination_id pointer inside the same transaction, guarded by row_version, so the projection can never point at a determination that is no longer current.

Worked solution (20 min)

  1. Create the table with no partial index, then run the naive check-then-insert from two psql sessions with BEGIN held open on the first, and count rows where is_current.
  2. Add CREATE UNIQUE INDEX determination_one_current ON determination (case_id, reopen_cycle) WHERE is_current and repeat the experiment.
  3. Rewrite the retire step as a single UPDATE with RETURNING determination_id and feed that value into the INSERT's supersedes_determination_id.
  4. Force the collision deliberately: two sessions, both targeting a case with no current row, so the UPDATE matches zero rows in both and only the index can separate them.
  5. Repeat the whole sequence at REPEATABLE READ and record which statement raises 40001 versus 23505.

Expected result: With the partial index in place, exactly one of the two concurrent transactions commits; the other raises 23505 on the INSERT under READ COMMITTED, or 40001 on the UPDATE under REPEATABLE READ when a current row already existed. SELECT count(*) FROM determination WHERE case_id=$1 AND reopen_cycle=$2 AND is_current returns 1 in every run.

Verify it yourself

  • The winning row's supersedes_determination_id equals the id the UPDATE returned, and following the chain backwards reaches every prior version exactly once with no forks.
  • The losing transaction leaves no partially written state: no retired row stays retired without a replacement.
  • The same test run with the partial index dropped reproduces two current rows, proving the index and not the transaction is what enforces the invariant.
  • case.current_determination_id matches the row where is_current is true after every run, including the failed ones.

Follow-up

  • The batch re-evaluation produces a byte-identical outcome to the existing current determination. Do you still write a superseding row, and what does an appeal officer make of a chain of identical decisions?
  • How do you express this with INSERT ... ON CONFLICT instead, and what must the inference clause contain for a partial index?
  • A third writer reopens the case, incrementing reopen_cycle, while these two are mid-flight. Which of the three transactions should fail, and against what constraint?

Compare asynchronous vs. synchronous execution in web development and API management.

Medium · system design and architecture

Approach

  1. Name the read and write paths separately; they rarely have the same bottleneck.
  2. State the consistency you need, and where you are willing to be stale.
  3. Fix the scope first: who calls this, how often, and what they do when it fails.

Follow-up

  • What breaks first when traffic grows ten times?
  • What would you drop to keep the system up under load?

Write a function to solve a string manipulation or array traversal challenge (e.g., verifying balanced parentheses or finding the longest common substring).

Medium · system design and architecture

Approach

  1. Choose a partition key and say what query it makes expensive.
  2. Name the read and write paths separately; they rarely have the same bottleneck.
  3. State the consistency you need, and where you are willing to be stale.

Follow-up

  • What breaks first when traffic grows ten times?
  • How does this behave when that dependency is down for an hour?

Explain the key architectural components of Kubernetes and how you would approach troubleshooting a failing pod or service.

Medium · system design and architecture

Approach

  1. Fix the scope first: who calls this, how often, and what they do when it fails.
  2. State the consistency you need, and where you are willing to be stale.
  3. Choose a partition key and say what query it makes expensive.

Follow-up

  • What would you drop to keep the system up under load?
  • What breaks first when traffic grows ten times?

How do you ensure the performance, responsiveness, and scalability of a distributed backend application?

Medium · system design and architecture

Approach

  1. Choose a partition key and say what query it makes expensive.
  2. Name the failure you are designing for, then the recovery path.
  3. Fix the scope first: who calls this, how often, and what they do when it fails.

Follow-up

  • What breaks first when traffic grows ten times?
  • How does this behave when that dependency is down for an hour?

How are mutexes and semaphores utilized in embedded systems to avoid race conditions and deadlock?

Medium

Approach

  1. Say what you would check first and why it is the highest-information step.
  2. Clarify what is being asked and what a complete answer contains.
  3. Work from the requirement backwards to the design.

Follow-up

  • How would you know your answer was wrong?
  • What assumption would you test first?

Deliver and verify case-event webhooks to partner agencies

Medium · webhooks · signature verification · ordering

A partner agency subscribes to case events for one program. The producer reads case_event (case_id, reopen_cycle, seq, event_type, occurred_at, recorded_at, payload, payload_version). The consumer is the partner's HTTP endpoint, which may be down for hours and whose operators will later replay deliveries from an archive. Design the delivery contract: the signed request, the verification the receiver performs, the delivery guarantee, the ordering guarantee, and what a receiver does with a duplicate. State explicitly what the receiver must not infer from a delivery. Deliver the request shape, the signature scheme and the receiver's pseudocode.

Approach

  1. Sign bytes, not objects: HMAC-SHA256 over a canonical string of timestamp, a separator and the raw request body, compared in constant time, with the timestamp also in a header and rejected outside a tolerance window so a captured request is not replayable forever. A receiver that parses and re-serialises JSON before verifying will fail on key order, whitespace and number formatting, and will end up disabling verification.
  2. Decide where archive replay lives before choosing the tolerance. Re-posting two-day-old signed bytes forces the window open to uselessness, so operators replay through an authenticated backfill endpoint that re-signs at send time instead.
  3. Rotate keys without an outage: send one signature per active key with a key id and let the receiver accept any valid one. With a single key, rotation is downtime for every partner that cannot deploy in the same minute.
  4. State the guarantee as at-least-once. Publish from a transactional outbox written in the same transaction as the case_event row, so nothing is lost when the publisher dies mid-flight, and duplicates are expected rather than a defect.
  5. Give the receiver a dedup key it can hold with a unique constraint: (case_id, reopen_cycle, seq). A per-delivery id deduplicates retries of one delivery but not an archive replay, which is precisely the case the partner's operators will create.
  6. Order per case only, by seq, never globally. A receiver drops a delivery whose seq is below the highest applied for that (case_id, reopen_cycle), and must not order on occurred_at — staff backdate it deliberately, so it is an assertion about the world and not a clock. And say what a delivery is not: a 2xx from a partner is not dispatch of a statutory notice and releases no effective date; only the Notice Service's own dispatch record does that.

Worked solution (30 min)

  1. Define the headers — event id, event timestamp, and a signature of the form v1=<key_id>:<hex> — and write the exact canonical string that is signed.
  2. Write the receiver: reject when the timestamp is outside 300 s, verify with a constant-time compare over the raw body, then INSERT the dedup key and return 200 on conflict.
  3. Re-deliver the same event with a new delivery id and confirm the receiver deduplicates on (case_id, reopen_cycle, seq).
  4. Deliver seq 7 before seq 6 and confirm the receiver does not move the case backwards.
  5. Kill the publisher between the outbox write and the HTTP call, restart it, and confirm exactly one logical apply at the receiver.

Expected result: Each event is applied once at the receiver despite duplicate deliveries and an out-of-order pair; verification succeeds on raw bytes and fails after any re-serialisation; the archive replay path re-signs rather than widening the tolerance window.

Verify it yourself

  • A body with reordered JSON keys fails verification, proving the signature covers bytes.
  • Two key ids both verify during a rotation window.
  • The receiver's deduplication still holds when the delivery id is new but the case event is old.
  • No code in the notice effective-date path reads a partner's response status.

Follow-up

  • The partner is down for six hours. What is your retry schedule, when do you stop, and how do they catch up without you replaying from memory?
  • payload_version moves to 2. How do you ship that to a partner still parsing version 1?
  • The partner asks for evidence document bytes inline in the webhook. What do you send instead, and what changes about retention?

How would you approach designing a cloud migration plan for on-premise legacy systems to AWS using secure VPC endpoints and gateways?

Medium · debugging and production issues

Approach

  1. Say what evidence would prove you wrong, then go and look for it.
  2. Pick a bisection that eliminates candidates whichever way it turns out.
  3. Check the instrumentation before believing the symptom.

Follow-up

  • What would you add now so this is faster to diagnose next time?
  • What would you look at first, and what would it rule out?

Walk through your step-by-step diagnostic methodology when troubleshooting a production deployment failure or network DNS issue.

Medium · debugging and production issues

Approach

  1. Say what evidence would prove you wrong, then go and look for it.
  2. Pick a bisection that eliminates candidates whichever way it turns out.
  3. Separate the trigger from the cause; the deploy is rarely the bug.

Follow-up

  • What would you look at first, and what would it rule out?
  • What would you add now so this is faster to diagnose next time?

Suggested practice checklist

Four days sample coding, design, fundamentals and the practical rounds at deliberately shallow depth, which is enough to surface the topics you did not know were in scope. That map, rather than a guess made on day one, decides where the last three days go.

Day 1 — Coding, one pass at shallow depth

  • Solve one problem from each of six families, an array with two pointers, hash counting, binary search, a tree traversal, a graph traversal and one dynamic program, under a hard twenty-minute cap with no extensions, marking each finished, late, or stalled.
  • For every stall, write the exact move you could not make rather than the subject, so the note reads could not turn the recurrence into a loop rather than bad at dynamic programming.
  • Fix nothing today. The value of the pass is the unfixed record.

Deliverable: Six timed attempts marked finished, late or stalled, each stall carrying a named blocking move.

Day 2 — Design, one pass at shallow depth

  • Spend twenty minutes each on three different shapes, a read-heavy feed, a write-heavy ingest path, and something needing a transaction across two entities, stopping each at requirements, interface and data model.
  • After each, write the first question you could not answer, which is usually a number you could not estimate or a failure mode you had no vocabulary for.
  • Mark which of the three you would be most relieved not to be asked, and treat that as data rather than as a preference.

Deliverable: Three shallow designs, each with the first unanswerable question written at the bottom.

Day 3 — Fundamentals and the practical rounds

  • Answer eight short questions in writing at four minutes each, covering the material that fills the gaps between the big rounds: what happens between a URL and a rendered page, what an index costs on write, when a process is preferable to a thread, and what conditions a deadlock requires.
  • Do one thirty-minute practical task of the kind a take-home compresses: read an unfamiliar two-hundred-line file and write what it does, what you would change, and the one thing you remain unsure of.
  • Score every answer fluent, correct but slow, or absent, and keep the absent ones visible.

Deliverable: Eight scored short answers and one written reading of unfamiliar code.

Day 4 — The rounds that are about you, and the map

  • Deliver three behavioural answers aloud against a timer, a conflict, a failure you owned, and a decision made without enough information, marking any that ran past three minutes or contained no number.
  • Assemble the map: every marked item from days one to three on a single page, sorted by how likely it is to appear in your loop rather than by how uncomfortable it felt.
  • Choose exactly two areas for the remaining three days and write down what you are deliberately abandoning.

Deliverable: A one-page scored map of the whole surface area with two areas chosen and the rest explicitly abandoned.

Day 5 — First chosen area, to the depth you skipped

  • Work the higher-ranked area in four focused blocks, choosing items one level above where you stalled rather than repeating what already works.
  • After each block write the rule you extracted in one sentence with its precondition attached, since a rule carrying no precondition is exactly what fails under a variation.
  • Re-attempt the day-one or day-two item that exposed this area and compare against the original timing.

Deliverable: Four worked blocks, a timed re-attempt against the original, and three one-sentence rules with preconditions.

Day 6 — Second chosen area, where the gap is coverage rather than speed

  • Treat the second area differently from the first. Day five drilled something you could already half-do; this one is usually a topic you had simply never met, so build one worked reference example end to end and keep it, rather than attempting six problems badly.
  • Write down the vocabulary you were missing on day two or three, five terms at most, each with the one sentence that makes it usable in an answer rather than the textbook definition.
  • Redo the shallow attempt that exposed this area and note whether you now fail later in the problem, because moving the failure point is the realistic gain from a single day and is worth more than a score that did not change.

Deliverable: One worked reference example for the newly covered area, a five-term vocabulary list, and a note on where the failure point moved.

Day 7 — Reassemble the loop

  • Sit two rounds back to back with no gap, ordering them so the area you chose second comes last, because the map was built from rested, isolated attempts and the loop will reach your weaker area when you are already spent.
  • Write where the second round suffered from the first, which is normally the point at which structure collapses into narration.
  • Reduce the week to one page holding only the rules you can state without reading them.

Deliverable: Mock notes on cross-round carryover plus a one-page card of rules you can recite from memory.

Outage answers are graded on instruments

Nobody is scoring your stamina at three in the morning. What carries weight is which signal told you something was wrong, what you measured before touching anything, what you rolled back versus what you fixed forward, and why you picked one. 'We restarted it and it went away' is a story about not knowing.

Describe a time when you experienced a significant technical failure on a project. How did you resolve it and what did you learn?

Medium · behavioural and engineering judgement

Approach

  1. Give the blast radius: what could have broken, and what you measured.
  2. State the situation in two sentences and spend the rest on the reasoning.
  3. Pick a story where you made the decision, not one where you watched it.

Follow-up

  • What did you decide not to do, and why?
  • How did you know your change caused the improvement?

How do you handle working alongside team members with conflicting priorities or insufferable working styles?

Medium · behavioural and engineering judgement

Approach

  1. Pick a story where you made the decision, not one where you watched it.
  2. Name the disagreement and how you resolved it with evidence.
  3. Close with what you would do differently, concretely.

Follow-up

  • What would you do differently if you ran that again?
  • What did you decide not to do, and why?

Argue against hot-editable eligibility rules in a live console

Hard · bitemporal modelling · reproducibility · design review · rule versioning

A proposal gives policy staff a console that edits eligibility thresholds and clause text in place, effective immediately for all evaluations, on the grounds that waiting for a release delays a statutory change by weeks. In scope: rule_version(rule_version_id, governing period, recorded-at) and determination(rule_version_id, evidence_snapshot_hash, outcome, reason_codes, clause trace). Describe a design you argued against in this shape: what you conceded, the failure you demonstrated rather than described, what you proposed instead, and what was actually decided.

Approach

  1. Concede the real problem in the first sentence: the release train is genuinely the wrong clock for a statutory effective date, and any argument that terminates in 'wait for the deploy' deserves to lose. The disagreement is about where the versioning lives, not about whether policy staff should be able to act without you.
  2. Demonstrate the failure instead of describing it. Take a determination decided last quarter, apply the proposed in-place edit to the threshold its clauses read, replay it, and show the outcome or the amount change with no row anywhere recording that anything changed. That is an unappealable decision, and it is the whole argument in one artifact a non-engineer can evaluate.
  3. Propose the shape that keeps the concession intact: policy staff author a new rule_version with a governing period and a recorded-at time, the same engine evaluates it, and publishing is their action rather than a deploy. The bitemporal pair is what lets you answer both what governed a case and what you believed governed it when you decided, and the second question is the one the correction workflow depends on.
  4. Extend the same treatment to the reference data the rules read. Rate tables, thresholds and jurisdiction lists are rule inputs, so an UPDATE to one is a silent rewrite of every historical determination that read it; versioning the ruleset while editing its inputs in place leaves the hole exactly where it was.
  5. Name the residual risk you have created and price it in: publishing a wrong version is now one click by a non-engineer, so the proposal must include a dry run against a sample of live cases reporting outcome deltas by reason code before a version can be made effective. Then say what was decided, including what you instrumented if you lost.

Follow-up

  • Policy publishes a version whose governing period starts three months in the past. What happens to determinations already made inside that window, and who chooses?
  • What is the rollback for a published rule version that turns out to be wrong, given determinations were issued under it?
  • How do you keep the dry run honest when the sample is drawn from cases that are not representative of the enrolment peak?