Interview guides · Axonius
Axonius Software Engineer Interview Guide
Axonius Software Engineer interview guide: 7 reported rounds, coding, debugging, architecture and behavioral questions, with a 7-day prep plan.
The 60-second brief
Axonius builds a cybersecurity asset management platform. It collects an inventory of an organization's devices, users and cloud instances and checks their security posture, so security teams can find gaps. Candidates describe the Software Engineer role as centred on the integrations that connect the platform to a large number of security and management tools: gathering requirements, designing the data flow, writing the code and watching performance in production, alongside product managers and other engineering teams.
This guide is for engineers preparing for the Axonius Software Engineer loop. It covers the seven reported rounds, the reported coding, debugging, architecture and behavioral questions, and a seven-day plan. The stack candidates describe is Node.js or a similar server-side language, asynchronous programming, API integrations, data handling and distributed systems, so the practice tasks lean on those. Cybersecurity or asset-management familiarity is described as a plus, not a requirement.
Candidates report seven rounds over roughly four to six weeks: Initial Screening, Remote Coding Sessions, Architectural Discussions, Behavioral Interviews, Take-home Assignments, Live Debugging Sessions and Final Onsite Interviews.
Focus areas
- System Design
- JavaScript
- Node.js
Overview
The work candidates describe is integration engineering: pulling data out of many third-party systems, normalizing it and keeping the pipeline healthy in production. The reported technical questions follow that shape. They cover an in-memory cache, recursive aggregation under a test suite, pagination that returns the right number of elements per page, and reading batches from an API without exhausting memory. None is an abstract algorithm puzzle. Each one is a small version of something an integration has to do, so prepare working code in your main server-side language, ideally Node.js, with the edge cases already thought through.
Debugging is a theme of its own. Candidates report being handed code that does not work, fixing a failing integration, or adding a feature to an existing codebase. That rewards a repeatable method more than flashes of insight: reproduce the failure, shrink it, form a hypothesis, change one thing, rerun the tests. In asynchronous code the usual suspects are a missing await, a promise that is never returned, an error swallowed inside a callback, and shared state mutated between concurrent calls. Practise on code you did not write.
The architecture questions are about aggregation at scale: a service that collects data from social networks using services, message queues and an aggregation step, the choices behind integrating with third-party APIs, and a system you designed yourself. Prepare to talk through ingestion workers, queue semantics, retries and rate limits, idempotent writes, normalization and storage, and what happens to memory and latency as volume grows. Give the trade-off behind each choice.
Four behavioral prompts are reported: past projects, a decision-maker who rejects your opinion, frequent job changes, and incident response. Candidates also mention peers and management in the behavioral stage and a take-home in some loops. Pair the 7-day plan with two or three stories from your own work that show ownership, a technical hurdle and a measurable result.
The Axonius interview process
The order and names below are what candidates report for the Axonius Software Engineer loop. The take-home is described as something candidates may be given, so confirm with your recruiter which stages apply to you.
Initial Screening
Candidates report that the loop opens with an initial screening of qualifications. Little else is reported about its format, so go in ready to summarize your background against the stack the role lists: server-side work in Node.js or similar, API integrations, data handling and asynchronous programming. Ask the recruiter what the later stages involve.
What to demonstrate
- How your experience lines up with the reported requirements: server-side development, API integrations and data handling
- How clearly you can summarize past projects and your own part in them
How to prepare
- Write a short spoken summary of your most relevant project, naming the data source, the volume and your specific contribution
- Prepare an honest, brief account of any short tenures on your CV, since frequent job changes is a reported question
- List the questions you want answered about the remaining stages, including whether a take-home applies
Remote Coding Sessions
Candidates report remote coding sessions. This is the stage where the coding questions in this guide are practised: an in-memory cache, recursive aggregation that must pass all tests, pagination that returns a fixed number of elements per page, and batch reading from an API without memory problems. Expect working code in your own language, so rehearse in a plain editor with a test runner.
What to demonstrate
- Working code that passes tests, including edge cases
- Sound handling of asynchronous flow when data arrives in batches
- Whether you can explain why you chose a structure or approach while you type
How to prepare
- Implement a cache in Node.js with get, set, a TTL and a size limit with eviction, and write tests for expiry and capacity
- Write a recursive aggregation over a nested structure and test empty input, deep nesting and repeated keys
- Build a paginated reader that walks a mock API page by page and talk through your reasoning as you write
Architectural Discussions
Candidates report architectural discussions about software architecture relevant to the role. Reported topics include a system you designed before, a service that collects data from social networks using services, message queues and aggregation, and the decisions behind integrating third-party APIs. Treat it as a conversation about trade-offs rather than a diagram exercise.
What to demonstrate
- Architecture choices around message queues, data persistence and API integration, with the trade-offs named
- Handling of large data volumes and memory limits
- Resilience to failing dependencies and varying load
How to prepare
- Write a one-page account of an architecture you built: the components, the alternatives you rejected and what you would change now
- Sketch the social-network collection design: fetch workers, a queue, a normalization step and storage, with retry and rate-limit handling
- List your checklist for a new third-party API: auth, pagination style, rate limits, error classes, schema drift and how to test against it
Behavioral Interviews
Candidates report behavioral interviews with both peers and management. The reported prompts are past projects, a decision-maker who does not accept your opinion, frequent job changes and incident response. Answer with the STAR structure and spend most of the time on your own decisions and the result.
What to demonstrate
- Ownership and the reasoning behind decisions in your past projects
- How you disagree and still work with a decision-maker
- How you handle incidents and how you explain career moves
How to prepare
- Write one STAR story for each of the four reported prompts, each with a concrete outcome you can verify
- Rehearse the disagreement story to name what evidence you brought and how you committed afterwards
- Say each story aloud and trim it until the situation takes only a few sentences
Take-home Assignments
Candidates report that a take-home assignment reflecting real-world tasks may be given. The reported work is close to integration engineering, so a small API consumer with tests is the likeliest shape to rehearse. Clarify scope and submission expectations before you start.
What to demonstrate
- Maintainable code that passes unit tests
- Sensible handling of pagination, rate limits and data transformation
- Written reasoning about the choices and trade-offs you made
How to prepare
- Build a small service that pulls paginated records from a mock API, transforms them and exposes the result, with unit tests
- Add retry with backoff and clear handling for malformed records, and note both in a short README
- Write a short list of trade-offs and what you would do with more time, so the reviewer can follow your reasoning
Live Debugging Sessions
Candidates report live debugging sessions on practical problems. Scenarios mentioned in candidate accounts include fixing an integration that is currently failing and implementing a feature on top of an existing codebase. Narrate your hypotheses as you go and make the smallest change that fixes the cause.
What to demonstrate
- Root-cause analysis in code you did not write
- Understanding of asynchronous behavior: promises, callbacks and the event loop
- Whether your fix is verified with a test or a rerun
How to prepare
- Take a small Node.js module with seeded bugs (a missing await, an unhandled rejection, an async callback inside forEach, shared mutable state) and fix it while speaking aloud
- Practise a fixed sequence: reproduce, read the error, narrow the failing path, state a hypothesis, change one thing, rerun
Final Onsite Interviews
Candidates report that the process ends with final onsite or deep-dive interviews. The reports give no detail beyond that, so prepare to revisit everything: coding, debugging, architecture and the behavioral stories, with extra depth on the projects you named earlier.
What to demonstrate
- Consistency between what you said in earlier rounds and what you can explain in depth now
- Depth on your own architecture and the trade-offs behind it
- Communication across technical and behavioral topics
How to prepare
- Re-read your notes from earlier rounds and prepare a deeper answer for every project you mentioned
- Rehearse the social-network collection design and the third-party API decisions once more, without notes
- Run a mixed mock: one coding task, one debugging task, one design question and one behavioral story back to back
Where candidates lose points
Typing the cache or aggregation solution in silence, then presenting finished code
Say the interface first (get, set, TTL, eviction), then your data structure and why, then write. Narrate edge cases such as expiry, capacity of one and empty input, and run the tests yourself before you are asked.
Answering the pagination and batch-reading questions by loading all records, then slicing
Describe a bounded reader: fetch one page or batch, process it, release it, and fetch the next, using a cursor or offset, an async generator or stream, and backoff when the API rate-limits you. Name what happens with duplicates or missing items if data changes between pages.
In a live debugging session, changing code on a guess before reproducing the failure
Reproduce first, read the error and stack, narrow to the smallest failing path, and state a hypothesis aloud. In async code check missing awaits, swallowed rejections and shared mutable state before rewriting anything, and finish by rerunning the tests.
Architecture answers that list boxes (queue, workers, database) without trade-offs or failure handling
For the social-network collection design and the third-party API question, state what happens on retry, on a rate limit, on a malformed record and when a downstream consumer is slow. Give one alternative you rejected for each major component.
Behavioral answers that blame others or stay vague, especially on job changes and disagreement with a decision-maker
Pick stories where you acted, name the evidence you brought and the outcome you measured, and speak of former colleagues neutrally. Rehearse the job-change answer aloud until it is short, factual and forward-looking.
Questions & practice
How would you implement an in-memory cache?
Medium · Technical & Coding Proficiency
Approach
- Start from the interface: get(key), set(key, value, ttlMs), delete(key), plus a capacity bound. In JavaScript a Map keeps insertion order, so an LRU is a Map where get() deletes and re-sets the key to move it to the end, and eviction removes map.keys().next().value. Both operations are O(1) on average.
- Handle expiry with an expiresAt timestamp per entry: check it lazily on get() and return a miss if it has passed, plus an optional periodic sweep. Call unref() on the setInterval timer so the sweep does not keep the Node process alive.
- Prevent a stampede on a cold key by caching the in-flight promise rather than the resolved value, so concurrent misses share one fetch. Remove the entry if the promise rejects so a failure is not cached.
- Bound memory by entry count, or by an approximate byte size when values vary a lot. State the limit: this cache lives inside one process, so each worker in a cluster or each replica holds its own copy, and invalidation does not propagate between them.
Follow-up
- How would you invalidate an entry across several Node processes or replicas?
- When would you choose LFU over LRU, and what extra state does it need?
- How would you test TTL expiry without making the tests sleep?
Can you perform a live coding exercise where you aggregate data recursively to pass all tests?
Medium · Technical & Coding Proficiency
Approach
- Pin down the input shape first: nested objects, arrays, or a tree of nodes with children. Then write the base case (a leaf or primitive value) and the recursive case (combine the results of the children) before writing any other code.
- Have each call return its partial result and let the parent merge it, rather than mutating a shared accumulator; the function stays pure and is easier to test. Total work is O(n) in the number of nodes, and the extra stack depth is O(depth).
- Cover the JavaScript edge cases explicitly: typeof null === 'object', so check for null before recursing; use Array.isArray to tell arrays from objects; return the identity value (0, {} or []) for empty containers; decide how to treat missing or non-numeric fields.
- Run the provided tests early and read each failure message; fix one failing case at a time instead of rewriting the whole function.
- Name the limits: very deep nesting can throw RangeError: Maximum call stack size exceeded, which an explicit stack fixes, and cyclic references need a visited Set or WeakSet.
Follow-up
- Can you rewrite it iteratively with an explicit stack?
- What changes if each child has to be fetched with an async call, and how would you cap the number of concurrent requests?
- How would you detect and handle a cycle in the input?
Given this piece of code that is not working, how would you identify and fix the issue?
Medium · Technical & Coding Proficiency
Approach
- Reproduce the failure before touching anything: run the failing test or input, then read the full error and stack trace to find the first frame in the code you were given.
- State a hypothesis and narrow it down: log or set a breakpoint (node --inspect) at the boundary between correct and incorrect state, and halve the search space each time instead of changing code at random.
- Check the usual Node and JavaScript suspects: a missing await, so a Promise is used as if it were a value; an async callback passed to forEach, which does not wait for it; a rejected promise nobody handles; a shared object mutated in place; an off-by-one in a loop or slice; == where === was meant; a lost this binding.
- Fix the root cause with the smallest change, explain why it fixes the bug, and add a regression test that failed before the fix. Then look for the same pattern elsewhere in the file.
Follow-up
- How would you debug it if it only failed in production and you could not attach a debugger?
- The test fails only some of the time. How do you find out why?
- What would stop this class of bug from coming back, for example a lint rule such as @typescript-eslint/no-floating-promises?
Hold a per-tenant active cap against concurrent creates
Hard · write skew · isolation levels · constraints
A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.
Approach
- Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
- Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
- Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
- State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.
Follow-up
- A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
- The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?
Stop tag and share joins from fanning out a page
Medium · join fan-out · semi-joins · many-to-many
resource_tag is (resource_id, tag_id) with PK (resource_id, tag_id); resource_share is (resource_id, shared_with_user_id, permission). The tagged-and-shared listing inner-joins resource to both, filters tenant_id, tag_id = ANY($2) and shared_with_user_id = $3, orders by updated_at DESC and takes 50. Pages come back with fewer than 50 distinct resources and the total in the header is far too high. Explain the row multiplication, rewrite both the page query and the count query so each is correct, and name the index each one needs. PostgreSQL 16.
Approach
- Do the arithmetic against the predicates that are actually there. An inner join emits one row per matching child row, and both joins are filtered: tag_id = ANY($2) admits only the requested tags, shared_with_user_id = $3 admits one user's share rows. So a resource holding three of the requested tags and shared with $3 once yields three rows, not one — the multiplier is its count of matching tags times its share rows for that single user, and that second factor is 1 unless the table admits duplicate (resource_id, shared_with_user_id) pairs. LIMIT 50 then limits rows rather than resources, and COUNT(*) counts pairs — the header is the product, not the population.
- Reject DISTINCT as the fix. It deduplicates after the product has been built, so the planner must materialise and sort the fanned-out set before the LIMIT can apply, and it leaves any SUM or AVG in the same select list wrong.
- Rewrite both filters as semi-joins, keeping resource as the only row source: AND EXISTS (SELECT 1 FROM resource_tag rt WHERE rt.resource_id = r.resource_id AND rt.tag_id = ANY($2)) and the same shape against resource_share. A semi-join stops at the first match per resource and preserves the driving index order, so ORDER BY updated_at DESC, resource_id DESC LIMIT 50 still stops after 50 rows.
- Count with the same predicates and no join at all: SELECT count(*) FROM resource r WHERE r.tenant_id = $1 AND r.status = 'active' AND EXISTS (...) AND EXISTS (...). Nothing multiplies a resource, so the number is the population.
Follow-up
- The filter changes from 'any of these tags' to 'all of these tags'. Rewrite it and state what it costs relative to the ANY form.
- A resource can be shared with the same user twice under different permissions. Does your count change, and should it?
How would you ensure pagination works correctly to display a specific number of elements per page?
Medium · Technical & Coding Proficiency
Approach
- Fix the contract: a 1-based page number p and a page size n give OFFSET (p - 1) * n LIMIT n, or arr.slice((p - 1) * n, p * n) in memory. Validate n as a positive integer with a maximum cap, and reject p < 1.
- Order by a deterministic key with a unique tiebreaker, for example ORDER BY created_at DESC, id DESC. Without the tiebreaker, rows with equal sort values can repeat or go missing between pages.
- Test the boundaries: an empty result, a total that is an exact multiple of n, a partial last page, and a page past the end. The page count is Math.ceil(total / n), which is 0 when the total is 0.
- Name the cost of offset paging: the database still reads and discards the skipped rows, so deep pages slow down, and inserts or deletes between requests shift items across page boundaries.
- Offer keyset (cursor) pagination as the fix: WHERE (created_at, id) < ($1, $2) with the same ORDER BY, LIMIT n + 1, where the extra row tells you whether a next page exists. Return the last row's key as an opaque cursor.
Follow-up
- How does a user jump straight to page 50 when you use cursor pagination?
- How would you show a total count on a very large table without counting on every request?
- What should the client see if the item it used as a cursor has been deleted?
How would you read batches of data from an API to avoid memory issues?
Medium · Technical & Coding Proficiency
Approach
- Do not collect every page into one array and process it at the end. Process page by page so peak memory is about one page, regardless of how large the dataset is.
- In Node, write an async generator (async function*) that requests a page, yields its items and follows the next cursor or link until there is none; the consumer uses for await...of. Because the consumer pulls, the next page is only fetched once the current one is handled.
- Respect backpressure downstream: await each database batch write, and when writing to a stream, stop once write() returns false and resume on the 'drain' event.
- If a single response is itself huge, stream and parse it incrementally instead of calling res.json(), which buffers the whole body in memory.
- Make it robust: on 429 or 5xx responses, retry with exponential backoff and jitter and honour Retry-After; keep any parallel fetching to a small fixed limit; checkpoint the last processed cursor so a crash resumes there instead of starting over.
Follow-up
- How would you resume after the process crashes halfway through?
- The API only supports offset paging and the data changes while you read it. What goes wrong, and how do you cope?
- How would you show that memory stays flat? For example, by sampling process.memoryUsage().heapUsed during a run.
Describe a system architecture you designed in a previous role.
Medium · System Design & Architecture
Approach
- Pick a system you can defend in detail, and open with the problem, its users and the scale you actually measured (requests, data volume, number of integrations), not with the technology list.
- Walk the components and the data flow end to end, with the read path and the write path separated, and say clearly which parts you owned and which belonged to the team.
- Give two or three real decisions as trade-offs: what you chose, which alternative you rejected and why (for example a queue instead of synchronous calls, or batch instead of streaming).
- Cover how it failed and how you knew: the metrics and alerts, one real incident or bottleneck, and what you changed afterwards.
- Close with what you would design differently today and why; it shows you have kept evaluating the decisions since.
Follow-up
- What would break first if the load grew ten times?
- Why did you not choose the alternative you mentioned?
- What was the hardest production issue in that system, and how did you find its cause?
How would you design a system to collect data from social networks, including services, message queues, and da
Medium · System Design & Architecture
How would you design a system to collect data from social networks, including services, message queues, and data aggregation?
Approach
- Clarify scope first: which networks, which data (posts, profiles, metrics), how fresh it must be, and the volume. Then outline the pipeline: a scheduler that emits collection jobs per source and account, a message queue, per-network connector workers, a normalizer, storage, and an aggregation stage.
- Keep each connector responsible for one network's auth, rate limits and pagination, writing the raw responses to cheap storage so normalization can be re-run without calling the APIs again. Map everything into one common schema keyed by (network, external_id).
- Assume at-least-once delivery: make consumers idempotent with upserts on (network, external_id), send poison messages to a dead-letter queue after N retries, and partition by account if per-account ordering matters.
- Collect incrementally with since cursors or webhooks where a network offers them, and enforce each network's quota with a token bucket per credential so a burst does not get the keys throttled or banned.
- Scale workers on queue depth and lag. Aggregate either in a streaming consumer or in periodic batch jobs over the normalized store, and state which freshness each option gives.
Follow-up
- One network changes its response schema without notice. What breaks, and how do you contain it?
- A network is down for several hours. What happens to the queue and to freshness, and how do you catch up?
- How would you backfill a year of history without starving the live collection?
How do you approach architectural decisions when integrating with third-party APIs?
Medium · System Design & Architecture
Approach
- Isolate every vendor behind an adapter that exposes your own normalized model, so a vendor change touches one module and the rest of the system never sees its raw payloads.
- Treat every call as unreliable: set an explicit timeout, retry only transient failures (network errors, 429, 5xx) with exponential backoff and jitter while honouring Retry-After, and never retry a non-idempotent write blindly. Add a circuit breaker so a vendor outage does not tie up all your workers.
- Choose the sync model per vendor: webhooks when offered (verify signatures, deduplicate deliveries), polling with a stored cursor otherwise. Keep the credential lifecycle, including OAuth token refresh and rotation, in one place.
- Validate responses against a schema, and fail the single bad record rather than the whole batch. Log unknown or missing fields so drift is visible before customers notice it.
- Test against recorded fixtures and contract tests instead of the live API, and pin the vendor's API version so upgrades are deliberate.
Follow-up
- The vendor announces it is deprecating the API version you use. How do you migrate without downtime?
- The vendor's pagination returns duplicates or skips records. How do you detect and handle that?
- How do you test the integration in CI without calling the vendor?
Read latency spikes on a sixty-second sawtooth
Medium · cache stampede · single-flight · ttl
The cached listing read path serves about 14k reads/second at an 85% hit rate. p99 sits at 35 ms for 57 seconds, jumps to 900 ms for 3, and repeats. During each spike the primary shows several hundred identical listing queries starting within the same millisecond, all carrying one large tenant's id. Cache entries use a 60-second TTL. Give the mechanism, the ordered checks, the fix, and the correctness hazard your fix must not introduce.
Approach
- Match the period to a configured number before theorising about load. A spike every 60 seconds against a 60-second TTL is an entry expiring, and you confirm it by correlating spike timestamps with the entry's write time rather than with the traffic curve. If the period had matched a cron or a GC interval instead, this is a different investigation.
- Establish the concurrency of the miss. Several hundred identical queries in one millisecond means the miss path has no coalescing: every request that arrives between expiry and repopulation recomputes. The herd size is that key's arrival rate times its recompute time, so at 1.2k reads/second for the hot key and a 250 ms recompute you expect about 300 concurrent misses, which matches what is observed.
- Add single-flight on the miss path so one caller per key recomputes under a short-lived lock while the rest wait for its result. Prefer stale-while-revalidate where the read tolerates it: return the expired value immediately and refresh asynchronously, which removes the latency spike rather than serialising it into a queue of waiters.
- De-synchronise the keys. Write TTLs with jitter, for example 60 seconds plus or minus 10%, so a deploy or a mass invalidation does not align every key on the same second and turn a per-key herd into a fleet-wide one.
Follow-up
- The same sawtooth appears on a key that is invalidated on write rather than expired. Is that the same bug?
- How does your answer change if the recompute takes 4 seconds instead of 250 ms?
Suggested practice checklist
The seven days follow the reported loop: setup and a loop map first, then coding, API data handling, debugging, architecture, behavioral stories and a full mixed rehearsal. Each day produces something you can show or re-run.
Day 1 — Map the loop and set up a working environment
- Write the seven reported rounds in order and note which ones you can confirm with the recruiter, especially whether a take-home applies
- Create a Node.js project (or your main server-side language) with a test runner and a mock paginated API you can reuse all week
- List your three strongest projects with the data source, volume, your decision and the result for each
Deliverable: A one-page loop map with open questions for the recruiter, a runnable project with a passing sample test, and a three-project list.
Day 2 — Coding: in-memory cache and recursive aggregation
- Implement an in-memory cache with get, set, TTL and a size limit with eviction, and explain the complexity of each operation
- Write a recursive aggregation over a nested structure so a provided test suite passes, then add tests for empty, deeply nested and repeated-key input
- Say both solutions aloud as if in a remote coding session, including the trade-offs you chose
Deliverable: Two tested implementations (cache and recursive aggregator) and a recording or notes of your spoken walkthrough.
Day 3 — API data handling: pagination and batch reads
- Add offset and cursor pagination to the mock API and write a function that returns exactly the requested number of elements per page, including the last partial page
- Write a consumer using an async generator that reads batches, processes each one and keeps memory flat across a large mock dataset
- Add retry with backoff for rate-limit responses and a rule for duplicate or missing records between pages
Deliverable: A pagination helper and a batch consumer, with tests and a short note on how memory use stays bounded.
Day 4 — Debugging code you did not write
- Seed a small Node.js module with four async bugs (a missing await, an unhandled rejection, an async callback in forEach, shared mutable state) and have the code fixed under a timer
- Follow the sequence reproduce, narrow, hypothesize, change one thing, rerun, and speak it aloud as you go
Deliverable: A fixed module with passing tests, a list of the four bugs with the symptom each caused, and notes on the latency-spike exercise.
Day 5 — Architecture: collection pipelines and third-party APIs
- Design the social-network collection system on paper: fetch workers, queue, normalization and aggregation, storage, plus retry, rate-limit and backpressure handling
- Write your third-party API checklist covering auth, pagination style, rate limits, error classes, schema drift and testing against a sandbox
- Write a one-page description of an architecture you built yourself, with the trade-offs and what you would change now
Deliverable: A diagram with failure handling for the collection design, a third-party API checklist and a one-page personal architecture write-up.
Day 6 — Behavioral stories
- Write STAR outlines for the past-projects prompt and the decision-maker disagreement prompt, each with measurable results
- Write a short, factual answer on frequent job changes and a full incident-response story with timeline, mitigation, root cause and follow-up actions
- Rehearse all four aloud and trim each to a tight version, then record yourself and fix rambling parts
Deliverable: Four STAR outlines and recorded spoken versions of each.
Day 7 — Full mixed rehearsal
- Rebuild the cache from scratch, then the batch reader from scratch, without looking at day 2 and 3 code
- Debug a fresh seeded module with narration, then walk through the social-network collection design without notes
- Tell the incident-response story once more and note any weak spots to fix before the real loop
Deliverable: A list of remaining weak spots with one concrete fix for each, and repeat runs of the cache and batch reader written from memory.
Behavioral preparation for the Axonius loop
Candidates report behavioral interviews with both peers and management. The reported prompts cover your past projects, disagreement with a decision-maker, frequent job changes and incident response. For an integration and data role, pick stories that show ownership of technical decisions, honest trade-offs and a result you can verify.
- Describe a time you had to learn an unfamiliar system quickly and what you did in the first days.
- Tell me about a time an integration or data pipeline you owned failed in production and how you fixed it.
Tell us about past projects you have worked on.
Medium · Behavioral & Situational
Approach
- Choose a project close to this role: an integration, a data pipeline or a service that handled growing volume. Pick one where you made decisions yourself, not one you only watched.
- Open with the situation in two sentences, then spend most of the answer on the technical hurdle, the options you weighed and why you picked one.
- State your own contribution against the team's, and give an outcome you can back up: latency, error rate, memory use, or time saved.
- Prepare two or three projects of different shapes (greenfield, rescue of a failing system, a migration) so you can match the follow-up. The mistake that sinks this answer is a tour of technologies with no decision in it.
Follow-up
- What would you change about that architecture today?
- What was the hardest bug or bottleneck, and how did you find the root cause?
- How did the system behave as the data volume grew?
How would you react if the person making the decisions does not accept your opinion?
Medium · Behavioral & Situational
Approach
- Use a real disagreement where you held a view and the decision-maker chose differently. Begin by saying what you understood of their constraints, such as deadline, cost or risk, before saying what you thought.
- Show how you argued: evidence such as a benchmark, a prototype, incident history or a smaller reversible experiment, delivered privately and respectfully.
- Show what happened once they had decided: you committed, executed the chosen path fully, and recorded the risk you saw so it could be revisited with data.
- State the outcome honestly, including when the decision-maker was right. Two things sink it: a story where you were simply right and they were wrong, and one where you went around them or complied in silence.
Follow-up
- What if their decision later caused the problem you predicted?
- When would you escalate instead of accepting the decision?
- How is disagreeing in a group meeting different from doing it one-on-one?
Why do you move between companies so frequently?
Medium · Behavioral & Situational
Approach
- Answer directly and briefly, without apology. Walk the moves in order and give each a reason framed around what you wanted to learn or build, backed by what you shipped before leaving.
- Show the through-line: skills that compounded across moves, such as integrations, data pipelines or distributed services, which leads naturally to this role.
- If a move was forced by layoffs, an acquisition or a contract ending, say so plainly in a sentence. Never criticize a former employer or manager.
- End on what you are looking for next and what would make a longer stay realistic. Defensiveness, vague answers and a list of grievances are the mistakes that hurt here.
Follow-up
- What would make you stay for several years in this role?
- Which move was a mistake, and what did you learn from it?
- How do you get productive quickly in an unfamiliar codebase?
How do you handle an incident response scenario?
Medium · Behavioral & Situational
Approach
- Clarify whether the interviewer means a production outage or a security incident, then pick a real story that fits, such as an integration that stopped ingesting data after a third-party API changed.
- Follow the order of an incident: detect and triage, assess impact, mitigate first (rollback, feature flag, rate limit, restart), communicate to stakeholders, find the root cause once stable.
- State your own role and the decisions you made, for example rollback versus fix-forward, and how you confirmed recovery with metrics, logs or a replayed case.
- Close with the follow-through: blameless postmortem, the alert or test you added, the runbook change. Common mistakes are hunting for the cause before mitigating, heroics without communication, and no lasting fix.
Follow-up
- How did you decide between rolling back and fixing forward?
- What monitoring would have caught this earlier?
- How did you keep stakeholders informed while you were still investigating?