Interview guides · AvidXchange
AvidXchange Software Engineer Interview Guide
AvidXchange Software Engineer interview guide: Karat coding, API design, SQL without aggregates, .NET and JavaScript, plus a 7-day prep plan.
The 60-second brief
AvidXchange provides a SaaS platform for accounts payable automation and B2B payments, aimed at middle-market companies. Candidates describe the engineering work as backend services, frequently in C# and .NET, alongside frontend components in JavaScript frameworks, with a lot of time spent writing, reviewing and tuning SQL over large volumes of financial data. Reported day-to-day work also includes refactoring older code, working with product managers and QA engineers, partnering with DevOps on deployment pipelines, and taking part in stand-ups, architecture reviews and code reviews.
This guide is for candidates preparing for a Software Engineer interview at AvidXchange, from a strong entry-level profile up to mid-level, as the requirements candidates describe suggest. It walks through each reported round, gives a worked approach and follow-ups for the behavioral and design-judgment questions, and ends with a seven-day plan. Nice-to-have areas that candidates mention, such as CI/CD, Azure or AWS and a FinTech background, are covered where they connect to a reported question.
Candidates report three stages over roughly three to five weeks: a Phone Screen with an internal recruiter, a Karat Technical Interview run by a third party, and Final Technical Interviews with AvidXchange engineers and engineering managers.
Focus areas
- SQL
- System Design
- SQL Query Writing (coding task)
Overview
The role is described as backend-heavy work on a payments and accounts payable platform: C# and .NET services, JavaScript on the front end, and a great deal of SQL. That shapes the loop. Expect one layer of general computer science (graphs, strings, arrays, grids), one layer of API and data design, and one layer of practical stack knowledge (SQL, JavaScript, OOP in .NET, deployment). Prepare all three, because the reported questions come from each.
The Karat stage is reported as a structured session that mixes scenario-based API design with Leetcode-style coding. Candidates describe easy-to-medium problems, up to three in 45 minutes, so speed and correctness matter more than cleverness. The coding questions reported for it are a DFS search in a graph, the longest substring without repeating characters, a bipartite check, two-sum by index, and a maximum path sum across a matrix; other reported examples include counting islands in a grid, detecting a cycle in a directed graph and flattening a nested structure. Practise them in sets of three, with complexity stated aloud after each.
The final interviews are described as conversational and technical at once. Candidates report a live coding or screen-sharing component that leans toward SQL, including a query that must avoid aggregate functions such as SUM, COUNT and MAX, plus questions on indexing, JavaScript Promises, the four pillars of object-oriented programming in .NET and the DevOps practices behind a safe deployment. The resume deep dive means every project you list needs a technical story you can tell at the level of queries, classes and failure modes.
Because the platform moves invoices and payments, several reported design questions share one thread: what happens when something fails halfway. Daily batch files, a request that fails mid-transaction, a call that fans out to three services and a multi-tenant schema all reward the same habits: idempotent operations, explicit failure states, an audit trail, and tenant isolation designed in from the start. Build those habits once and reuse them across answers.
The AvidXchange interview process
Candidates describe three stages in this order. Question types are tied to a stage only where candidates tie them; the rest are the types worth practising for the whole loop.
Phone Screen
Candidates describe an opening call with an internal recruiter that covers your background, your interest in AvidXchange and a high-level check of technical fit. Some candidates report a few basic technical questions in the call, and that the recruiter takes notes on the answers. Treat it as a short pitch: a clear career story, a specific reason for this company, and crisp definitions of the tools on your CV.
What to demonstrate
- How clearly you tell your background and current work
- Whether your reason for choosing AvidXchange is specific to accounts payable and B2B payments
- High-level fit between your experience and the SQL, .NET or JavaScript work the role lists
How to prepare
- Write a two-minute walk through your CV that ends on the kind of backend, data-heavy work you want next, and rehearse it aloud.
- Build the answer to why you want to work here from the business: invoices, approvals and payments for middle-market companies on a SaaS platform, linked to something you have built.
- Keep one-sentence definitions ready for a database index, a Promise, the four OOP pillars and CI/CD, in case a basic technical question comes up.
- Prepare two questions about the team's stack and how deployments and code reviews work.
Karat Technical Interview
A structured interview run by Karat rather than AvidXchange staff, reported as about an hour and combining scenario-based API design with Leetcode-style coding. Candidates describe easy-to-medium problems, up to three in 45 minutes, a scripted feel, and an immediate follow-up question when a problem is finished early. The coding questions reported under this round are graph traversal, a sliding-window string problem, a hash-map lookup and a grid path sum. For the API scenarios, candidates say the prompts can be thin on context, so confirm scope before designing.
What to demonstrate
- Working code for graph, string, array and matrix problems
- Time and space complexity stated for each solution
- Handling an API design scenario with missing context by asking clarifying questions first
How to prepare
- Time yourself on sets of three: one graph problem (DFS search, bipartite check), one string or array problem (longest substring, two-sum) and one grid problem (maximum path sum).
- Write the working version first, then say its complexity and name one edge case before moving on.
- Rehearse the opening of an API answer: who calls it, expected volume, what the caller does on failure, then endpoints, methods and status codes.
- Practise the batch-file and user-management design questions out loud, since they are the reported API design types.
Final Technical Interviews
Candidates describe conversational interviews with AvidXchange engineers and engineering managers that go deep on your resume, core technologies and fit. The reported core-technology questions are a SQL query without aggregate functions, indexing and when to avoid it, how Promises and asynchronous code work in JavaScript, the four pillars of OOP with a .NET example, and the DevOps principles behind a safe production deployment. Candidates also report live coding or screen sharing here, often in SQL, and design discussions such as a multi-tenant schema and partial failure across services.
What to demonstrate
- SQL fluency under a constraint: self-joins, subqueries, window functions and indexing
- JavaScript asynchronous behavior and OOP applied in .NET
- Awareness of how code is built, deployed and monitored after merge
- Depth on the projects listed on your resume
How to prepare
- Write the second-highest-salary-per-department query three ways (self-join or NOT EXISTS, a correlated subquery, a window function) and be ready to say which are allowed without aggregates.
- Explain indexing end to end: B-tree lookup, composite column order, covering indexes, write overhead and low-selectivity columns, then run EXPLAIN on a sample table.
- Write a short script that shows the order of Promise callbacks, then compare Promise.all, Promise.allSettled and sequential awaits for several API calls.
- For each resume project, prepare the schema, the hardest bug, the trade-off you made and the deployment path.
Where candidates lose points
Reaching for MAX, COUNT or SUM in the SQL question that forbids aggregate functions
Practise second-highest-per-group three ways: NOT EXISTS or a self-join that excludes the top salary, a correlated subquery, and a window function such as DENSE_RANK. Ask whether window functions are allowed under the constraint, and cover ties, NULL salaries and departments with a single salary, which should return no row.
Answering a vague API or batch-file design prompt straight away with a list of endpoints
Open with questions: who calls it, expected volume, what happens on failure, how files arrive and who may send them. State assumptions, then give endpoints, methods and status codes, plus validation, idempotent reprocessing, a quarantine for bad records, an audit trail and encryption for the batch case.
Solving the graph questions only for the connected, undirected happy path
For the bipartite check, loop over every vertex so disconnected components are covered, and treat a self-loop as a failure. For directed cycle detection, track three states (unvisited, in the current path, done) because a plain visited set reports false cycles. State O(V+E) and the recursion-depth risk of DFS.
Giving one-line textbook definitions on indexing, Promises, OOP and DevOps
Add the trade-off and an example each time. For indexes, mention write overhead and low-selectivity columns. For Promises, name the microtask queue and the difference between Promise.all and Promise.allSettled. For OOP, show an interface and an override in C#. For DevOps, describe build, test, staged rollout, rollback and monitoring.
Treating the partial-failure and multi-tenant questions as one-box diagrams
For fan-out calls, separate required from optional dependencies and describe timeouts, retries, fallbacks and per-section status. For multi-tenant schemas, compare a tenant_id column on shared tables, schema per tenant and database per tenant, and say how you stop one tenant's query from reading another's rows.
Questions & practice
Implement a Depth-First Search (DFS) algorithm to find a specific node in a graph.
Medium · Data Structures and Algorithms (Karat Ro
Approach
- Clarify the representation (adjacency list vs edge list vs matrix), whether the graph is directed, and whether it may have cycles or several disconnected components; build an adjacency list (dict of lists) if given edges.
- Iterative DFS: push the start node on a stack, pop, return as soon as it equals the target, otherwise mark it visited and push unvisited neighbours. A visited set is what stops infinite loops on cycles.
- Complexity is O(V + E) time and O(V) space for the visited set and stack. A recursive version is shorter, but Python's default recursion limit (about 1000 frames) can fail on a long chain, so say why you chose the stack.
- Keep a parent map (child -> node it was reached from) so you can return the path to the target, not just true/false. Edge cases: start equals target, target absent, start not in the graph, self-loops.
Follow-up
- How would you return the path from the start to the target, and is it the shortest one?
- When would you switch to BFS instead, and what does it guarantee that DFS does not?
- How would you change this to detect a cycle in a directed graph?
Given a string, find the longest substring without repeating characters.
Medium · Data Structures and Algorithms (Karat Ro
Approach
- Brute force checks every substring for duplicates in O(n^2) to O(n^3); state it, then move to a sliding window that grows the right end one character at a time.
- Keep a map from character to its last seen index. When the current character was seen at or after the window's left edge, set left = last[c] + 1; then record last[c] = right and update best = max(best, right - left + 1).
- Never move left backwards: use left = max(left, last[c] + 1). The string "abba" shows why, since the second 'a' was last seen before the window's current left edge.
- O(n) time, since each index enters the window once; O(min(n, alphabet)) space. A fixed array of 128 works for ASCII, a dictionary for general Unicode. Test: empty string (0), all the same character (1), all distinct (n).
Follow-up
- Return the substring itself, not only its length.
- How does the window change for the longest substring with at most k distinct characters?
- What changes if the input is a stream you cannot index back into?
Write a function to determine if a given graph is bipartite.
Medium · Data Structures and Algorithms (Karat Ro
Approach
- A graph is bipartite exactly when it has a 2-colouring with no edge between same-coloured nodes, which is equivalent to having no odd-length cycle.
- BFS colouring: colour an uncoloured node 0, give each neighbour the opposite colour, and return false as soon as a neighbour already has the same colour as the current node.
- Loop over every node and start a new BFS from each uncoloured one, because a disconnected graph must be checked component by component; a check from node 0 alone is a common bug.
- O(V + E) time and O(V) space. A self-loop makes the graph non-bipartite immediately, and isolated nodes are fine. Union-find is an alternative: union all neighbours of u together and fail if u ends up in the same set as any neighbour.
Follow-up
- Return the two groups, or an odd cycle that proves the graph is not bipartite.
- Given pairs of people who dislike each other, can they be split into two groups?
- Would you choose DFS or BFS here, and does the choice change the complexity?
Given an array of integers, return the indices of the two numbers that add up to a specific target.
Medium · Data Structures and Algorithms (Karat Ro
Approach
- Brute force tries every pair in O(n^2). Clarify whether exactly one answer exists, whether the same element may be used twice, and what to return if there is no pair.
- One pass with a hash map from value to index: for each x at index i, check whether target - x is already in the map; if yes return [map[target - x], i], otherwise store x -> i.
- Checking before inserting stops an element pairing with itself and still handles duplicates: [3, 3] with target 6 returns [0, 1].
- O(n) time and O(n) space. Sorting with two pointers is O(n log n) and loses the original indices unless you sort (value, index) pairs, so explain why the hash map wins when indices are required.
Follow-up
- If the array is already sorted, can you do it in O(1) extra space?
- Return all unique pairs that sum to the target, without duplicates.
- How would you extend this to three numbers, and what is the complexity?
Traverse a matrix to calculate the maximum path sum from the top-left to the bottom-right.
Medium · Data Structures and Algorithms (Karat Ro
Approach
- Clarify the allowed moves first. The standard version allows only right and down, which makes the grid a DAG and lets dynamic programming solve it. Ask whether values can be negative and whether the grid can be empty.
- Recurrence: dp[i][j] = grid[i][j] + max(dp[i-1][j], dp[i][j-1]). The first row and first column have only one predecessor, so they are running sums. The answer is dp[m-1][n-1].
- O(mn) time. Space drops from O(mn) to O(n) with a single row updated left to right, because dp[j] still holds the value from the row above and dp[j-1] already holds the current row.
- Explain why greedy (always take the larger neighbour) fails, using a small counterexample. If moves in all four directions are allowed without revisiting cells, the DP ordering no longer holds and the problem becomes a backtracking search.
Follow-up
- Reconstruct the actual path, not only its sum.
- Some cells are blocked: how does the recurrence handle unreachable cells?
- How would you solve it if the path could also move diagonally down-right?
Walk me through how asynchronous programming and Promises work in JavaScript.
Medium · Core Technologies and SQL
Approach
- JavaScript runs your code on one thread with one call stack. Timers, network and file I/O are handed to the host (browser APIs or libuv in Node), and their callbacks are queued for the event loop to run when the stack is empty.
- Two queues matter: microtasks (promise reactions, queueMicrotask) are drained completely after each task, before the next macrotask (setTimeout, I/O callbacks). So logging sync code, then Promise.resolve().then(...), then setTimeout(..., 0) prints the sync line, then the then callback, then the timeout.
- A Promise is pending, then settles once as fulfilled or rejected. .then returns a new promise, which is what makes chaining work, and async/await is syntax over the same mechanism; errors go to .catch or a try/catch around await, and an unhandled rejection is a bug.
- Combinators: Promise.all rejects on the first rejection; allSettled waits for every result; race settles with the first to settle; any fulfils with the first success and rejects with AggregateError only if all fail. Awaiting inside a for loop runs the calls one after another, while starting them first and awaiting Promise.all runs them concurrently.
Follow-up
- Predict the output order of a snippet that mixes setTimeout, promises and async functions.
- Run 100 API calls with at most 5 in flight at once.
- How would you cancel or time out a pending fetch?
How would you structure the database schema for a multi-tenant SaaS application?
Medium · System and API Design
Approach
- Name the three models and their trade-offs: shared tables with a tenant_id column (cheapest, weakest isolation), a schema per tenant, and a database per tenant (strongest isolation and per-tenant restore, highest cost and migration effort).
- For shared tables: put tenant_id on every tenant-owned table, lead composite indexes with it (tenant_id, created_at), and scope unique constraints per tenant, for example UNIQUE (tenant_id, email).
- Prevent cross-tenant references with composite foreign keys on (tenant_id, id), and enforce filtering in the database with row-level security (available in PostgreSQL and SQL Server), not only in application code.
- Plan the operational parts: noisy neighbours (a large tenant can be moved to its own database), per-tenant data export and deletion, and migrations that must run across every schema or database in the per-tenant models.
Follow-up
- One tenant grows to most of your data volume: what do you do?
- How would you support tenant-specific custom fields: extra columns, a key-value table or a JSON column?
- How do you guarantee a bug in a query cannot leak another tenant's rows?
Write a SQL query to retrieve specific relational data without using any aggregate functions (e.g., no SUM, CO
Medium · Core Technologies and SQL
Write a SQL query to retrieve specific relational data without using any aggregate functions (e.g., no SUM, COUNT, MAX).
Approach
- Confirm what counts as an aggregate. Ranking window functions (ROW_NUMBER, RANK, DENSE_RANK) and DISTINCT are not aggregate functions, but MAX() OVER still is, so avoid it. Then confirm the tie rule, using the second highest salary per department as the worked case.
- Window version: compute DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) in a CTE or subquery and filter rank = 2 in the outer query, because a window function cannot be filtered in the WHERE clause of its own SELECT.
- Anti-join version: the top salary is a row e1 for which NOT EXISTS a row e2 in the same department with e2.salary > e1.salary. For the second highest, apply the same NOT EXISTS test only among rows that do have a higher salary in their department.
- For one scope only: SELECT DISTINCT salary ... ORDER BY salary DESC OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY (LIMIT 1 OFFSET 1 in PostgreSQL or MySQL). Edge cases: ties (DENSE_RANK vs RANK vs ROW_NUMBER), departments with one distinct salary (return nothing, or NULL through a LEFT JOIN), and NULL salaries, since NOT IN against a subquery that returns NULL yields no rows; prefer NOT EXISTS.
Follow-up
- Generalise to the Nth highest salary per department.
- Departments with no second salary must still appear with NULL: how does the query change?
- Which index makes the anti-join version fast?
Explain the concept of indexing in SQL. How does it work, and when would you avoid using it?
Medium · Core Technologies and SQL
Approach
- Mechanics: most indexes are B-trees kept sorted on the key, so an equality or range lookup becomes a seek in O(log n) page reads instead of a full scan. In SQL Server the clustered index is the table's row order (one per table), and nonclustered indexes store the key plus a row locator.
- Composite and covering indexes: an index on (a, b) serves predicates on a, or on a and b, but not on b alone. Adding INCLUDE columns lets a query be answered from the index without key lookups back to the table.
- Non-sargable predicates defeat an index: a function on the column (WHERE YEAR(order_date) = 2025), a leading wildcard (LIKE '%abc'), or an implicit type conversion. Rewrite them as ranges on the bare column.
- When to avoid one: write-heavy tables (every INSERT, UPDATE and DELETE maintains every index), low-selectivity columns where a scan is cheaper, very small tables, and overlapping duplicate indexes. Confirm with the execution plan, not by guessing.
Follow-up
- Your index exists but the plan still shows a scan: what do you check?
- Why can a random GUID clustered key hurt insert performance compared with an increasing identity key?
- How do you choose the column order in a composite index?
Given a scenario where an application needs to securely process daily batch files, how would you design the sy
Medium · System and API Design
Given a scenario where an application needs to securely process daily batch files, how would you design the system?
Approach
- Pin down the scope first: who sends the files and how, their size and format, when they arrive each day, what processing means (validate, transform, load, call downstream), and what must happen to a bad row versus a bad file.
- Secure intake: SFTP or upload to object storage through short-lived signed URLs; TLS in transit, encryption at rest, least-privilege credentials per sender; verify a checksum or signature (for example PGP) before parsing, and keep sensitive values out of logs.
- Idempotency: record each file in a manifest table keyed by sender, business date and content hash, with a status (received, validated, processing, done, failed), so a re-delivered or retried file is never applied twice.
- Pipeline: land the file in a staging area, validate the schema and row counts, then have queue workers process it in chunks with a checkpoint per chunk so a crash resumes instead of restarting. Rejected rows go to a quarantine table with reasons.
- Operations: alert when the expected file has not arrived by its cutoff, when the reject rate spikes, or when processing overruns; keep an audit trail of who sent what and what was applied, and retry transient failures with backoff.
Follow-up
- The same file arrives twice, or a second corrected file arrives for the same day: what happens?
- The job dies halfway through a large file: how do you resume without double-applying rows?
- How does the design change if the file grows tenfold or must be processed within minutes of arrival?
Design a RESTful API for a user management system. What HTTP methods and status codes would you use?
Medium · System and API Design
Approach
- Resources: /users and /users/{id}, plus sub-resources such as /users/{id}/roles. Use nouns in the path and let the HTTP method carry the action, with no /createUser-style routes.
- Methods and codes: GET /users (200, paginated with limit and a cursor or page, filtered by query parameters); POST /users returns 201 with a Location header; GET /users/{id} returns 200 or 404; PATCH for partial updates and PUT for full replacement return 200 or 204; DELETE returns 204.
- Errors: 400 for malformed input (422 if you separate validation errors), 401 for missing or invalid credentials versus 403 for authenticated but not allowed, 409 for a duplicate email, 429 for rate limiting. Return one consistent error body with a code and message.
- Safety and semantics: GET, PUT and DELETE are idempotent and POST is not, so support an Idempotency-Key header on creation. Use ETag with If-Match to prevent lost updates (412 on mismatch), never return password hashes, and consider deactivating users instead of hard deletes for auditability.
Follow-up
- What is the difference between PUT and PATCH, and which is idempotent?
- How would you version this API without breaking existing clients?
- What should a second DELETE on the same user return, and why?
Explain the four pillars of Object-Oriented Programming and provide an example of how you use them in.NET.
Medium · Core Technologies and SQL
Approach
- Encapsulation: private fields and properties with private setters, so state changes only through methods that keep invariants. Example: an Invoice whose ApplyPayment(amount) rejects negative amounts or overpayment instead of exposing a public Balance setter.
- Abstraction: depend on an interface such as IPaymentGateway or INotifier and inject it through the constructor (ASP.NET Core's built-in DI), so callers do not know or care about the concrete implementation.
- Inheritance: C# allows one base class and many interfaces. Use an abstract base class for shared behaviour, mark extension points virtual and override them, and seal classes that should not be extended. Prefer composition when the relationship is not truly is-a.
- Polymorphism: runtime dispatch through virtual/override or interface calls (a list of INotifier with Email and Sms implementations, each with its own Send), versus compile-time method overloading. Note that the new keyword hides a method instead of overriding it, so the base reference still calls the base version.
Follow-up
- When would you choose an abstract class over an interface in C#, now that interfaces can have default methods?
- How do the SOLID principles relate to these four pillars in code you have written?
- What are the Transient, Scoped and Singleton lifetimes in .NET dependency injection, and when does each cause bugs?
What DevOps principles do you follow to ensure your code is safely deployed to production?
Medium · Core Technologies and SQL
Approach
- CI on every pull request: build, unit and integration tests, static analysis and a dependency vulnerability scan, plus a required review, so main is always deployable. Keep changes small so each deploy is easy to reason about.
- Build the artifact once and promote that same artifact through test, staging and production. Configuration comes from the environment, secrets from a vault or managed secret store (never the repo), and infrastructure is defined as code.
- Separate deploy from release: feature flags for risky behaviour, and blue-green or canary rollouts gated on health metrics (error rate, latency, saturation), with automatic or one-command rollback.
- Database changes follow expand and contract: add nullable columns or new tables first, deploy code that handles both shapes, backfill, then remove old columns in a later release, so a rollback never meets a schema it cannot read.
- After the deploy: structured logs, metrics, traces and alerts on the paths you changed, plus a short post-incident review when something slips through, to add the missing test or check.
Follow-up
- A migration has already run and the new release is broken: how do you roll back?
- Which signals would you use to decide automatically whether a canary is healthy?
- How do you ship an urgent hotfix without skipping the safety checks?
Exports duplicate a row range about once a week
Hard · leases · clock skew · idempotency
Roughly once a week an export writes a file containing a duplicated range of rows. The affected job_run rows show attempt = 1, status = succeeded, one started_at, and a lease_owner naming a different host from the one whose logs show the job starting. Leases last 30 seconds and are heartbeated every 10 from inside the handler; lease_expires_at is computed on the worker and compared against the database's now(). Find the mechanism, and give a fix that holds even if you cannot fix the clocks.
Approach
- Start from the fact that eliminates the obvious answer. attempt = 1 means no retry was recorded, so this is not a re-run after failure; two workers ran the same row concurrently and the takeover path never touched the counter. lease_owner naming a host other than the one that started the job is the same statement from the other side.
- Enumerate the mechanisms that cause a premature takeover, then find the signal that separates them. Either the lease genuinely expired because the heartbeat did not fire, which is what happens when the heartbeat runs on the handler's own thread and the handler makes a long blocking call, or it only appeared expired because two clocks disagree, since lease_expires_at is written from the worker's clock and evaluated against the database's. The discriminator is the distribution: incidents clustered on the longest exports indict the heartbeat, incidents clustered on one host indict skew. Measure both, and measure each host's offset against the database directly.
- Read the reclaim query precisely. In PostgreSQL now() is transaction start time, not statement time, so a reclaimer holding a long transaction compares against an older timestamp than expected; clock_timestamp() is the statement-time function. This is worth ruling in or out before you redesign anything, because it changes which rows look expired.
- Remove the second clock rather than trying to synchronise it. Issue and extend the lease in the database, with lease_expires_at = now() + interval '30 seconds' in both the claim and the heartbeat, so exactly one clock is ever compared and worker skew stops mattering to this predicate.
Follow-up
- The displaced worker has already streamed half the file to object storage. What makes that side effect safe to repeat?
- You now count takeovers. What alert fires on that counter, and at what threshold?
Suggested practice checklist
The plan follows the three reported stages: first the Karat-style coding set, then SQL and design, then the stack questions and behavioral stories, ending with a full mock run. Each day produces something you can check.
Day 1 — Map the loop and set a graph baseline
- Write the three reported stages, then list which question types each is reported to cover, and mark your weakest.
- Solve the DFS node search and the bipartite check against a timer, in your own words after each: what the traversal is, its complexity, and one edge case.
- Add one directed-graph cycle check, written with three visit states.
Deliverable: A one-page loop map plus two working graph solutions with complexity notes.
Day 2 — Karat pace: strings, arrays and grids
- Write the sliding-window solution for the longest substring without repeating characters, and the hash-map solution for two-sum by index.
- Write the dynamic programming solution for the maximum path sum from top-left to bottom-right, then handle a single row or column.
- Run all three back to back against a 45-minute limit and log where the time went.
Deliverable: Three timed solutions and a short log of the minutes lost on each.
Day 3 — SQL under constraints and indexing
- Write the second-highest salary per department without aggregate functions using a self-join or NOT EXISTS, a correlated subquery and DENSE_RANK; test ties and NULLs.
- Create a sample table with a few hundred thousand rows, run EXPLAIN before and after adding a single-column and a composite index, and note when the plan ignores them.
- Write a half-page answer on when you would avoid an index.
Deliverable: Three working versions of the query, an EXPLAIN before-and-after, and the half-page indexing answer.
Day 4 — API, batch and multi-tenant design
- Sketch the secure daily batch file system: intake, validation, quarantine of bad rows, idempotent processing, audit trail and encryption.
- Write the user-management REST API as a table of resources, methods and status codes, including conflict and validation errors.
- Draw the multi-tenant schema (shared tables with tenant_id versus schema per tenant) and write the fan-out-to-three-services failure matrix.
Deliverable: A design outline for the batch system, an endpoint table, a tenant schema sketch and a failure matrix.
Day 5 — JavaScript, .NET and deployment
- Write a script that logs the order of synchronous code, microtasks and timers, and compares Promise.all, Promise.allSettled and sequential awaits for three API calls.
- Write a small C# example showing encapsulation, inheritance, polymorphism and abstraction, for instance a payment method interface with two implementations.
- Write a deployment checklist covering CI build and tests, artifact versioning, staged rollout, rollback and monitoring.
Deliverable: An annotated runnable script, a C# sample and a one-page deployment checklist.
Day 6 — Behavioral stories and the company pitch
- Write the answer to why you want to work at AvidXchange from the business facts you researched, tied to one project of yours.
- Outline the stakeholder-explanation story, the technical-hurdle story and the changing-requirements story, each with a measurable outcome.
- Say all four aloud and cut each opener to two sentences.
Deliverable: Four story outlines with outcomes and a recording or notes from the spoken run.
Day 7 — Full mock run
- Open with your recruiter-call pitch and the reason you want to work here.
- Do a Karat-style set: the two-sum problem, then the user-management API question asked with clarifying questions first.
- Write the constrained SQL query live in a plain editor while narrating, then redo whichever earlier answer was weakest.
Deliverable: A scored checklist of the mock run and a short list of fixes for each miss.
Behavioral and fit preparation
Candidates report behavioral content in the recruiter call and again in the final interviews, where the conversation goes into resume projects and fit. The reported prompts cover motivation for joining, explaining technical ideas to non-technical people, a technical hurdle from your resume and unclear requirements. For this role, stories that involve SQL, backend or deployment work and end with a clear outcome are the most reusable.
- Tell me about a release or deployment that went wrong and how you handled it.
- Describe a disagreement with a teammate or product manager over a technical decision and how it ended.
If an API request requires fetching data from three different microservices, how do you handle partial failure
Medium · System and API Design
If an API request requires fetching data from three different microservices, how do you handle partial failures?
Approach
- Start by sorting the three calls into required and optional for the endpoint to make sense, for example an invoice record that must exist versus enrichment such as vendor details. That split decides whether each failure fails the request, degrades it or substitutes a default.
- Call the services in parallel, each with its own timeout inside one overall deadline, so a slow dependency cannot hold the whole request. Retry only idempotent reads, using exponential backoff with jitter and a small attempt cap, so retries do not pile load onto a struggling service.
- Put a circuit breaker or bulkhead around each dependency, and fall back to cached or stale data for the optional parts. When a required call fails, return a clear error (502, 503 or 504 as fits, with Retry-After where useful) instead of a body that looks complete but is not.
- For partial success, return 200 with an explicit per-section status (for example vendor: unavailable) so clients cannot mistake a missing section for an empty one. Add a correlation ID across all three calls and log which dependency failed and why.
- If any call writes data, give it an idempotency key and use compensating actions (a saga) or an outbox, because a local database transaction cannot roll back across services.
Follow-up
- How would you stop retries from becoming a retry storm when one service is slow?
- How do you decide between failing the whole request and returning partial data, and how does the client learn which it got?
- How would you test these failure paths before production?
What do you know about AvidXchange, and why do you want to work here?
Medium · Behavioral and Cultural Fit
Approach
- Build the answer in three parts: what you understand about the business, what in the work attracts you, and how your experience connects. Do your own research on the public product pages and bring two or three concrete facts rather than adjectives.
- Show you understand the domain candidates describe: a SaaS platform automating accounts payable and B2B payments for middle-market companies. Name the flow in plain terms (invoice arrives, gets coded and approved, payment goes out) and say why correctness and security matter when software moves money.
- Tie it to the work the role lists: SQL-heavy backend services in C# and .NET, JavaScript on the front end, and working with product, QA and DevOps. Say which part you want to deepen and give one example of similar work you have done.
- Give a reason that only fits this employer. Reciting the mission, praising size, or saying you like fintech without an example would fit any company and adds nothing.
- Keep it short enough to say in about a minute and a half, and finish with a question about the team so the conversation continues.
Follow-up
- Which part of the accounts payable process do you think is hardest to automate, and why?
- What would you want to learn first on this team?
- Why this role rather than a similar backend role at another SaaS or payments company?
Tell me about a time you had to explain a complex technical concept to a non-technical stakeholder.
Medium · Behavioral and Cultural Fit
Approach
- Pick an explanation where the listener had to make a decision, such as why a nightly batch failed, why a release had to wait, or why a retried request could charge twice. The decision gives the story a point and an outcome.
- Structure it as situation (who, what they needed), translation (an analogy, a diagram or a before-and-after example in their vocabulary), check (you asked them to restate it) and result (what they decided, what risk was avoided).
- Show what you left out on purpose: you led with impact and options with their trade-offs, not the mechanism, and offered the detail only when asked.
- Describe the outcome honestly and, where you have real numbers, include them: tickets avoided, rework prevented, a decision made earlier. Do not invent figures.
- The story fails if you talk down to the stakeholder, if the explanation was still jargon, or if nothing changed afterward.
Follow-up
- How did you know they had understood, rather than just nodded?
- What would you do if they rejected the trade-off you laid out?
- Can you give a second example with a different audience, such as an executive versus a support team?
Describe a project on your resume where you had to overcome a significant technical hurdle.
Medium · Behavioral and Cultural Fit
Approach
- Choose a project you can discuss at code level, since candidates report the final interviews dig into the resume. State the hurdle in one technical sentence: a slow query, a race condition, a migration, a flaky integration.
- Walk through diagnosis in order: what you measured or reproduced (query plan, profiler, logs), the hypotheses you ruled out, and what finally pointed to the cause.
- Present at least two options you weighed, why you picked one, and the trade-off you accepted, for example speed of delivery against a cleaner fix.
- Give the result with a before-and-after measure, then say what you would change today. Separate what you did (use I) from what the team did.
- Avoid hurdles that were only effort or deadlines, and avoid stopping at the fix without saying how you confirmed it held in production.
Follow-up
- Which alternative did you reject, and what would have made you choose it?
- How did you verify the fix held in production?
- If you rebuilt it today, what would be different?
How do you handle a situation where the requirements for a feature are vague or constantly changing?
Medium · Behavioral and Cultural Fit
Approach
- Show a repeatable routine: ask who the user is, what outcome they need, the constraints and what is out of scope, then write your assumptions down and send them to the product manager for confirmation. This is the same habit as asking clarifying questions on a vague API scenario.
- Build the smallest slice that tests the riskiest assumption and show it early, so wrong guesses are caught while they are cheap.
- Design for change: narrow interfaces, feature flags, additive schema changes, and no hard-coding of the part most likely to move.
- When requirements shift mid-build, make the cost visible (what moves, what slips), agree the new priority with the product manager, and keep a short change log so the decision trail is clear.
- The story fails if you built silently from guesses, or if you stalled waiting for a perfect specification.
Follow-up
- What do you do when two stakeholders disagree about what the feature should do?
- Tell me about an assumption that turned out wrong and how you recovered.
- How do you keep tests and estimates stable while requirements move?