cswork / libraryOpen practice workspace

Interview guides · Aquera

Aquera Software Engineer Interview Guide

Aquera Software Engineer interview guide: JavaScript fundamentals, graph and sliding-window coding, multi-tenant provisioning design, 7-day plan.

The 60-second brief

Aquera works on identity and integration: its products move data between separate enterprise systems so that organisations can run identity provisioning and access management. For a Software Engineer, the role description candidates see covers building new connectors, tuning existing pipelines, designing API components for data synchronisation, chasing integration problems for individual clients and paying down technical debt, with ownership of a module from design to deployment. The must-have requirements candidates describe are JavaScript and Node.js, data structures, algorithms and time complexity, distributed systems and API design. Experience with identity management, enterprise integration or cloud-native architectures is listed as a plus, not a requirement.

This guide is for engineers interviewing for a Software Engineer role at Aquera, particularly those whose daily language is JavaScript or Node.js. It covers the four reported stages and works through every reported question: the JavaScript language questions (cloning, array methods, let/const/var, currying), the two coding problems (graph traversal and longest substring without repeating characters), the design and debugging questions about multi-tenant identity provisioning, high availability and a slow tenant-specific sync, and the behavioral questions on technical debt, a hardest problem and an integration you built. It ends with a seven-day plan built on those questions.

Candidates report four rounds over roughly three to five weeks: Technical Screening, Live Coding Session, Deep-Dive Architectural Discussion and Leadership-Level Conversations, with the loop combining asynchronous assessments and live sessions.

Focus areas

  • System Design
  • Scalability
  • Multi-Tenancy

Overview

The work behind this role is plumbing between enterprise systems. HR systems and other sources produce changes to people, and those changes have to become accounts, attributes and access in downstream applications, for many customer tenants at once. The reported questions follow that shape. Design questions ask for a provisioning system that takes concurrent writes from thousands of HR sources and a pipeline sized for millions of transactions a day. A debugging question concerns a sync service that has slowed down for some tenants only. The topics candidates list for the loop are system design, scalability, multi-tenancy, concurrency and distributed systems.

The reported questions fall into four groups, and each needs a different kind of preparation. The JavaScript group (deep versus shallow cloning, map/filter/find, let/const/var, currying) rewards precise language knowledge plus a short code example and the gotcha that catches people. The coding group (a graph traversal and the sliding-window longest-substring problem) needs working code with stated complexity. The design and debugging group needs an architecture you can defend under concurrency and tenant-isolation pressure. The behavioral group returns to technical debt twice, and adds a hardest-problem retrospective and the reasoning behind an integration you built.

For preparation, that means practising in JavaScript, not only in pseudocode, and knowing Node.js well enough to explain the event loop and what blocks it, since candidates list Node.js internals among the areas assessed. In design answers, bring ordering, idempotency and noisy-neighbour isolation into the design itself; a generic web-scale design that ignores them misses what these questions are about. Candidates also report being asked to go deep on a specific past technical challenge. Choose one project in advance and rehearse it until you can explain every design decision and the alternative you rejected.

The Aquera interview process

Candidates report four stages that move from a technical screen to live coding, then an architecture deep-dive, and finally conversations with leadership. No reported question is tied to a specific stage, so prepare every question group before the first round.

Technical Screening

Candidates describe the Technical Screening as an initial check of technical skills and foundational competence. They also report that the loop mixes asynchronous assessments with live sessions, without saying which stage takes which form, so be ready to answer fundamentals both in writing and aloud. No question is reported against this stage specifically. The reported question set does contain a block of JavaScript fundamentals that you can lock in before the loop starts. For each one, have a one-sentence definition, a five-line code example and the edge case that separates a correct answer from a nearly-correct one, such as the temporal dead zone, what JSON-based cloning loses, or what a fresh binding per loop iteration does.

What to demonstrate

  • Foundational technical competence, the purpose candidates report for this stage
  • Correct, concise explanations of core concepts
  • Ability to back an explanation with a small working example

How to prepare

  • Write a one-paragraph answer plus a code snippet for each reported JavaScript question: deep vs shallow cloning, map/filter/find, let/const/var, and currying.
  • Predict the output of the classic var-in-a-for-loop setTimeout snippet, then explain why let changes it.
  • List what JSON.parse(JSON.stringify(x)) loses (Dates, undefined, functions, Map/Set, cycles), and what structuredClone does differently.
  • Explain the Node.js event loop in two minutes: call stack, microtasks (promises) versus macrotasks (timers, I/O), and what blocks it.

Live Coding Session

Candidates describe the Live Coding Session as an interactive session where you solve problems in real time. A live stage tests code that runs, so this is where to practise the two reported coding problems: a graph traversal with a time-complexity explanation, and the longest substring without repeating characters solved with a sliding window. Write them in JavaScript in a plain editor with no autocomplete. Talk through the invariant before you type, and run two or three edge cases by hand at the end. The JavaScript-specific mistakes are the ones to rehearse away: using Array.prototype.shift as a queue, deep recursion on large graphs, and indexing strings by UTF-16 code unit when the input may contain emoji.

What to demonstrate

  • Working code written live
  • Problem-solving reasoning explained while coding, as the stage's reported format implies
  • Correct time and space complexity for the solution given

How to prepare

  • Implement the sliding-window longest-substring solution with a Map of last-seen indexes, and test it on '', 'bbbb', 'abcabcbb' and 'abba'.
  • Implement BFS with a head-index queue and an iterative DFS on an adjacency-list Map, and state O(V + E) with the reason.
  • Extend the graph code to count connected components and detect a cycle in a directed graph, so you have a variant ready if asked.
  • Do two timed sessions where you narrate each step aloud from reading the prompt to final tests.

Deep-Dive Architectural Discussion

Candidates describe this stage as an in-depth conversation about architectural design and how you handle complexity. The topics candidates report for the loop as a whole are system design, scalability, multi-tenancy, concurrency and distributed systems, and the reported design questions are all set in identity provisioning: a multi-tenant provisioning system with concurrent writes from thousands of HR sources, a pipeline for millions of transactions a day, high-availability principles, and debugging a performance regression that affects only some tenants. Expect a design to be pushed deeper rather than widened. Prepare one architecture you know thoroughly: how changes to one identity stay ordered, how a retry avoids applying twice, how one large tenant is kept from starving the rest, and what happens when a downstream application is down.

What to demonstrate

  • Architectural design reasoning, as candidates report for this stage
  • Handling complexity as a design is examined in depth
  • Trade-offs stated and defended rather than listed

How to prepare

  • Draw the provisioning pipeline end to end (ingest, normalise, diff, provision, confirm) and mark the partition key, the idempotency key and the dead-letter path.
  • Do the capacity arithmetic for millions of events a day: average per second, a peak multiplier you state as an assumption, and what that means for worker count and target API rate limits.
  • Write a five-step debugging sequence for a slowdown that hits only some tenants, starting with what the affected tenants have in common.
  • Prepare a high-availability checklist covering redundancy, timeouts, retries with jitter, circuit breakers, bulkheads and safe deploys, with one example of each from your own work.

Leadership-Level Conversations

Candidates describe the final stage as conversations with leadership about cultural fit and alignment with values. The behavioral questions candidates report concern judgement over time: balancing technical debt against a tight deadline, deciding when to pay debt down, walking through the hardest technical problem you solved and what you would change, and explaining the design choices in an integration you built. Prepare stories in which you made the decision yourself, can name what it cost, and can describe the outcome with a number or a concrete event. Prepare questions of your own about the identity and integration platform as well, because candidates report being invited to ask about the stack and current challenges.

What to demonstrate

  • Cultural fit and alignment with values, as candidates describe this stage
  • Clarity in explaining past decisions and their trade-offs

How to prepare

  • Write two technical-debt stories, one where you shipped with debt and one where you stopped to pay it down, each with the evidence that drove the decision.
  • Rehearse your hardest-problem story with a specific, technical answer to what you would do differently today.
  • Prepare three questions about the platform, for example how connectors are built and tested, how tenant isolation is handled, and where the current scaling pain is.

Where candidates lose points

Answering the JavaScript questions with a definition and no example or gotcha

For cloning, scope and currying, a textbook definition is not enough. Back each answer with a five-line example and the edge case: what JSON cloning loses and what structuredClone throws on, the temporal dead zone and the var loop closure, the fn.length caveat for curry. Write these out on day one and say them aloud until each takes under two minutes.

A sliding-window or BFS solution that fails a known edge case or hides a cost

Test the longest-substring code on 'abba', where a stale last-seen index moves the left pointer backwards unless you guard it. In BFS, mark nodes visited on enqueue and avoid Array.shift, which makes each dequeue O(n). State O(n) and O(V + E) with the reason, not just the formula.

Designing provisioning with no story for two updates to the same identity racing

The reported design question includes concurrent writes from thousands of HR sources, so ordering and conflicts are central. Name the partition key that keeps one identity's changes in order, the idempotency key that makes retries safe, the optimistic-concurrency update or single-writer rule, and how conflicting sources are resolved per attribute.

Treating multi-tenancy as adding a tenant_id column

Explain what stops one tenant's 500,000-record import from delaying everyone else: per-tenant quotas, fair scheduling, separate rate limits per target, per-tenant lag metrics and alerts. For the slow-tenant debugging question, start by comparing affected and unaffected tenants instead of reading code.

Technical-debt and past-project answers that are slogans or cannot survive a second question

Two of the reported behavioral questions are about technical debt. Prepare one story where you shipped with debt and one where you paid it down, each with the evidence behind the decision and the outcome. For the hardest-problem and integration questions, rehearse the chain of follow-ups on your most important design decision.

Questions & practice

How do you utilize array methods like map, filter, and find in your daily work?

Medium · Technical Fundamentals and Programming

Approach

  1. State the semantics precisely: map returns a new array of the same length, filter returns the elements whose callback is truthy, and find returns the first matching element (or undefined) and stops early. None of them mutate the source array, but a callback can still mutate the elements.
  2. Give a realistic chain, for example records.filter(r => r.active).map(toTargetSchema) to turn a batch of source user records into the shape a target system expects, plus find to look up one record by email.
  3. Point out the complexity trap: calling find inside a map over another list is O(n*m). Build a Map keyed by id once (O(n)), then each lookup is O(1).
  4. Cover async correctly: arr.map(async fn) returns an array of promises, so wrap it in Promise.all, or limit concurrency when each call hits a rate-limited API. forEach does not await its callbacks.
  5. Mention smaller gotchas: filter(Boolean) also drops 0 and empty strings, chaining creates intermediate arrays (use one reduce or a loop for very large inputs), and find returns undefined both for 'not found' and for a found element whose value is undefined.

Follow-up

  • What does records.map(async r => save(r)) return, and how do you wait for it with at most 10 requests in flight?
  • How would you deduplicate an array of user objects by email?
  • A map with an inner find is slow on 100k records. How do you fix it?

Solve a graph traversal problem and explain the time complexity.

Medium · Technical Fundamentals and Programming

Approach

  1. Clarify the graph before coding: directed or undirected, weighted or not, the input format (edge list, adjacency list, grid), whether it can be disconnected, and whether the goal is reachability, shortest path, components or ordering.
  2. Build an adjacency list as a Map from node to neighbour array. Use BFS with a queue for shortest paths in an unweighted graph, and DFS for reachability, components, cycle detection and topological sort. Mark a node visited when you enqueue it, not when you dequeue it, so nodes are not queued twice.
  3. Give the complexity: O(V + E) time, because each vertex is dequeued once and each edge examined once (twice for undirected graphs), and O(V) extra space for the visited set and queue. With an adjacency matrix it becomes O(V^2).
  4. Handle JavaScript specifics: Array.prototype.shift is O(n), so keep a head index into the array for an O(1) dequeue. Use an explicit stack for DFS on large graphs to avoid exceeding the call stack.
  5. Cover the edge cases: disconnected components (loop over every node as a start), self-loops, cycles, a start node that is not in the graph, and duplicate edges. A domain-flavoured example is resolving nested group membership while detecting a group cycle.

Follow-up

  • The edges now have weights. What changes, and what is the complexity of Dijkstra with a binary heap?
  • How do you detect a cycle in a directed graph, and why does undirected cycle detection differ?
  • The graph does not fit in memory. How would you traverse it?

Write a function to find the longest substring without repeating characters using sliding window techniques.

Medium · Technical Fundamentals and Programming

Approach

  1. Start from brute force: check every substring for uniqueness, which is O(n^3), or O(n^2) with a Set while extending. Then explain why a window works: once s[left..right] has no repeats, the next valid window never needs left to move backwards.
  2. Keep a Map from character to its last index. For each right: if the character was seen at an index >= left, set left = lastIndex + 1. Then record the index and update best = Math.max(best, right - left + 1).
  3. The >= left check (or left = Math.max(left, last + 1)) is what makes 'abba' correct. Without it, the stale index of the first 'a' moves left backwards and the answer is wrong.
  4. Complexity: O(n) time, since each index is visited once by right and left only moves forward, and O(min(n, alphabet)) space.
  5. Edge cases: '' returns 0, 'bbbb' returns 1, an all-unique string returns its length. JavaScript strings index by UTF-16 code unit, so iterate with for...of or Array.from when emoji or other astral characters must count as one character. Track a start index if the substring itself is required.

Follow-up

  • Return the substring itself, not just its length.
  • Change it to the longest substring with at most k distinct characters.
  • The input arrives as a stream of characters. What state do you keep?

Write the update path that detects a concurrent edit

Medium · optimistic concurrency · transactional outbox · audit history

resource carries version INT NOT NULL DEFAULT 1. resource_revision holds revision_id, resource_id, version, actor_user_id, change_kind, patch JSONB, request_id, created_at with UNIQUE (resource_id, version). outbox_event holds aggregate_type, aggregate_id, aggregate_version, event_type, payload, status. A PUT carries the version the client read. Write the exact statements for the single transaction that applies the edit, records the revision and enqueues 'resource.updated', and give the handler's branch on zero affected rows. Then say what PostgreSQL 16 does under READ COMMITTED when two of these updates hit one row at once.

Approach

  1. One transaction, three writes, no network call inside it: UPDATE resource SET title = $3, version = version + 1, updated_at = now() WHERE resource_id = $1 AND tenant_id = $4 AND version = $2; then INSERT the resource_revision row at version $2 + 1; then INSERT the outbox_event row at the same aggregate_version. The event goes to a table rather than a broker because no transaction spans both.
  2. Branch on the affected-row count before doing anything else. Zero has three causes — stale version, wrong tenant, row gone — so re-read once and map to 409 carrying the current version, or 404 for an id outside the caller's tenant, which also stops the endpoint confirming that another tenant's id exists.
  3. State the engine behaviour instead of assuming it. Under READ COMMITTED the second UPDATE blocks on the row lock, and when the first commits PostgreSQL re-evaluates the WHERE clause against the newly committed row, so the version predicate now fails and the statement reports zero rows. Under REPEATABLE READ the identical collision raises SQLSTATE 40001 instead, so the handler must fold both shapes into one conflict response.
  4. Keep UNIQUE (resource_id, version) even though the predicate already serialises writers. It is what makes a lost update unwritable if any other path ever reaches the revision table, and it converts a logic bug into 23505 rather than into a silently missing history row.

Follow-up

  • A client sends the version it read ten minutes ago and the resource has moved three versions. What is in your 409 so it can resolve the conflict without a full re-fetch?
  • Two editors, two disjoint fields, no overlap. Does your answer still refuse the second write, and should it?

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

  1. 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.
  2. 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.
  3. 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.
  4. 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?

Explain the differences between deep and shallow cloning in JavaScript.

Medium · Technical Fundamentals and Programming

Approach

  1. Define shallow copy: a new top-level container whose nested objects are still shared by reference. Spread ({...obj}, [...arr]), Object.assign, slice and Array.from are all shallow, so mutating copy.address.city also changes the original.
  2. Define deep copy: every reachable nested object is duplicated. The modern built-in is structuredClone, which handles Date, Map, Set, RegExp, typed arrays and circular references. It throws on functions and DOM nodes and does not preserve class prototypes, so instances come back as plain objects.
  3. Explain why JSON.parse(JSON.stringify(x)) is a lossy deep copy: Dates become strings, undefined and functions are dropped, Map/Set become {}, NaN and Infinity become null, BigInt throws, and circular references throw.
  4. Write or outline a recursive deep clone: handle primitives, then arrays and plain objects, special-case Date/Map/Set, and use a WeakMap from source to copy to handle cycles. It is O(n) in the number of reachable values.
  5. Close with when to use each: for immutable state updates, copy only the path you change (structural sharing) instead of deep cloning everything, because deep clones of large configs on hot paths cost CPU and memory.

Follow-up

  • How would your clone function handle an object that references itself?
  • Is Object.freeze deep, and how would you make it deep?
  • Why might deep cloning a large per-tenant configuration on every request be a problem in Node.js?

What are the functional differences between let, const, and var?

Medium · Technical Fundamentals and Programming

Approach

  1. Scope: var is function-scoped (and at the top level of a classic script it becomes a property of the global object), while let and const are block-scoped to the nearest braces.
  2. Hoisting: all three are hoisted. var is initialised to undefined, so reading it early gives undefined. let and const sit in the temporal dead zone until their declaration runs, and reading them early throws a ReferenceError, even with typeof.
  3. Redeclaration and reassignment: var can be redeclared in the same scope. let and const cannot (SyntaxError). const must be initialised and cannot be reassigned, but the object it points to is still mutable; use Object.freeze for shallow immutability.
  4. Show the loop closure example: for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)) logs 3, 3, 3 because one binding is shared; with let, each iteration gets a fresh binding and it logs 0, 1, 2.
  5. State your default: const everywhere, let when the value really is reassigned, no var in new code.

Follow-up

  • What does console.log(x); let x = 1; do, and why is that better than var's behaviour?
  • Can you push to a const array? Why?
  • How would you have fixed the var loop bug before let existed?

Explain the concept of currying and how it is applied in your code.

Medium · Technical Fundamentals and Programming

Approach

  1. Define currying: turning f(a, b, c) into f(a)(b)(c), a chain of unary functions. Distinguish it from partial application, which fixes some arguments and returns a function that takes the rest in one call (Function.prototype.bind does partial application).
  2. Write a generic curry: function curry(fn) { return function curried(...args) { return args.length >= fn.length ? fn.apply(this, args) : (...more) => curried.apply(this, [...args, ...more]); }; }
  3. Point out the arity caveat: fn.length ignores parameters with defaults and rest parameters, so a production curry often takes the arity explicitly.
  4. Give an applied example from your own code: configuration-first factories such as const forTenant = tenantId => mapping => record => transform(tenantId, mapping, record), predicate builders such as users.filter(hasRole('admin')), and Express middleware factories such as rateLimit(options).
  5. State the trade-off: reusable, composable functions against harder-to-read stack traces and call sites, so use it where configuration is fixed early and data arrives later.

Follow-up

  • How is currying different from bind?
  • How would you make your curry function work for a function with default parameters?
  • Implement compose or pipe and show how curried functions fit into it.

Design a scalable multi-tenant provisioning system handling concurrent writes from thousands of HR sources.

Medium · System Design and Architecture

Approach

  1. Scope it first: what gets provisioned (accounts and attributes in downstream applications), the number of tenants and sources per tenant, events per second at peak, freshness requirements, and which source wins when two HR systems update the same person.
  2. Ingest: connectors receive webhooks or poll deltas from each source and write normalised change events to a durable log partitioned by (tenant_id, person_id). Changes to one identity are then processed in order while different identities and tenants run in parallel. Each event carries an idempotency key: source, source record id and source version or timestamp.
  3. Concurrent writes: apply changes with optimistic concurrency (UPDATE ... SET ..., version = version + 1 WHERE id = ? AND version = ?, then retry on zero rows), or rely on single-writer-per-partition ordering. Resolve conflicts with attribute-level source precedence (for example HR owns department, another system owns email) instead of blind last-writer-wins.
  4. Multi-tenancy: put tenant_id in every key and query, set per-tenant quotas and weighted fair scheduling so one tenant's bulk import cannot starve the others, and enforce per-target rate limits. Compare a shared schema with tenant_id, schema per tenant and database per tenant on isolation, cost and operations.
  5. Failure handling: retries with exponential backoff and jitter, a dead-letter queue with replay, an audit trail per identity change, and a periodic reconciliation job that compares desired state with actual state in each target to repair drift.

Follow-up

  • Two HR systems send conflicting departments for the same person a second apart. What ends up in the target, and why?
  • One tenant starts a 500,000-record initial load. How do the other tenants stay unaffected?
  • How do you stop a retried event from creating a duplicate account in the target application?

How would you design an identity provisioning pipeline that can handle millions of transactions a day?

Medium · System Design and Architecture

Approach

  1. Size it: 10 million events a day is about 116 per second on average. State a peak multiplier as an assumption (for example 10x during bulk onboarding), then derive worker counts from per-event latency, including calls to rate-limited target APIs.
  2. Lay out the stages with a queue between each: change detection (webhook, poll or delta), normalisation into a canonical identity model, per-tenant mapping and transformation rules, diffing against the last known state so unchanged records are not pushed, outbound provisioning (create, update, disable), and confirmation written back to state.
  3. Keep workers stateless and horizontally scalable, partition by tenant and identity for ordering, and make every target call idempotent: upsert by external id, or look up before create. Delivery is at-least-once with idempotent effects, not exactly-once.
  4. Give deprovisioning its own priority lane, because a delayed termination leaves a departed employee with access. That is a security failure, while a delayed attribute update is a freshness one.
  5. Plan observability and recovery: per-tenant lag and error rate per target, dead-letter queue depth, and the ability to replay a tenant's events from the log after a mapping bug is fixed.

Follow-up

  • How would you add support for a new target application without redeploying the core pipeline?
  • A mapping bug corrupted job titles for one tenant for two days. How do you repair it?
  • Where would you put backpressure when a target API starts returning 429s?

What are the fundamental principles you follow when designing for high availability?

Medium · System Design and Architecture

Approach

  1. Start from a target, not a technique: an availability SLO (99.9 percent allows about 43 minutes of downtime a month), plus RPO and RTO for data. Then name the failures you must survive: a process, an instance, a zone, a dependency, a bad deploy.
  2. Remove single points of failure: stateless services spread across zones behind a load balancer with health checks, replicated datastores with tested automatic failover, and an understanding of replication lag and what it means for reads right after a write.
  3. Contain failures: a timeout on every remote call, retries with exponential backoff, jitter and a retry budget, circuit breakers around flaky dependencies, bulkheads so one tenant or one target application cannot exhaust shared pools, and queues that absorb work while a downstream system is down.
  4. Make change safe, since many outages start with a deploy: canary releases, feature flags, fast rollback and backward-compatible schema migrations, plus idempotent handlers so retries cannot corrupt data.
  5. Verify it: alerts on SLO burn rate, synthetic checks and regular failover drills. A failover that has never been exercised cannot be relied on.

Follow-up

  • Active-active or active-passive across regions: what does each cost you in consistency?
  • How can retries cause an outage, and how do you prevent a retry storm?
  • The primary database fails in the middle of a write. What does the client see, and what happens to that write?

Describe a complex integration you built and explain the rationale behind your design choices.

Medium · Behavioral and Professional Experience

Approach

  1. Choose an integration between systems with different data models and failure behaviour, and describe it in this order: the purpose, the two sides, the volume, and the sync model (webhook push, polling, batch export) with why you chose it.
  2. Explain record matching: how a record in system A is correlated with system B (stable external ids against matching on email), and what happens when the match key changes.
  3. Cover reliability: idempotent writes so a retry after a timeout cannot create duplicates, retries with backoff for 429 and 5xx responses, pagination and partial failure in bulk calls, OAuth token refresh, and a dead-letter path for records that cannot be mapped.
  4. Cover change: how you handled API version changes or schema drift on the other side (contract tests, tolerant parsing, versioned mappings), and how you tested against a third-party system (sandbox, recorded fixtures).
  5. For each decision, name the alternative and why you rejected it, then say what broke in production and how the design absorbed it.

Follow-up

  • The other system times out after your write may have succeeded. What does your code do?
  • The partner changes a field format without notice. How do you find out, and what limits the damage?
  • If you rebuilt this integration today, what would you change first?

Explain your approach to debugging a performance regression in a distributed identity sync service affecting s

Medium · System Design and Architecture

Explain your approach to debugging a performance regression in a distributed identity sync service affecting specific tenants.

Approach

  1. Define the regression precisely: which metric (end-to-end sync latency, throughput, error rate), which percentile (p95 and p99, not the average), which tenants, and when it started.
  2. Find what the affected tenants share that the healthy ones do not: data size or growth, connector or target application type, shard or partition, region, a feature flag or configuration change. That comparison narrows the search faster than reading code.
  3. Line up the timeline: deploys, configuration changes, dependency upgrades, upstream API changes and tenant data growth around the onset. A deploy that coincides with the onset is a lead, not a cause, until the mechanism is shown.
  4. Use per-tenant traces to locate the slow stage (queue wait, processing, database, outbound call). Then test the usual suspects: a hot partition holding a large tenant, a query whose plan changed as the tenant's tables grew (EXPLAIN ANALYZE, a missing tenant-scoped index), N+1 calls to the target, 429 throttling plus retries, lock contention, or Node.js event-loop blocking from synchronous work on large payloads (measure event-loop lag).
  5. Fix, then confirm with the same metric on the same tenants. Add an alert or guardrail for the root cause, such as per-tenant lag alerts or query plan checks, and write up the incident.

Follow-up

  • Only one tenant is slow and nothing was deployed. Where do you look first?
  • How would you reproduce the problem without copying the tenant's production data?
  • The traces show most of the time is spent waiting in the queue. What does that tell you?

Suggested practice checklist

Each day covers one group of the reported Aquera questions: JavaScript fundamentals, the two coding problems, provisioning design, scale and availability, debugging, and the behavioral stories. Every day ends with something written or spoken that you can review before the loop.

Day 1 — JavaScript language fundamentals

  • Write an answer with a code example for deep vs shallow cloning, including a WeakMap-based deep clone and a list of what JSON cloning loses.
  • Write the let/const/var comparison covering scope, hoisting, the temporal dead zone and the var loop closure, and predict the output of three snippets before running them in Node.
  • Implement a generic curry function, test it on a three-argument function, and write two real uses (a predicate builder and a per-tenant transform factory).
  • Write a map/filter/find example that replaces an O(n*m) inner find with a Map lookup, plus a Promise.all version of an async map.

Deliverable: Four written answers with runnable snippets: a clone utility, a scope cheat sheet with predicted outputs, a tested curry function, and an array-method example with its fast-lookup and async versions.

Day 2 — Live coding: sliding window and graph traversal

  • Solve longest substring without repeating characters in JavaScript with no autocomplete, then test it on '', 'bbbb', 'abcabcbb', 'abba' and an emoji string.
  • Implement BFS (head-index queue) and iterative DFS on an adjacency-list Map, then use them to count connected components and detect a directed cycle.
  • Do one timed mock where you narrate from clarifying questions through complexity and hand-traced tests, and record it.
  • Spend a short block on Node.js internals: event loop phases, microtasks versus timers, and what blocks the loop.

Deliverable: Tested solutions to both coding problems plus the two graph variants, a recording of one narrated mock, and a one-page event-loop summary.

Day 3 — Multi-tenant provisioning under concurrent writes

  • Draw the provisioning system for thousands of HR sources: connectors, durable log partitioned by tenant and identity, normaliser, diff, provisioner and reconciler.
  • Write the optimistic-concurrency update that detects a concurrent edit, and the check that holds a per-tenant cap against concurrent creates.
  • Write a conflict-resolution table saying which source owns which attribute and what happens on a tie.
  • Explain the design aloud in ten minutes, covering noisy-neighbour isolation and the dead-letter and replay path.

Deliverable: An architecture diagram, two concurrency-safe update statements, an attribute-precedence table, and a recorded walkthrough of the design.

Day 4 — Pipeline scale and high availability

  • Do the capacity arithmetic for millions of identity transactions a day: average and assumed peak throughput, worker count, and how target API rate limits cap it.
  • Add a priority lane for deprovisioning to the pipeline design and say why terminations cannot wait behind bulk updates.
  • Write a high-availability checklist (SLO, redundancy, timeouts, retries with jitter, circuit breakers, bulkheads, safe deploys) with one example from your own work for each item.

Deliverable: A capacity calculation sheet, an updated pipeline diagram with the deprovisioning lane, and a personal high-availability checklist with examples.

Day 5 — Debugging a slowdown that only hits some tenants

  • Write the debugging sequence: define the metric and percentile, compare affected and healthy tenants, line up the timeline, trace to the slow stage, then test hypotheses.
  • For each of five likely causes (hot partition, query plan change, N+1 target calls, 429 retries, event-loop blocking), write the signal that confirms it and the fix.
  • Practise the answer aloud twice, the second time assuming no deploy happened near the onset.

Deliverable: A written debugging sequence, a five-row cause/signal/fix table, and two spoken run-throughs.

Day 6 — Integration and hardest-problem stories

  • Write the integration story: sync model, record matching, idempotency, retries and rate limits, schema drift, and the alternative you rejected for each choice.
  • Write the hardest-problem story in context, difficulty, options, action, measured result and a specific technical 'what I would change'.
  • Have someone ask three follow-up questions on each story, or write the three yourself and answer them.

Deliverable: Two written stories, each with a list of rejected alternatives, plus answers to three follow-ups per story.

Day 7 — Technical-debt stories and a final mock

  • Write two debt stories: one where you shipped with debt and contained it, one where you paid debt down, each with the evidence and the outcome.
  • Write your criteria for paying debt down versus living with it, and connect each criterion to one of the stories.
  • Prepare three questions for the leadership conversation about connectors, tenant isolation and current scaling problems, then run a mixed mock with one JavaScript question, one coding problem and one design question.

Deliverable: Two debt stories, a written set of debt criteria, three questions for leadership, and notes from a full mixed mock.

Behavioural preparation

Candidates describe the last stage as conversations with leadership about cultural fit and values. The reported behavioral questions focus on judgement over time: two ask about technical debt, one asks for your hardest technical problem and what you would change, and one asks for the reasoning behind an integration you built. Prepare stories where you made the call, can name what it cost, and can describe the outcome concretely.

  1. Describe a complex integration you built and explain the rationale behind your design choices.
  2. Tell me about a production issue that affected only one customer and how you found the cause.
  3. Tell me about a design decision you made that a teammate disagreed with, and how it was settled.

Walk me through the hardest technical problem you solved and what you would do differently today.

Medium · Behavioral and Professional Experience

Approach

  1. Pick a problem that was hard technically, not organisationally, where you drove the solution and can go several levels deep. Candidates report deep-dives into a specific past technical challenge, so the details must hold up.
  2. Structure it: context (the system and its scale), why it was hard (the constraint or unknown), the options you weighed and why you rejected them, what you built or changed, and a measured result (latency, error rate, incidents, cost).
  3. Keep the story in the first person singular: 'I traced', 'I chose'. Name the teammates' contributions accurately without hiding behind them.
  4. Make 'what I would do differently today' specific and technical, for example 'I would have added per-tenant metrics before the rollout instead of after the first incident'. A vague answer such as 'communicate more' adds nothing.
  5. The mistake that sinks it is a story you cannot defend at the next level of detail. Rehearse the follow-up chain on the hardest design choice in it.

Follow-up

  • What was the second option you considered, and what would have happened if you had taken it?
  • How did you prove the fix worked, rather than coinciding with something else?
  • What did this problem change about how you design systems now?

How do you balance technical debt against shipping features on a tight timeline?

Medium · Behavioral and Professional Experience

Approach

  1. Answer with a real case: the deadline and what depended on it, the shortcut you took, and why it was the right call at that moment.
  2. Show how you contained the debt: made it explicit (a ticket with an owner and a date), protected it with tests or a feature flag, and told the people who would inherit it.
  3. Draw the line you would not cross under pressure. In identity work, correctness of access changes (a disable that silently fails) and data integrity are not traded for a date.
  4. Close the loop: when and how the debt was paid back, or why it was deliberately left in place, and what that cost later.
  5. Avoid the extremes. 'I never take on debt' and 'shipping always wins' both sound like you have not had to weigh the two.

Follow-up

  • Tell me about a shortcut that came back to hurt you. What would you change?
  • How do you convince a product manager to give you time to clean something up?
  • Which kinds of debt would you refuse to take on even under a hard deadline?

How do you decide when to pay down technical debt versus when to live with it?

Medium · Behavioral and Professional Experience

Approach

  1. Give your criteria rather than a slogan: how often the code changes, how many incidents or bugs trace back to it, how much it slows the current roadmap, the security or data-correctness risk it carries, and the cost of fixing it.
  2. Back the criteria with evidence: lead time for changes in that module, incident and on-call history, and time lost in reviews. Debt in stable, rarely touched code is often cheapest to leave alone.
  3. Describe how you pay it down: incrementally (refactor the part you are already changing, strangler-style replacement behind an interface) rather than a big-bang rewrite, with the case made to product in delivery terms.
  4. Tell two short stories, one where you chose to live with debt and why that was right, and one where you stopped feature work to fix something, with the measured result.
  5. The answer that sinks you is a rewrite story with no evidence for why the rewrite was needed or what it delivered.

Follow-up

  • How do you measure whether a piece of debt is actually slowing the team down?
  • Tell me about a refactor that was not worth it.
  • How do you prioritise debt against a customer escalation?