Candidate reports describe the Software Engineer role at Anduril as building software for real-time command-and-control, autonomous platforms and edge compute systems such as Lattice OS. Unlike a typical web-services or internal-tools job, the code often talks directly to sensors, autonomous air vehicles and distributed field nodes. The reported specialisms include vehicle management systems, real-time data ingestion, low-level embedded software and tactical user interfaces. Engineers work alongside electrical, mechanical and systems engineers.
The same reports list C++, Rust, Go, Python and TypeScript as working languages, depending on the team. Engineers own a feature from the first pull request through hardware integration testing and deployment. The work can include testing against hardware simulators, debugging live hardware in test bays, supporting field trials, and presenting software proposals in design reviews.
Candidates report that ordinary questions often come with physical constraints attached. A graph problem becomes a network of field sensors. A pipeline design has to cope with high-frequency drone telemetry. A command-and-control interface has to keep working when the link drops. The loop is also reported to vary by team, for example vehicle management systems versus frontend tactical dashboards. Find out on the recruiter call whether you are preparing for low-level C++ and firmware questions or for React and TypeScript rendering questions, and weight your week accordingly.
Recruiter Call
reportedCandidates describe the first stage as a conversational screen with a recruiter. It covers your background, your project experience and your alignment with the mission. You do not write code here, but this call shapes the rest of your preparation. The later rounds are reported to be tailored to the team, for example vehicle management systems versus frontend tactical dashboards, so use this call to find out which technical track you are preparing for. Ask which team you are interviewing for, which languages it works in, whether a technical presentation is part of your loop, and whether the role needs a security clearance.
What to demonstrate
- Whether you can summarise your background briefly and link specific past projects to software that works with hardware, sensors, real-time data or unreliable networks
- Whether your reason for wanting this mission-focused role is concrete and personal rather than a generic interest in the company
- Whether your project experience matches the team's track, either embedded and low-level systems or user-facing platforms
How to prepare
- Write a short spoken summary of your background that names two projects, what you personally built in each, and one constraint you worked under, such as latency, memory, hardware or schedule. Say it aloud until it comes out the same way every time
- Prepare a motivation answer that ties your past work to the kind of systems described for the role, instead of repeating the job posting
- Write down the questions you will ask: team, language, embedded or frontend track, whether the loop includes a project presentation, and clearance requirements
Technical Screen
reportedCandidates report that a team engineer or the hiring manager runs this screen. It involves live coding, bug fixing or a domain-specific review, depending on the team. The reported coding categories for the role are graph traversal (cycle detection and connected components with DFS or Union-Find), two-pointer and sliding-window problems, building a hash map or tree from scratch, and a spatial-coordinate store with fast lookups. The reported debugging category is a C++ program with data races and logic flaws. Any of these could come up here or later in the loop. Before you write code, ask about data bounds, memory limits and scale. Get a correct version working first, and state its complexity before you optimise.
What to demonstrate
- Whether your code is correct on edge cases (empty input, a single node, disconnected components) and whether you state time and space complexity without being asked
- Whether you ask about input bounds, memory constraints and expected scale before choosing an approach
- Whether you debug by naming shared state and tracing control flow, rather than trying edits until the output looks right
How to prepare
- Implement cycle detection for both undirected and directed graphs, plus Union-Find with a component count, from a blank file. Test each on an isolated node, a self-loop and a graph with two components
- Seed a small C++ producer-consumer program with an unsynchronised shared buffer and an off-by-one error. Find both, using ThreadSanitizer to confirm the race, and explain each fix out loud
- Write a hash map with chaining and resizing. Explain why lookups are O(1) on average, and describe the collision pattern that makes them O(n)
Final Round Loop
reportedCandidates report that the final stage consists of four to five sessions covering live coding, system design and behavioral assessment, with domain-specific deep dives that depend on the team. The reported design questions include a real-time ingestion pipeline for high-frequency drone telemetry, a command-and-control interface that keeps working through intermittent disconnection, an aerial vehicle tracking system that sends video and telemetry to ground stations, and an API for edge node state updates across thousands of sensors. Candidates report domain deep dives on C++ memory and concurrency for low-level roles, and on rendering and state management in React or TypeScript for frontend roles. Across several sessions, the same project may come up more than once, so your account of it needs to stay consistent.
What to demonstrate
- Whether a design names bandwidth, memory, CPU and connectivity limits early and handles lost links, reconnection and backpressure explicitly
- Whether coding answers stay correct and clearly explained across several sessions in a row
- Whether your behavioral stories and project figures stay the same when a project comes up again in a different session
- Whether your domain answers go below surface level: object lifetimes, copies versus references and synchronisation primitives, or re-render cost and state synchronisation in a UI
How to prepare
- Work the reported design categories with the network allowed to fail: bounded local buffers, sequence numbers or versioned state for syncing after reconnection, and an explicit rule for resolving conflicting state
- Write a one-page fact sheet for each project you might mention, covering scale, team size, timeline, what broke and what you owned. Quote from it and nothing else
- Run a mock with a coding session, a design session and a behavioral session back to back, then list the places where your explanation lost structure
Technical Presentation
reportedCandidates report this round for specialized roles. You present a past engineering project to a panel of engineers and then defend your architectural choices under questioning. The reported project-defense questions ask for the most complex system you have built and why you chose its frameworks, databases or languages. They also cover a project that failed or missed its performance targets and the root-cause analysis you did, and trade-offs made under hardware or schedule limits. Expect follow-up questions about the alternatives you rejected, the main bottleneck under stress, and exactly which parts you built yourself.
What to demonstrate
- Whether each technology choice comes with the alternative you rejected and the constraint that decided it
- Whether you clearly separate what you designed and wrote from what teammates built
- Whether a failure is explained through diagnosis, root cause and a permanent fix rather than through blame or luck
- Whether you respond to a challenge without getting defensive, and say where your knowledge ends instead of bluffing
How to prepare
- Structure the talk in four parts: context and objective, the architecture and your specific contributions, key trade-offs and failures overcome, and lessons learned
- For every box on your architecture diagram, write down what else you considered and why you rejected it, and note whether you built that component, reviewed it or inherited it
- Rehearse with someone who interrupts with questions like 'why not the obvious alternative', 'what was the bottleneck' and 'what would you change to double throughput', and answer each in a few sentences
8 candidate reports. Individual accounts describe a particular role and hiring cycle.
Anduril Software Engineer interview: C++ screen and graph clusters
I went through a familiar sequence: recruiter contact, a technical coding screen, and an on-site. In the Zoom technical round, we briefly covered my background before moving into C++. I had to read basic lines of code and explain pointers, references, lambdas, and move semantics. The second half was a programming challenge using depth-first search to identify node clusters in a graph. The on-site…
Read full experienceAnduril Software Engineer interview: drone equations and a demanding onsite
After an HR chat, I had a coding screen that was closer to a simulation than a standard textbook prompt. I had to calculate drone-related equations, so pattern matching was not enough to rush through it. Passing that screen led to an in-person onsite with system design, a cultural or motivational discussion, and two coding challenges. It felt difficult because the formats required both technical…
Read full experienceAnduril Software Engineer interview: cantilever beam questions and a nontechnical thermal screen
The recruiter screens were brief and mostly light. Technical questions did appear, but not consistently. In a screen about mechanical design, I got basic cantilever-beam questions. A separate thermal recruiter screen had no technical questions at all, which made the experience feel somewhat inconsistent from interviewer to interviewer. I did not get an offer from this stage. It felt comparatively…
Read full experienceAnduril Software Engineer interview: conventional flow and onsite concerns
I went through a conventional recruiter, tech-screen, and onsite flow. The questions covered data structures and algorithms, system design, and why I wanted to work at Anduril. The onsite was the main day, but the atmosphere did not match what I had hoped for. At one point, it felt like everyone was tired and the logistics were chaotic, based on how the interviewers talked about their situation.…
Read full experienceAnduril Software Engineer Interview Experience: Mock application work and a project presentation
My experience began with easy coordination. Reaching the recruiter and scheduling interviews was smooth. From there, the questions were tightly connected to the actual job and how effective I could be in it. I expected a typical interview structure, but it had a stronger performance element than I was used to. I had to demonstrate my abilities through mock application-style work, not just theory.…
Read full experiencePracHub editorial advice for the preparation topics above.
Designing a telemetry or command-and-control system as if the network never drops
The reported design questions include a drone telemetry pipeline, a command-and-control interface that must survive intermittent disconnection, and an API for state updates from thousands of edge nodes. A cloud-default answer, with an always-reachable broker and a central database as the only source of truth, fails all three. State the constraints at the start: link bandwidth and loss, memory and CPU on the field device, and what an operator must still be able to do while offline. Then design for them. Use a bounded local buffer with a stated drop policy. Use sequence numbers or versioned state so a reconnecting node sends a delta instead of its full history. Make updates idempotent so retries are safe, and give an explicit rule for which copy wins when two stale versions of state conflict.
Presenting a team project without separating your own contribution from everyone else's
The reported project-defense questions ask why you chose each technology, what the bottleneck was, and what you personally built. Saying 'we' for everything makes it impossible to credit you. Before the round, mark every component as designed and written by you, reviewed by you, or inherited. For each choice, name the rejected alternative and the constraint that decided it. Have one failure ready with its diagnosis, root cause and permanent fix. If a question goes beyond what you know, say where your knowledge ends and reason from first principles instead of guessing.
Fixing a C++ data race by guessing, or by wrapping everything in one mutex
The reported debugging question is a C++ program with data races and logic flaws. Start by naming the shared state, every thread that reads or writes it, and the invariant that is being broken. Separate the race from any single-threaded logic bug, because they need different fixes. Confirm the race with ThreadSanitizer or a reproduction rather than asserting it. Then apply the narrowest fix that closes the race: a mutex around the shared buffer, an atomic for a single counter, or handing ownership to one thread. Check lock ordering so the fix does not introduce a deadlock, and say how you would verify that the race is gone.
Reusing directed-graph cycle detection on an undirected sensor network
In an undirected graph, the edge back to the node you just came from is not a cycle. Track the parent during DFS, or use Union-Find: a cycle exists when an edge connects two nodes that are already in the same set. Directed graphs need a visiting/visited state (the recursion stack) instead. Start a traversal from every unvisited node so that disconnected components are covered. With Union-Find, the component count is the node count minus the number of successful unions. State O(V + E) for DFS, and near-linear time for Union-Find with path compression and union by rank. Before you finish, test on an empty graph, an isolated node and two separate components.
Quoting different figures for the same project in different final-loop sessions
With four to five sessions in the final loop, plus a presentation for some roles, the same project often comes up more than once. If the throughput, team size or timeline changes between tellings, the interviewers' notes will disagree. Keep a one-page fact sheet for each project with the figures you will quote and what broke. Rehearse from it until the numbers are identical every time, and give the same answer at the same depth when a question you have already answered comes up again.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Implement an in-memory data structure for storing and querying dynamic…
Implement an in-memory data structure for storing and querying dynamic spatial coordinates with fast lookups.
Approach
- Restate the input: its shape, its size, and what is guaranteed about it.
- Walk one small example through your approach before writing the whole thing.
- Choose the data structure from the access pattern, not from familiarity.
Follow-up
- What is the worst case, and how likely is it on real data?
- How does this change if the input no longer fits in memory?
Debug a broken or buggy C++ program containing data race conditions an…
Debug a broken or buggy C++ program containing data race conditions and logic flaws under time constraints.
Approach
- Name the brute-force solution and its complexity before improving on it.
- Walk one small example through your approach before writing the whole thing.
- Restate the input: its shape, its size, and what is guaranteed about it.
Follow-up
- What is the worst case, and how likely is it on real data?
- How does this change if the input no longer fits in memory?
Explain the performance differences between passing parameters by valu…
Explain the performance differences between passing parameters by value, reference, or pointer in low-latency C++ services.
Approach
- Name what is shared across threads and what owns each piece of state.
- Distinguish a value from a reference to it, and say which one you handed out.
- Reach for the cheapest primitive that closes the race, not the broadest lock.
Follow-up
- Where could this allocate more than you expect?
- What happens if two callers reach this at the same time?
Explain how hardware interrupts work and how you would design a thread…
Explain how hardware interrupts work and how you would design a thread-safe task scheduler for embedded firmware.
Approach
- Say what the runtime actually does before reasoning about the code.
- Name what is shared across threads and what owns each piece of state.
- Distinguish a value from a reference to it, and say which one you handed out.
Follow-up
- Where could this allocate more than you expect?
- How would you prove the race exists rather than suspect it?
Schedule ordered webhook retries with a heap of subscription queues
Design the in-memory scheduler for webhook delivery. Up to 20 million rows sit in status pending or failed_retryable across 200,000 subscriptions, each row carrying next_attempt_at and attempt_count, and each endpoint having a circuit breaker. Deliveries for one subscription must be attempted in order, so at most one attempt per subscription may be in flight. Support due(now), complete(delivery, outcome) and insert(delivery) in O(log S), where S is the subscription count rather than the delivery count. Give the backoff formula you schedule retries with.
Approach
- Key the global heap by subscription, not by delivery. Each subscription owns a FIFO of its due deliveries in event order; the heap holds one entry per eligible subscription, keyed by its head's
next_attempt_at. That is 200,000 heap entries instead of 20 million, and it makes the one-in-flight rule structural rather than a check somebody can forget. due(now): peek the minimum. If its key is in the future, sleep until then instead of spinning. Otherwise pop it, move the subscription into an in-flight set, and do not re-push it. A subscription absent from the heap cannot be dispatched twice, which is precisely how ordering is preserved.complete: on success, drop the head and re-push the subscription keyed by its new head, or leave it out when the queue empties. On a retryable failure, incrementattempt_countand setnext_attempt_at = now + uniform(0, min(cap, base * 2^attempt)), sampled uniformly across the whole interval. That is full jitter; deterministic backoff re-synchronises the herd you just created.- Circuit breaker: park the subscription in a second heap keyed by its half-open time, so an endpoint dead for six hours costs one heap entry and zero attempts rather than consuming worker slots. Admit exactly one probe at half-open and close the breaker only on its success.
- Say the price of the ordering guarantee out loud. One in-flight attempt per subscription means an endpoint answering in 10 seconds drains at 0.1 deliveries/second however many workers you run, and its backlog grows until it recovers. If the customer does not need order, allow k in flight and document delivery as unordered; that is the trade, and it is a product decision.
- All three operations are O(log S) with O(S) resident heap memory and the queues themselves backed by the store. The database-backed equivalent is a partial index on
(subscription_id, next_attempt_at) where status in ('pending','failed_retryable')claimed withFOR UPDATE SKIP LOCKED, and the write-back must be fenced onlease_tokenso a worker that stalled and resumed cannot overwrite a newer attempt.
Worked solution 30 min
- Define the four structures explicitly:
queues: subscription_id -> deque[delivery],ready: min-heap of (next_attempt_at, subscription_id),inflight: set[subscription_id],breaker: min-heap of (half_open_at, subscription_id). - Write down the invariant you will assert after every operation: a subscription appears in at most one of
ready,inflightandbreaker, never in two. - Implement
due,completeandinsert, then simulate 200,000 subscriptions with Zipf-distributed queue depths totalling 20 million deliveries. - Add one endpoint that always times out after 10 seconds and one that always answers in 20 ms, then measure the fast endpoint's throughput with and without the per-endpoint breaker.
- Instrument heap size across the run.
Follow-up
- One subscription has 4 million queued deliveries. What stops it from starving the other 199,999, and what does your heap look like under that load?
- A customer requests redelivery of last Tuesday's events. Where do those rows enter your structure, and what keeps them from reordering live traffic?
- The process restarts. How much state do you rebuild, and what stops every subscription from being attempted in the same second?
Decide which facts an invoice line copies instead of joining
invoice_line_item already denormalises tenant_id, which is reachable through invoice_id, and stores amount_minor even though quantity times unit_price_micros would recompute it. A reviewer asks you to normalise both away, and separately asks whether the tenant's legal name and billing address should be copied onto the invoice header. Decide each case. For every field you keep denormalised, name the read pattern or the invariant that justifies it, the anomaly the copy can develop, and the mechanism that prevents that anomaly here.
Approach
- Split the question into two kinds of copy, because they fail differently. A copy of a currently mutable fact is a cache: it drifts and needs invalidation. A copy of a fact frozen at write time is not a cache at all, it is the record of what happened, and normalising it away destroys information the source no longer holds.
- Keep tenant_id on the line. It costs 8 bytes, it leads every index on the table so no read is ever accidentally cross-tenant, and it turns a wrong join into an empty result rather than another tenant's money. Prevent the drift structurally: a unique constraint on invoice (invoice_id, tenant_id) plus a composite foreign key from the line on (invoice_id, tenant_id) makes a mismatched pair impossible, so the database enforces agreement instead of a code review.
- Keep amount_minor. Rounding must happen exactly once, at a named site, with a stated mode (half-even here). If readers recompute from quantity and unit_price_micros, every reader owns a rounding decision, and half-up and half-even diverge systematically across thousands of lines rather than cancelling out. A check constraint can bound the stored value but deliberately cannot re-derive it.
- Copy the legal name and billing address onto the invoice header, written once and never updated. The statement must show what was true when it was sealed, and the tenant record will change afterwards. This is a snapshot for the same reason
source_rollup_watermarkis stored per line: without it, nobody can reconstruct what the customer was told. - Name the read pattern that pays for all of it. Rendering, dispute response and export are per-tenant, per-period reads over thousands of lines that would otherwise join back to slowly changing dimensions that no longer hold the historical value. The write side is a once-per-period batch, so the extra columns cost nothing that matters.
- Concede the case where the reviewer is right: a mutable operational attribute such as the tenant's current plan name has no business on a line. If a report wants it, join. If a statement needs the plan as of the period, that is another snapshot and it belongs on the header with the rest.
Follow-up
- Write the composite foreign key and the unique constraint it requires on the parent. What does it cost on every line insert, and what does it do to a bulk load?
- A tenant is renamed after being invoiced. Which rows change, and what does the customer see on last quarter's PDF?
- Where does currency live, and what breaks if a tenant's billing currency changes between two periods?
Find the join that inflates every invoice total
invoice_line_item holds line_id, invoice_id, tenant_id, sku, rate_tier, quantity, unit_price_micros, amount_minor (bigint), currency, kind, voided_at. invoice_payment_attempt holds attempt_id, invoice_id, tenant_id, amount_minor, status (succeeded, failed, pending), created_at, and an invoice has many attempts. A finance report runs select i.invoice_id, sum(l.amount_minor), count(p.attempt_id) from invoice i join invoice_line_item l using (invoice_id) join invoice_payment_attempt p using (invoice_id) group by 1 and the totals are wrong. Say precisely what the sum now equals, and write a version that is also correct for invoices with zero attempts.
Approach
- Compute what the query actually returns before fixing it. The two joins form a Cartesian product per invoice, so each line row repeats once per attempt row:
sum(l.amount_minor)is the true total multiplied by the attempt count, andcount(p.attempt_id)is attempts times lines. Three lines and two attempts report double the money and six attempts. - Reject the reflex repair.
count(distinct p.attempt_id)does fix the count, because attempt_id is unique.sum(distinct l.amount_minor)does not fix the sum, because two legitimate lines with equal amounts collapse into one. DISTINCT inside an aggregate deduplicates values, not rows, and the difference stays invisible until two lines happen to match. - Aggregate each branch to invoice grain before joining: one CTE summing lines by invoice_id, one counting attempts by invoice_id, then join the two results. A LATERAL subquery per invoice is equivalent and sometimes plans better when the outer set is small. Either way every aggregate stays at the grain it was defined at.
- Keep invoices with no attempts by making the attempt branch a LEFT JOIN with
coalesce(attempt_count, 0). An inner join here silently drops every unpaid invoice, which is usually the exact population finance is asking about. - Push each filter to its own grain:
where l.voided_at is nullbelongs inside the line CTE, not the outer query, or it would also filter the attempt branch through the join. Put the tenant predicate on both branches, since the denormalised tenant_id is what stops a wrong join crossing tenants. - Leave yourself a standing check: an invoice total is a function of its non-voided lines and of nothing about payments, so if changing the payment filter moves the money figure, the fan-out is back.
Worked solution 25 min
- Create one invoice with three lines of 1000, 1000 and 500 minor units and two payment attempts, then run the original query.
- Confirm it reports 5000 and 6 rather than 2500 and 2.
- Apply
sum(distinct l.amount_minor)and confirm the total becomes 1500, which is worse rather than better. - Write the two-CTE version with a LEFT JOIN and coalesce, and confirm 2500 and 2.
- Add a second invoice with lines and no attempts and confirm it still appears.
Follow-up
- Add a third branch for credit notes applied to the invoice. Does the CTE shape still hold, and when would a single pass with
filter (where ...)be better? - Over 500k invoices this report takes minutes. Which grain would you materialise, and how do you keep it correct when a line is voided?
- The same report is needed per tenant per month. What index makes the line CTE cheap?
Design an end-to-end aerial vehicle tracking system that distributes r…
Design an end-to-end aerial vehicle tracking system that distributes real-time video streams and target telemetry to ground stations.
Approach
- Fix the scope first: who calls this, how often, and what they do when it fails.
- Choose a partition key and say what query it makes expensive.
- Name the read and write paths separately; they rarely have the same bottleneck.
Follow-up
- What would you drop to keep the system up under load?
- What breaks first when traffic grows ten times?
How would you structure an API to handle edge node state updates acros…
How would you structure an API to handle edge node state updates across thousands of deployed sensors?
Approach
- Fix the scope first: who calls this, how often, and what they do when it fails.
- Choose a partition key and say what query it makes expensive.
- State the consistency you need, and where you are willing to be stale.
Follow-up
- How does this behave when that dependency is down for an hour?
- What would you drop to keep the system up under load?
How would you architect a command-and-control interface that remains o…
How would you architect a command-and-control interface that remains operational during partial or intermittent network disconnections?
Approach
- Name the failure you are designing for, then the recovery path.
- State the consistency you need, and where you are willing to be stale.
- Choose a partition key and say what query it makes expensive.
Follow-up
- How does this behave when that dependency is down for an hour?
- What would you drop to keep the system up under load?
How do you balance rapid delivery ("Anduril speed") with long-term sof…
How do you balance rapid delivery ("Anduril speed") with long-term software maintainability and safety critical constraints?
Approach
- State your assumptions explicitly before working the problem.
- Say what you would check first and why it is the highest-information step.
- Clarify what is being asked and what a complete answer contains.
Follow-up
- How would you know your answer was wrong?
- What assumption would you test first?
Publish limit semantics that clients can back off against
The gateway enforces three separate limits: a per-tenant token bucket, a monthly plan quota, and a cap on concurrent in-flight requests. Tenants hold several credentials and call from three regions, and today they see undifferentiated 429s. Design the client-facing contract: which headers carry which limit, the status codes that distinguish slow down from out of plan allowance from too many in flight, and what a well-behaved SDK does for each. Also state what the gateway returns when the shared counter store is unreachable, and what bound that choice implies.
Approach
- Separate the three objects before naming a header. A bucket refills continuously, a quota does not refill until the period rolls, and a concurrency cap clears when an in-flight request finishes. They have different remedies, so they cannot share a status code.
- Map them accordingly: 429 with Retry-After for the bucket, where waiting works; 429 with a distinct code and a reset hint for concurrency, where waiting works only if something else completes; and 402 or 403 with a plan code for quota, where waiting never works and the SDK must surface it rather than sleep on it.
- Emit RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset for the bucket only, and document Remaining as advisory. It comes from a shared counter that other requests are changing concurrently, so a client treating it as a reservation has built a race.
- State the aggregation in the docs: the limit applies per tenant across every credential and every region. The cheap per-pod bucket of rate/N is correct only when traffic spreads evenly, and a tenant whose connections land on a few pods is throttled well under its published limit while a widely spread one exceeds it.
- Decide the degraded mode in advance and publish it. Failing open serves unmetered traffic during a counter outage; failing closed converts a counter outage into a total outage. Pick one, bound it, for example a local fallback bucket at a fraction of the limit for the duration, and say so in the contract.
- Specify SDK behaviour: honour Retry-After over local backoff, apply full jitter otherwise, never retry 402 or 403, and cap total attempts so retries expire before the caller's own deadline.
Worked solution 25 min
- Write a three-row table of limit, refill behaviour, status code, headers, and the client's correct action.
- Write the exact header set for one throttled response and one quota-exhausted response, showing that they differ.
- Write the documentation sentence that states aggregation across credentials and regions, and the sentence marking Remaining advisory.
- Choose and justify the counter-store failure behaviour, then state the numeric bound it puts on overage or on availability.
- Write the SDK's decision function: given status and headers, return sleep duration or surface to the caller.
Follow-up
- Two services under the same tenant each read Remaining = 50 and each send 50 requests. What did the contract promise, and what actually happens?
- How would you keep a burst from a staging workspace out of the production workspace's share of the same tenant's bucket?
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.
Approach
- 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-up
- 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?
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.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Recruiter call preparation and a cold baseline
- Write and say aloud a short background summary that links two past projects to real-time, hardware-adjacent or network-constrained work, with your own contribution named in each
- Write your recruiter questions: team, language, embedded or frontend track, whether a technical presentation is in your loop, and clearance requirements
- Attempt one reported coding question cold, detecting cycles and disconnected components in a network of field sensors, and write one sentence on what slowed you down
- Attempt one reported design question cold, a command-and-control interface that survives intermittent disconnection, and write one sentence on where the design ran out
Deliverable: A background summary, a list of recruiter questions, and two cold attempts with a note on what blocked each.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02Graphs: cycles, components and reachability
- From a blank file, implement undirected cycle detection with parent-tracking DFS, directed cycle detection with three states, and Union-Find with union by rank, path compression and a component count
- Test all three on an empty graph, an isolated node, a self-loop and two disconnected components, predicting each result before running
- Work the reachability pattern from the bank questions on unreachable heap objects: mark everything reachable from the roots, then report what was never marked
- State each solution's time and space complexity aloud before you run it
Deliverable: Three graph implementations with a shared edge-case test file and a written complexity line for each.
Practice prompt ↗Practice prompt ↗Practice prompt ↗03Data structures from scratch, intervals and heaps
- Build a hash map with chaining and resizing, then explain collisions, load factor, and why average O(1) can become O(n)
- Sketch a store for moving spatial coordinates with fast lookups. Compare grid bucketing with a quadtree for update-heavy data, and say which queries each makes expensive
- Solve merging intervals and the min-heap channel-assignment pattern from the bank, then choose a fixed-size sliding-window problem and state its extra-space cost
- Work this page's heap-scheduler exercise and check your answer against its invariant and expected heap size
Deliverable: A working hash map, a written spatial-index comparison, and three solved heap, interval or window problems with time and space complexities stated.
Practice prompt ↗Practice prompt ↗04C++ memory, concurrency and debugging (or the frontend track)
- Explain passing by value, by reference and by pointer in C++: the cost of copying, aliasing and null risks, when a const reference or a move is right, and how each affects a hot path
- Seed a small multithreaded C++ program with an unsynchronised shared telemetry buffer and a logic bug, then find both, confirming the race with ThreadSanitizer
- Explain how hardware interrupts reach a handler, and design a thread-safe task scheduler in which the interrupt handler does minimal work and passes the rest to a task through a queue
- Frontend track instead: build a search-and-filter component over thousands of map objects, and measure how debouncing, memoisation and list virtualisation or canvas rendering change re-render cost
Deliverable: A written pass-by-value/reference/pointer comparison, a fixed race with the sanitizer output, and a scheduler sketch (or a measured UI component for the frontend track).
Practice prompt ↗Practice prompt ↗Worked solution ↗05Real-time and disconnected system design
- Design a real-time sensor telemetry pipeline for several drones: ingestion, buffering, transformation and delivery to a UI, with rough message rates and the bottleneck named
- Design edge node state updates across thousands of sensors: versioned state, delta sync after reconnection, idempotent updates and a conflict rule
- Extend the command-and-control design from day 1 with what operators can do offline and how commands are ordered and acknowledged when the link returns
- Work this page's rate-limit contract exercise to practise stating failure behaviour and a numeric bound in a design
Deliverable: Two designs, each naming its hardware and network constraints up front, one reconnection protocol, and one trade-off you would reverse under a stated condition.
Practice prompt ↗Practice prompt ↗06Technical presentation and project defense
- Build the presentation in four parts: context and objective, the architecture and your contributions, trade-offs and failures, and lessons learned
- For every component, write the rejected alternative, the deciding constraint, and whether you built it, reviewed it or inherited it
- Rehearse with someone who interrupts with 'why not X', 'what was the bottleneck under stress' and 'what would you change to double throughput'
- Prepare the reported behavioral stories: a trade-off under hardware or schedule limits, a project that missed its targets and the root cause, and how you balance speed of delivery against maintainability
Deliverable: A rehearsed presentation, an alternatives table for its architecture, and three behavioral stories built from one project fact sheet.
Practice prompt ↗Practice prompt ↗07Final-loop rehearsal and fact sheet
- Run a back-to-back mock of a coding session (a graph problem), a design session (the telemetry pipeline) and a behavioral session, with no break between them
- Straight after the mock, write down the three moments you lost structure, and redo only those
- Finalise a one-page fact sheet per project with the figures you will quote, and check that they match what you said in the presentation rehearsal
- Do not open any new material. Re-read your own notes and list the questions you still need answered about logistics
Deliverable: Notes from the mock naming three failure points with a fix for each, and a final fact sheet for every project you will mention.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
The reported behavioral and project-defense questions for this role focus on decisions made under constraint: hardware limits, schedule pressure, systems that failed, and the tension between shipping fast and keeping software maintainable. Build each story from one project fact sheet so the numbers match wherever the project comes up. Name what you decided yourself, the alternative you rejected, and how you knew afterwards whether the decision held.
Describe a time you had to make a critical technical trade-off under s…
Describe a time you had to make a critical technical trade-off under strict hardware or schedule limitations.
Approach
- Name the disagreement and how you resolved it with evidence.
- Pick a story where you made the decision, not one where you watched it.
- Give the blast radius: what could have broken, and what you measured.
Follow-up
- How did you know your change caused the improvement?
- What did you decide not to do, and why?
Resolve a review disagreement over a quota check
A colleague's pull request enforces a per-tenant quota by selecting the current count and then inserting when it is under the limit. You flag it as a race. They reply that the transaction already runs at repeatable read, so the snapshot makes it safe, and the tests pass. Walk through taking that disagreement to a resolution: what you write in the review, what you demonstrate rather than assert, which fix you propose and why, and what you do if they still disagree after all of it.
Approach
- Answer the claim precisely instead of restating your objection, because they have made a specific technical argument. In PostgreSQL, repeatable read is snapshot isolation; this is write skew, which snapshot isolation permits by design. Both transactions read a count that is stable within their own snapshot, insert disjoint rows that the other cannot see, and both commit, so the limit is exceeded by exactly the concurrency.
- Demonstrate rather than cite. Two psql sessions, both BEGIN ISOLATION LEVEL REPEATABLE READ, both select the count, both insert, both commit: it succeeds. Repeat at SERIALIZABLE and the second commit fails with serialization_failure, SQLSTATE 40001. That takes two minutes, ends the argument without anyone conceding a position, and leaves an artefact for the next reviewer.
- Offer the options with their costs rather than a verdict. Serialisable plus a retry loop on 40001 is correct but obliges every caller to retry and degrades under contention. An increment-and-compare on a counter row — update tenant_quota set used = used + 1 where tenant_id = $1 and used < limit returning used — is safe even at read committed, because a blocked updater re-evaluates the WHERE clause against the row version it finally locks, and zero rows returned means full. A unique or exclusion constraint that makes the surplus write fail is the third.
- Name the plausible non-fix explicitly, since it is what usually gets merged instead: folding the count into the insert as insert ... select ... where (select count(*) ...) < limit is still racy under read committed, because the subquery cannot see the other transaction's uncommitted rows. It looks atomic and is not.
- Say what you do if they still disagree: escalate the decision rather than the disagreement. Attach the reproduction, hand it to the service owner or a third reviewer, and state that you will not block the merge if the owner accepts the risk knowingly — and that you want that acceptance written down.
- Close with the general lesson worth leaving in the review thread: a passing suite is weak evidence for a concurrency claim because it runs one request at a time. Ask for a test that runs two.
Follow-up
- Write the counter-row version. Does your answer change if the quota counts child rows rather than a column?
- Under serialisable, who performs the retry, and what does the API client see if the retry also fails?
- This is the third disagreement with the same reviewer this month. What changes in how you review?
Reverse a webhook ordering decision after measuring its cost
You argued for strict per-subscription ordering in webhook-delivery, which means one in-flight attempt per subscription. It shipped. Three months later a single unresponsive endpoint holds one subscription's queue at a six-hour backlog, and two customers report events arriving out of order anyway once their own retries are counted. Describe a decision you reversed: what you originally optimised for, the measurement that changed your mind, what the reversal cost in engineering time and customer change, and how you told the people who had already built on the original guarantee.
Approach
- State the original decision as a trade you made knowingly. Ordering across a network requires a single in-flight attempt per subscription, and its price is head-of-line blocking whenever one endpoint is slow. 'We priced it wrong' is a much stronger opening than 'we did not realise', and it is usually the true one.
- Bring the measurement that flipped it, not the anecdote: backlog age at the ninety-ninth percentile per subscription, the share of subscriptions where one slow endpoint gated an otherwise healthy queue, and the delivery throughput lost to serialisation. A reversal justified by complaints is indistinguishable from a reversal justified by fatigue.
- Name what you learned about the guarantee itself, which is the engineering content of this story. At-least-once delivery means a retried event already arrives after newer ones and the consumer already must be idempotent, so a guarantee the customer has to defend against anyway was never worth what it cost to provide.
- Describe the migration, because reversing a published contract is the hard half and the part candidates skip. Parallel attempts behind a per-subscription flag, a monotonically increasing sequence number added to the envelope so order-sensitive consumers can sort or discard, documentation that states at-least-once and unordered in those words, and a deprecation measured in quarters because the client is a pinned SDK inside a build pipeline you cannot see or redeploy.
- Give the cost in the two currencies that matter: engineer-weeks, and how many customers had to change code. Then say who you told before it shipped rather than in a changelog afterwards, and which large customer you left on the old behaviour and for how long.
- Close with the signal you now weight differently, stated as something you would do earlier next time: measuring the blocking cost on the slowest decile of endpoints before committing to the guarantee, rather than after a customer noticed.
Follow-up
- A customer insists they need ordering. What do you offer them that is not global serialisation?
- How did you choose the deprecation window given that you cannot see or redeploy the clients?
- What would have to be true for you to reverse back?
- 01
Describe a time you had to make a critical technical trade-off under strict hardware or schedule limitations.
- 02
Describe a situation where a technical project failed or missed performance metrics. What root-cause analysis did you perform, and how did you resolve it?
- 03
How do you balance rapid delivery ("Anduril speed") with long-term software maintainability and safety critical constraints?
- 04
Walk through the most complex software system you have architected, explaining why you chose specific frameworks, databases, or languages.
- 05
Connect your career, your motivation for this role, and the project experience that prepares you for it.
Is this an official Anduril interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at Anduril. Rounds and questions reflect what candidates have reported, not a process Anduril has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗How difficult are the coding questions?
Candidate reports put the algorithm questions at medium to hard, comparable to large technology companies. They also say there is more weight on practical execution, low-level fundamentals and defending past decisions. The reported topics include graph traversal and cycle detection, Union-Find, two-pointer and sliding-window problems, building a hash map or tree from scratch, and spatial lookups. Aim to produce correct code that handles edge cases and to state its complexity without being asked, rather than to memorise many problems.
PracHub interview research ↗Do I need prior defense or aerospace experience?
Reports say no for most software engineering roles. Candidates come from commercial tech, consumer hardware, finance and startups. What helps is showing how your experience carries over: talk about the latency, memory, reliability or connectivity constraints you have worked under, and explain the trade-offs you made because of them.
PracHub interview research ↗Is an active security clearance required before applying?
Not always, according to candidate reports. Many software teams work on unclassified systems where a clearance is not needed up front. Where a role does require one, the reported path is going through the clearance process after hire. An active clearance appears in the reports as a nice-to-have rather than a must-have. Ask on the recruiter call whether your target team requires one.
PracHub interview research ↗How long does the process take?
Candidates report four rounds over roughly three to five weeks: a recruiter call, a technical screen, a final loop and, for specialized roles, a technical presentation. Candidates also report that final decisions can depend on team matching after the technical rounds. Plan your preparation so the presentation and your project stories are ready before the final loop is scheduled.
PracHub interview research ↗Will I have to give a technical presentation?
Candidates report it for specialized roles. You present a past engineering project to a panel of engineers and then answer detailed questions about your choices. Structure it as context and objective, the architecture and your specific contributions, key trade-offs and failures, and lessons learned. Prepare the rejected alternative for every major choice, and be clear about what you built yourself versus what teammates built.
PracHub Software Engineer practice ↗Does the loop differ between embedded and frontend teams?
Candidates report that specific rounds are tailored to the team. Low-level and firmware teams go deeper into C++ memory management, pointers and references, mutexes, interrupts and debugging races. Frontend teams focus on React or TypeScript rendering performance, state synchronisation across display modules, and components that handle thousands of map objects. Graph coding and real-time design are reported question categories for the role; prepare them whichever track you are on.
PracHub Software Engineer practice ↗Sources & methodology 3 sources ↗
Official role evidence, timestamped platform data and clearly labeled preparation advice.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
platform · Accessed 2026-09-24 - 02PracHub Software Engineer practice ↗
Cross-company practice questions for this role.
platform · Accessed 2026-09-24 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
platform · Accessed 2026-09-24