cswork / libraryOpen practice workspace

Interview guides · Avetta

Avetta Software Engineer Interview Guide

Avetta Software Engineer interview guide: reported rounds, vanilla JavaScript live coding, integration design questions and a 7-day prep plan.

The 60-second brief

Avetta sells a SaaS platform for supply chain risk management. Client organisations use it to track the safety, compliance and sustainability standing of the contractors and suppliers they work with, in industries from construction to telecommunications. Software Engineers there work on three kinds of things: integrations with third-party systems, the user interfaces clients work in, and the backend services behind both. That work involves large datasets and analytics views. Some candidates interview for a specialised track, such as Integration Developer or front-end work.

This guide is for engineers interviewing for a Software Engineer role at Avetta, on either the integration side or the front-end side. Candidates describe these requirements: strong JavaScript (TypeScript is a plus), a modern framework such as React or Angular, HTML5/CSS3, building or consuming RESTful APIs, and handling asynchronous data flows. Experience with AWS or Azure, CI/CD, Java or Node.js, and supply chain or compliance domain knowledge are listed as nice to have, and roughly 3+ years of professional experience is typical. The guide covers the reported rounds, every reported question with a worked approach, the mistakes that cost candidates points on these question types, and a 7-day plan.

Candidates report three rounds over roughly 3-5 weeks: an introductory video call with a recruiter or HR representative, technical video interviews built around live coding, and a final technical panel with harder coding, system design and time with senior leadership such as the CTO.

Focus areas

  • JavaScript
  • Front-End Development
  • HTML

Overview

Avetta's product helps companies manage risk across their contractor and supplier networks: safety records, compliance status and sustainability data. On the engineering side, that means pulling data in from outside systems, transforming it and showing it in dashboards that client users rely on. The reported questions follow those three areas: browser code that fetches and renders data, design questions about calling unreliable outside services, and questions about keeping data-heavy screens fast.

The reported coding questions are practical browser tasks, not algorithm puzzles: debounce a search input, fetch from a mock API and render a list on a button click, flatten a nested array without the built-in method, explain let/const/var with a closure, and build a counter in plain JavaScript, HTML and CSS. Candidates report the tested topics as JavaScript, front-end development, HTML, CSS and asynchronous programming with callbacks. Prepare by building each one from a blank file, without a framework, and talk through your choices as you type. Other reported scenarios are good practice too: toggling element visibility on click, filtering and sorting objects by several keys, blocking duplicate submits while a request is in flight, rewriting callbacks as async/await, and waiting on three independent requests before rendering.

The design questions are mostly about integration and front-end architecture. They cover a retry strategy for an unreliable third-party API, state management in a large single-page app, protecting sensitive data between a client and an external service, speeding up a data-heavy dashboard, and walking through the most complex system you have built. Related scenarios candidates mention include integrating with a vendor's legacy SOAP API, keeping records consistent when syncing two databases, and speeding up a slow query or endpoint. For each one, have the failure modes, the trade-off and a measurement ready.

Candidates report that the first call includes a presentation on Avetta's business model, methodology and team practices, and that the final stage can include senior leadership such as the CTO. That makes product understanding part of the preparation. Be ready to explain in plain words what the platform does for its clients, why the domain interests you, and how your past architecture decisions affected the business. One candidate review argues that building for maintainability costs less over time than repeatedly fighting fires. Expect the question about balancing shipping speed and code quality to come up, and answer it with a concrete example.

The Avetta interview process

Candidates describe three stages over roughly 3-5 weeks. Stages can vary slightly by track (integration or front-end), and some final stages are held onsite depending on location.

Introductory Video Call

Candidates report a video call with a recruiter or HR representative that includes a slideshow on Avetta's business model, methodology and team practices, with room for discussion. Treat it as a two-way conversation. Have a short background story that maps your experience to integration or front-end work. Listen for details about how clients use the platform, and come with questions that show you followed the presentation. This is also the natural place for the question about why you want to work in supply chain risk management and why Avetta.

What to demonstrate

  • How clearly you connect your background to the Software Engineer role and its track
  • Engagement with the business-model presentation, shown through the questions you ask
  • Motivation for the supply chain risk and compliance domain

How to prepare

  • Write a short background pitch covering one integration or data-heavy UI project and the stack you used (JavaScript/TypeScript, React or Angular, REST APIs)
  • Prepare your answer to why supply chain risk management and why Avetta, built on a plain-language description of what the platform does for clients
  • Write three questions about the business model or team practices that you can adjust based on what the presentation covers
  • Decide which track you are aiming for (integration or front-end) and say so

Technical Video Interviews

Candidates describe one or more technical video sessions on domain expertise with live coding. You write practical functions, build UI elements and make them interactive, usually while sharing your screen. The reported coding questions belong to this format: small browser builds in plain JavaScript, HTML and CSS, plus async data handling. Have a blank HTML/JS/CSS environment ready to go, state your assumptions before you type, and test in the browser as you build instead of at the end.

What to demonstrate

  • Working vanilla JavaScript written live: DOM manipulation, event handling, array methods
  • Asynchronous code: callbacks, promises, async/await, error handling
  • Explaining your choices and debugging out loud when something does not run

How to prepare

  • Set up a reusable CodeSandbox or local index.html/script.js/styles.css template and rehearse screen-sharing while you narrate
  • From a blank file and without a framework, build the reported tasks: debounced search, fetch-and-render list, nested array flatten, counter component
  • Rehearse the async scenarios: rewrite a callback fetch as async/await, use Promise.all for three independent requests, block double submits
  • Write a multi-key filter-and-sort over an array of objects and a click handler that toggles visibility

Final Technical Panel

Candidates report a final technical panel, or an onsite visit depending on location. It has more advanced live coding, system design discussion, and conversations with senior leadership such as the CTO. Prepare for two things: harder builds in the same practical style, and design conversations about integrations, front-end architecture and performance. With leadership, connect technical decisions to business impact and long-term engineering direction.

What to demonstrate

  • Harder live coding in the same practical, browser-based style
  • System design reasoning: integrations, data flow, failure handling, performance
  • How you discuss architecture and business impact with senior technical leaders

How to prepare

  • Rehearse the reported design questions end to end: third-party retry strategy, SPA state management, securing data to an external service, dashboard load time
  • Prepare one architecture story about your most complex system, with a diagram you can sketch, the bottleneck you found and the numbers before and after
  • Sketch an integration with a legacy SOAP vendor API and a two-database record sync, naming the consistency and failure trade-offs
  • Prepare questions for leadership about engineering direction, technical debt and how integrations are prioritised

Where candidates lose points

Using a framework or a built-in shortcut when the task asks for plain JavaScript, such as calling arr.flat() in the flatten question or setting up React for the counter

Practise the reported builds with the DOM API only: querySelector, createElement, addEventListener, textContent, classList and dataset. Write flatten, debounce and multi-key sort by hand at least twice before the technical rounds.

Treating fetch as if it rejects on HTTP errors, and rendering API data with innerHTML and no loading, empty or error state

Check response.ok every time, wrap the call in try/catch/finally, disable the trigger while the request runs, render with textContent, and handle the empty-list case. Make this your default fetch-and-render pattern so it shows up without thinking.

Retry designs that retry every error immediately, including 4xx responses and non-idempotent POSTs that then create duplicate records on the vendor's side

Start the retry answer by classifying errors, then cover exponential backoff with jitter, attempt caps, idempotency keys or lookup by external id, dead-letter handling and a circuit breaker. Rehearse it once against the legacy SOAP and two-database sync scenarios as well.

An unready screen-share setup, or long silences while coding live

Keep an HTML/JS/CSS template ready in CodeSandbox or locally, test screen-sharing beforehand, and practise narrating your choices as you type. When you are stuck, say what you are checking (a console.log, the network tab, the event target).

Treating the introductory call as a formality, with no questions after the business-model presentation and a generic answer to why Avetta

Before the call, write a plain explanation of what the platform does for clients and three questions about it. Afterwards, refine your answer to why supply chain risk management and why Avetta using what you heard.

Questions & practice

Implement a JavaScript function that debounces a search input field.

Medium · Core JavaScript and Live Coding

Approach

  1. Return a wrapper function that closes over a timer id. Each call runs clearTimeout(timer) and then timer = setTimeout(() => fn.apply(this, args), wait). Use a regular function plus rest args so this and the arguments reach the original handler.
  2. Wire it to the input event, not keyup, so paste and IME input also trigger it. Trim the query, and for an empty string clear the results instead of calling the API.
  3. Debouncing cuts the number of calls but does not keep responses in order. Abort the previous fetch with an AbortController, or tag each request with a counter and drop any response that is not the latest.
  4. Each call is O(1). Mention trailing-edge (the default) versus leading-edge firing, and add cancel() and flush() methods so a component can clean up on unmount.
  5. Contrast it with throttle: debounce waits for a pause, throttle fires at most once per interval (better for scroll or resize).

Follow-up

  • Implement throttle, or add a leading-edge option and a maxWait to your debounce.
  • How would you unit-test it without real waiting (fake timers such as jest.useFakeTimers)?
  • The user types fast and an older response arrives after a newer one. What does your code display?

Write a function that flattens a deeply nested array without using built-in array flattening methods.

Medium · Core JavaScript and Live Coding

Approach

  1. Recursive version: function flatten(arr, out = []) { for (const item of arr) Array.isArray(item) ? flatten(item, out) : out.push(item); return out; }. Pushing into one shared output array keeps it O(n) over all elements. Repeated concat or spread copies arrays and can go quadratic.
  2. Iterative version for very deep nesting, where recursion can overflow the call stack: use an explicit stack. Push items in reverse order, pop, and expand arrays as they come up so the original left-to-right order holds.
  3. Add an optional depth parameter that mirrors Array.prototype.flat(depth): recurse only while depth > 0.
  4. Edge cases: empty nested arrays disappear, strings and array-like objects are not flattened (use Array.isArray, not an iterable check), sparse holes are skipped by the built-in flat, and circular references need a Set of arrays already seen so the function throws instead of looping forever.

Follow-up

  • Write a generator version using function* and yield* so a caller can consume it lazily.
  • Flatten a nested object into dot-path keys such as {"a.b.c": 1}.
  • What happens with your recursive version at a nesting depth of 100,000?

Explain the difference between let, const, and var, and demonstrate closure in a short code snippet.

Medium · Core JavaScript and Live Coding

Approach

  1. var is function-scoped, hoisted and initialised to undefined, can be redeclared, and at the top level of a script it becomes a property of the global object. let and const are block-scoped and hoisted into the temporal dead zone, so reading them before the declaration throws a ReferenceError.
  2. const stops reassignment of the binding, not changes to the value: a const object or array can still be modified, and Object.freeze only freezes one level deep.
  3. A closure is a function that keeps access to its outer scope's variables after the outer function returns. Show function makeCounter() { let count = 0; return { inc: () => ++count, get: () => count }; }, where count is private state.
  4. The loop example: for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)) logs 3, 3, 3 because every callback shares one i. With let, each iteration gets a new binding and it logs 0, 1, 2. The pre-ES6 fix is an IIFE.
  5. Link it to real code: debounce, memoize and module-style private state all rely on closures. A closure can also keep detached DOM nodes alive if its listener is never removed.

Follow-up

  • What does typeof return for a variable in the temporal dead zone, compared with one that was never declared?
  • How does this differ between an arrow function and a regular function inside a closure?
  • Implement once(fn) so that fn runs only on the first call.

Build a simple counter component using pure JavaScript, HTML, and CSS.

Medium · Core JavaScript and Live Coding

Approach

  1. Markup: decrement, reset and increment buttons with type="button", plus an <output> or a span with aria-live="polite" for the value. CSS: a flex row, a visible :focus-visible style and a disabled style.
  2. Keep the state in one variable and write a single render() that sets textContent and the disabled flags at min and max. Handlers only change state and then call render(), so data flows one way and the DOM never becomes the source of truth.
  3. Use event delegation: one click listener on the container reads event.target.closest('[data-action]').dataset.action instead of three separate listeners.
  4. Make it reusable with a factory such as createCounter(root, { min, max, step, initial }) that only touches elements inside root. Then two counters on one page do not clash over global ids.
  5. Mention optional extras and their cost: saving to localStorage, handling non-numeric step input, and how the same component looks in React (useState) or Angular.

Follow-up

  • Turn it into a Web Component (a custom element) with attributes for min and max.
  • Keep the value across page reloads and across two open tabs (the storage event).
  • How would you test the bounds and the disabled states?

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

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

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?

Create a basic HTML layout with a button, and write a JS script that fetches data from a mock API and displays

Medium · Core JavaScript and Live Coding

Create a basic HTML layout with a button, and write a JS script that fetches data from a mock API and displays it in a list when clicked.

Approach

  1. Markup: a <button type="button">, an empty <ul>, and a status element with aria-live="polite" for loading and error text. In the script, attach the listener with addEventListener('click', async () => {...}).
  2. fetch only rejects on network failure, so check response.ok and throw on a 4xx or 5xx before calling await response.json(). Wrap the whole thing in try/catch/finally.
  3. Render safely and in one pass: create <li> elements, set textContent (never innerHTML with API data, because of XSS), append them to a DocumentFragment, then call list.replaceChildren(fragment) so a second click does not duplicate items.
  4. Handle every state: disable the button and show loading while the request runs, show a message for an empty array and for errors, and re-enable the button in finally. This also blocks double clicks.
  5. For the mock API, use a public test endpoint or a function that returns a Promise resolving after setTimeout. Say how you would switch it for the real endpoint.

Follow-up

  • Add a 'load more' button that appends the next page without re-rendering existing items.
  • The endpoint returns 10,000 rows. What changes (pagination, chunked rendering, virtualisation)?
  • How would you test this click-fetch-render flow?

How would you design a robust retry mechanism for integrating with an unreliable third-party API?

Medium · Architecture and Integration

Approach

  1. Classify failures first. Retry network errors, timeouts, 429 and 502/503/504, and honour Retry-After. Never retry 400, 401, 403, 404 or 422: those need a fix or an alert, not another attempt.
  2. Use exponential backoff with full jitter (random between 0 and base * 2^attempt, with a cap), a maximum attempt count, and a timeout on each attempt through AbortController. Without jitter, many clients retrying together keep hitting the vendor at the same moments.
  3. Make retries safe to repeat. GETs are idempotent. For writes, send an idempotency key if the vendor supports one, or look up the record by its external id before creating it, so a timed-out POST that actually succeeded is not created twice.
  4. For anything beyond a few quick retries, move the call into a persistent job or queue. Record the attempt state, send jobs to a dead-letter queue after the last attempt, provide a way to replay them, and add a circuit breaker that stops calling the vendor while its error rate is high.
  5. Make it observable: log every attempt with a correlation id, track retry and dead-letter rates, and alert on sustained failure, not on single errors.

Follow-up

  • The vendor enforces a hard rate limit. How do you share that budget across workers (token bucket)?
  • A POST timed out. How do you find out whether it was applied on the vendor's side?
  • Part of a batch sync fails. How do you resume without reprocessing the records that succeeded?

Describe the architecture of the most complex application you have built. What were the bottlenecks?

Medium · Architecture and Integration

Approach

  1. Pick a system you can draw from memory: clients, API layer, services, data stores, background jobs and third-party integrations. Then trace one important request through it from start to end.
  2. Name the bottleneck and how you found it (profiler, slow-query log, APM traces, bundle analyser), with numbers: p95 latency, query time, payload size or error rate before and after.
  3. Explain what you changed and what it cost: a cache that serves stale data, a denormalised table that complicates writes, or a queue that adds operational work.
  4. Keep your own part clear by saying 'I designed' or 'I found' rather than describing the team in general, and end with what you would redesign today.

Follow-up

  • What breaks first if traffic or data volume grows tenfold?
  • Why that datastore or framework and not the obvious alternative?
  • How did you deploy and roll back changes to the bottleneck safely?

Explain how you would handle state management in a large-scale single-page application.

Medium · Architecture and Integration

Approach

  1. Split state by type: server data (cached copies of API results), shared client state (current user, permissions, theme), local component state, URL state (filters, sorting, pagination) and form state. Each one has its own home.
  2. Server data goes in a query cache (TanStack Query, RTK Query, or Angular services with RxJS) that handles caching, request deduplication, invalidation and refetching. Do not copy it into a global store by hand.
  3. Use a global store (Redux, NgRx, Zustand) only for state that is truly shared on the client. Normalise entities by id and read them through memoised selectors so an update re-renders only the views that depend on it.
  4. Keep state as close as possible to where it is used and lift it only when it is shared. Compute derived values (totals, filtered lists) instead of storing them, which removes a whole class of sync bugs.
  5. For optimistic updates, apply the change immediately, keep the previous value, and roll back if the request fails.

Follow-up

  • Two users or two tabs edit the same record. What does the UI show and when?
  • A list re-renders on every keystroke elsewhere on the page. How do you find and fix that?
  • Where do dashboard filters live so that a shared link reproduces the same view?

How do you secure sensitive data when transmitting it between a client application and an external service?

Medium · Architecture and Integration

Approach

  1. In transit: TLS everywhere with certificate validation and HSTS on the web app. Never put secrets or personal data in URLs or query strings, because they end up in logs. Consider mutual TLS between servers.
  2. Never put third-party credentials in the browser. The client calls your own backend, and the backend calls the external service with credentials from a secrets manager. Use OAuth 2.0 client credentials between servers, and authorization code with PKCE when a user delegates access.
  3. Tokens: use short-lived access tokens and verify a JWT's signature, exp and aud claims. Keep session tokens in httpOnly, Secure, SameSite cookies rather than localStorage, where an XSS bug can read them.
  4. Send less data: only the fields the receiver needs. Encrypt or tokenise highly sensitive fields, redact them from logs, and verify incoming webhooks with an HMAC signature and a timestamp to block replays.
  5. Cover rotation and audit: rotate keys without downtime by accepting two keys during the overlap, and log who accessed what.

Follow-up

  • JWT or opaque session token: which do you pick and why?
  • How do you rotate an API key the vendor issued without an outage?
  • How do you confirm that a webhook really came from the vendor?

Walk me through your approach to optimizing the load time of a data-heavy dashboard.

Medium · Architecture and Integration

Approach

  1. Measure first: Web Vitals (LCP, INP), the network waterfall, server timing and a performance profile. Find out whether the time goes to backend queries, payload size, JavaScript bundle size or rendering.
  2. Backend: aggregate on the server instead of sending raw rows, paginate, add the right indexes, precompute heavy aggregates, cache with a clear TTL, and compress responses.
  3. Network: run independent widget requests in parallel with Promise.all instead of one after another, decide between one combined endpoint and many small ones, and use HTTP caching or ETags for data that rarely changes.
  4. Frontend: code-split the chart libraries, lazy-load widgets below the fold with IntersectionObserver, show skeletons so each widget renders as its data arrives, virtualise long tables, and move heavy transforms to a web worker.
  5. Show the result as numbers before and after, and name what you gave up, such as data freshness.

Follow-up

  • One widget is ten times slower than the rest. How do you keep it from holding back the whole page?
  • Users need near-real-time numbers. How does that change your caching?
  • A table needs to show 100,000 rows. What do you do?

p99 jumped on one listing filter while p50 stayed flat

Medium · query planning · composite index · percentiles

After a release that added an owner_user_id filter to the resource listing, p99 rose from 90 ms to 1.9 s while p50 stayed at 40 ms. Traffic and row counts are unchanged. resource carries the index (tenant_id, status, updated_at DESC, resource_id DESC). The new query filters tenant_id and owner_user_id, orders by updated_at DESC, resource_id DESC, and takes 20 rows. On PostgreSQL, explain the shape of the regression, prove it from a query plan, and give the index you would add.

Approach

  1. Start from the shape. A flat p50 with a moved p99 means a subset of requests changed cost, not all of them, so the first job is naming the subset. Bucket the endpoint's latency by the tenant's row count; the natural hypothesis is that large tenants are a small share of requests and all of the tail.
  2. Get the plan for the new query on a large tenant with EXPLAIN (ANALYZE, BUFFERS). Expect an index scan over the tenant's range, a filter discarding most of it, then a Sort feeding the Limit, possibly reporting Sort Method: external merge Disk. Read actual rows on the scan node, not estimated.
  3. Explain why the existing index cannot serve it. A composite B-tree is seekable only as a left prefix, and with no equality predicate on status the scan cannot treat updated_at as an ordering, because rows in the tenant's range are ordered by status first. Everything matching must be read and sorted before LIMIT 20 can apply, so a tenant with 400,000 rows pays 400,000 rows to return 20.
  4. Add (tenant_id, owner_user_id, updated_at DESC, resource_id DESC). Equality on the first two columns leaves the index ordered by updated_at within that pair, so the plan becomes an index scan that stops after 20 rows with no Sort node. PostgreSQL can scan a B-tree backwards, so the DESC markers matter only if the two sort columns ever disagree in direction; keeping them explicit documents the order the keyset cursor depends on.

Follow-up

  • The endpoint paginates with OFFSET. What does page 500 cost with your index, and what does the keyset version cost?
  • How would you have caught this before release, given that a 10,000-row seed database produces the same plan shape at an unnoticeable cost?

Suggested practice checklist

Each day targets one reported question group and builds toward the three reported rounds: plain JavaScript first, then DOM builds, async code, integration design, front-end architecture, behavioral stories, and finally preparation for the first call plus a full mock.

Day 1 — JavaScript scope, closures and recursion

  • In one file, write snippets showing var hoisting, the let/const temporal dead zone, mutating a const object, and the setTimeout loop with var compared with let
  • Write makeCounter and once(fn) to show closures, then explain each one out loud
  • Write a recursive flatten, an iterative stack-based flatten and a depth-limited flatten, and test empty arrays, deep nesting and a circular reference

Deliverable: A scope-and-closure snippet file with spoken explanations, and three tested flatten implementations.

Day 2 — DOM builds from a blank file

  • Set up the reusable HTML/JS/CSS template you will screen-share in the technical rounds
  • Build the counter with one render(), delegated events, min/max bounds and two independent instances on the page
  • Build the button-fetch-list page against a mock API with loading, empty and error states, textContent rendering and double-click protection
  • Build a click handler that toggles the visibility of a group of elements

Deliverable: A working template plus three small pages: the counter, the fetch-and-list, and the visibility toggle.

Day 3 — Async JavaScript and the debounced search

  • Write debounce with cancel and flush, connect it to a search input, and drop stale responses with AbortController
  • Write a callback-based user fetch, then rewrite it with promises and with async/await
  • Render a dashboard only after three independent requests resolve with Promise.all, then handle one failing with Promise.allSettled
  • Write a multi-key filter-and-sort over an array of objects

Deliverable: A debounced search page, the callback-to-async rewrite, a Promise.all dashboard stub and a tested multi-key sort.

Day 4 — Integration design: retries, security, sync

  • Write the full retry answer: error classification, backoff with jitter, idempotency, queue with dead-letter, circuit breaker, metrics
  • Write the secure-transmission answer: TLS, backend-only vendor credentials, OAuth flows, cookie storage, webhook HMAC, key rotation
  • Sketch an integration with a legacy SOAP vendor API and a sync between two databases, naming the consistency guarantee each one gives
  • Work the practice question on holding a per-tenant cap against concurrent creates as a data-integrity exercise

Deliverable: Two one-page design outlines (retry, security), two integration sketches, and a solved concurrency-cap query.

Day 5 — Front-end architecture and performance

  • Draw the state map for a large SPA (server cache, shared client state, URL, local, form) and say where each kind lives in React or Angular
  • Write the dashboard load-time answer as a measure-then-fix checklist covering backend, network and frontend steps
  • Work the practice debugging question where p99 jumped on one listing filter, and the join fan-out SQL question, as slow-endpoint drills
  • Diagram your most complex system and write its bottleneck story with before-and-after numbers

Deliverable: An SPA state map, a dashboard performance checklist, two solved performance drills and one architecture diagram with its story.

Day 6 — Behavioral stories

  • Write STAR stories for the disagreement and the production bug, each with evidence, outcome and what you changed afterwards
  • Write the learning-from-scratch story, tied to a technology in this role's stack if you can
  • Write your rule for balancing speed and maintainability, with one shortcut you took and paid back, then say all four stories out loud

Deliverable: Four written STAR stories, each rehearsed aloud at least once.

Day 7 — Introductory call preparation and full mock

  • Write a plain description of the platform and your answer to why supply chain risk management and why Avetta
  • Write your background pitch and three questions for the business-model presentation, plus two for senior leadership
  • Run a mock while screen-sharing: one plain-JavaScript build from days 2-3 followed by one design question from days 4-5, narrated throughout

Deliverable: A motivation answer, a background pitch, five prepared questions, and notes from one full mock run.

Behavioural preparation

Candidates report behavioral and process questions about technical disagreements, learning new technology, balancing speed against code quality, handling production bugs and motivation for the supply chain risk domain. The final stage can include senior leadership such as the CTO. Prepare stories with concrete decisions and results, and be ready to explain the business effect of your technical choices.

Tell me about a time you disagreed with a senior engineer or product manager about a technical approach. How d

Medium · Behavioral and Process

Tell me about a time you disagreed with a senior engineer or product manager about a technical approach. How did you resolve it?

Approach

  1. Choose a real technical disagreement with stakes, for example a library choice, an API contract, a deadline against a cleaner design, or a data model. Describe both positions fairly, including the other person's constraint (a PM's deadline, a senior engineer's experience).
  2. Show how you settled it with evidence rather than seniority: a quick prototype, a benchmark, a written comparison of options or production data. Say who decided and how.
  3. Give the outcome, what happened to the working relationship, and the result in the product. It is fine if you turned out to be wrong, as long as you say what you learned.
  4. What sinks this answer: a story where the other person was simply foolish, one with no resolution, or one where you went around them.

Follow-up

  • What would you have done if they had overruled you?
  • How did you keep working well with that person afterwards?
  • What would you do differently now?

Describe a situation where you had to learn a new technology completely from scratch to deliver a project.

Medium · Behavioral and Process

Approach

  1. Name the technology, why the project needed it, and the deadline. Strong examples for this role: moving from React to Angular, a new integration protocol such as SOAP or OAuth, TypeScript, or a cloud service.
  2. Describe how you learned it: the official docs, a small throwaway prototype, reading existing production code, and pairing with or asking someone experienced. Include the point where you decided you knew enough to build.
  3. Show how you limited the risk: a smaller first scope, tests around the new parts, and a review from someone who knew the tool.
  4. Close with the delivered result and what you left behind for others, such as a write-up or a starter template. Avoid stories where you only completed a course.

Follow-up

  • What mistake did you make because you were new to it?
  • How do you decide whether to learn a tool deeply or just enough?
  • How would you get up to speed on this team's stack in your first month?

How do you balance the need to ship features quickly with the need to write clean, maintainable code?

Medium · Behavioral and Process

Approach

  1. Give a rule, not a slogan. Be strict about things that are expensive to undo, such as data models, public API contracts, security and integration interfaces. Allow shortcuts inside code that can be rewritten later.
  2. Back it with one example: a shortcut you chose on purpose to meet a date, how you recorded it (a ticket or a code comment), and when and how you paid it back.
  3. Name the habits that keep clean code cheap: tests on core paths, small pull requests, code review and feature flags for shipping in pieces.
  4. Mention the long-term cost. One candidate review argues that maintainable code saves time compared with constant firefighting, so show you can make that case to a product manager in terms of delivery dates.
  5. What sinks this answer: 'I always write clean code' with no trade-off, or 'shipping always wins' with no plan to repay the debt.

Follow-up

  • Tell me about a shortcut that came back to cause problems.
  • How do you get technical debt work onto a product roadmap?
  • What will you not compromise on, even under deadline pressure?

Tell me about a time you discovered a critical bug in production. What were your immediate steps?

Medium · Behavioral and Process

Approach

  1. Follow the order of a real incident. Assess the impact (who is affected and how badly), communicate early to your team and stakeholders, mitigate first with a rollback, feature flag or hotfix, then find the root cause.
  2. Give concrete details: how the bug was found (an alert, a customer report, your own check), how you confirmed the mitigation worked, and how you found and repaired any records it damaged.
  3. End with prevention: the test that would have caught it, the monitoring or alert you added, and a blameless review.
  4. What sinks this answer: a solo hero fix with no communication, blaming a colleague, or skipping the data clean-up.

Follow-up

  • How do you decide between rolling back and fixing forward?
  • How did you find which customers or records were affected?
  • Which alert would have caught it earlier?

Why are you interested in the supply chain risk management space, and why Avetta specifically?

Medium · Behavioral and Process

Approach

  1. Explain the product in plain words: a SaaS platform that clients use to manage safety, compliance and sustainability risk across their contractors and suppliers. Show you followed the business-model presentation from the first call.
  2. Connect it to your own work, for example B2B SaaS, connecting to third-party systems, data-heavy dashboards or compliance workflows, and to the engineering problems that interest you (integrations, large datasets, reliability).
  3. Give one specific reason for Avetta rather than the industry in general, taken from what you learned in the presentation or your own research, and admit any domain knowledge you lack.
  4. What sinks this answer: generic praise for the mission, or talking only about the role's benefits.

Follow-up

  • Which part of the platform would you want to work on first, and why?
  • What do you think is technically hard about this domain?
  • What questions do you have for the team?