Skip to main content

Forward-deployed engineer coding interview questions

11 questions on coding for forward-deployed engineer candidates. Expect prompts such as “Flatten a deeply nested JSON API response” and “Implement async retry with exponential backoff and jitter”, each with a worked answer outline and the follow-ups interviewers push on.

Questions

Showing 1 to 11 of 11 coding questions.

As asked

A third-party API returns deeply nested JSON with inconsistent structure. Write a function that flattens it into a single-level object with dot-notation keys, and handle arrays by appending the index.

Sample answer outline

The function recursively traverses the object; for each value that is a plain object it recurses with the current key as prefix, for arrays it iterates with index as part of the key suffix, for primitives it assigns to the flat output. Edge cases: null values, empty arrays, keys that already contain dots (need escaping or explicit handling). Time complexity O(n) where n is the total number of leaf values.

Reference implementation (javascript)

JavaScript
// Starter: flatten({ a: { b: { c: 1 } }, d: [2, 3] })
// Expected: { 'a.b.c': 1, 'd.0': 2, 'd.1': 3 }
function flatten(obj, prefix = '', result = {}) {
  // your implementation here
  return result;
}

Expect these follow-ups

  • How would you un-flatten this back to the original structure?
  • What if two different paths produce the same flattened key?
jsonrecursiondata-transformationintegrations

As asked

Write an async function that wraps any async operation and retries it up to maxRetries times with exponential backoff and random jitter. It should surface the last error if all retries are exhausted.

Sample answer outline

Attempt the operation in a loop up to maxRetries times. On failure, compute delay as baseDelay * 2^attempt plus a random fraction of the base delay as jitter (e.g., Math.random() * baseDelay). Sleep for that delay, then retry. After the last failure, re-throw. Strong candidates discuss capping the max delay, handling non-retriable errors (4xx vs 5xx), and cancellation via AbortSignal.

Reference implementation (javascript)

JavaScript
async function withRetry(fn, { maxRetries = 3, baseDelayMs = 200 } = {}) {
  // implement here
}

// Usage:
const data = await withRetry(() => fetch('https://api.example.com/data'));

Expect these follow-ups

  • Why does jitter matter specifically when you have many clients all retrying the same failing service at the same time?
asyncretryexponential-backoffresilienceintegrations

As asked

A third-party sends webhooks with an X-Signature-256 header containing an HMAC-SHA256 of the raw request body, signed with a shared secret. Write the code to verify it securely.

Sample answer outline

Compute the expected HMAC using the shared secret and the raw bytes of the request body (before any JSON parsing). Compare using a constant-time comparison function (hmac.compare_digest in Python, crypto.timingSafeEqual in Node.js) to prevent timing attacks. Return 401 if the signatures don't match. Candidates should specifically call out that comparing with == leaks timing information and is insecure.

Reference implementation (python)

Python
import hmac
import hashlib

def verify_webhook(payload_bytes: bytes, signature_header: str, secret: str) -> bool:
    """
    signature_header format: 'sha256=<hex_digest>'
    Returns True if valid, False otherwise.
    """
    # implement here
    pass

Expect these follow-ups

  • What happens if you verify the signature after parsing the body as JSON and re-serializing? Why might that fail?
securityhmacwebhookscryptographyauthentication

As asked

A third-party API returns JSON with a 'data' array and a 'next_cursor' field that is null on the last page. Write a function that fetches all pages and returns the concatenated list of all records.

Sample answer outline

Loop: make a GET request, extend the results list with the data field, check if next_cursor is null to exit. Pass next_cursor as a query param in the next request. Candidates should handle errors per request (don't silently swallow), add a safety max_pages limit to avoid infinite loops from a buggy API, and respect rate limits between pages (optional sleep or backoff).

Reference implementation (python)

Python
import requests

def fetch_all(base_url: str, api_key: str) -> list[dict]:
    """
    Fetches all pages from the API and returns all records.
    Response shape: {"data": [...], "next_cursor": str | null}
    """
    results = []
    cursor = None
    # implement here
    return results

Expect these follow-ups

  • What if the API has no cursor and uses page number plus a 'has_more' boolean? How does the code change?
paginationapi-clientpythonintegrationsdata-collection

As asked

Implement a function that returns true if a request should be allowed, and false if it should be rejected. The limit is N requests per window of W seconds using a sliding window algorithm. No external libraries.

Sample answer outline

Use a queue of timestamps. On each call, remove timestamps older than W seconds from the front of the queue, then check if the queue length is less than N. If yes, push the current timestamp and return true. Otherwise return false. Time complexity O(N) in worst case per call for the cleanup step. A better approach for high N uses a deque and binary search. Candidates should also note thread-safety concerns if this were concurrent.

Reference implementation (javascript)

JavaScript
class SlidingWindowRateLimiter {
  constructor(limit, windowSeconds) {
    this.limit = limit;
    this.window = windowSeconds * 1000;
    this.timestamps = [];
  }

  isAllowed(now = Date.now()) {
    // implement here
  }
}

Expect these follow-ups

  • How would you implement this in Redis for a distributed system with multiple servers?
rate-limitingsliding-windowdata-structuresalgorithms

As asked

Write a Python generator function that reads a CSV file line by line, skips malformed rows (logging them), and yields each valid row as a dict. The file may be 5 GB and must not be loaded fully into memory.

Sample answer outline

Use csv.DictReader in a with open(...) block, which reads line by line. Wrap the yield in a try/except to catch rows that cause parsing errors, log the row number and error, and continue. Generator semantics mean the caller controls iteration speed. Strong candidates also address encoding (utf-8 with errors='replace'), BOM handling, and configurable delimiter.

Reference implementation (python)

Python
import csv
import logging

def stream_csv(filepath: str):
    """Yield valid rows as dicts, log malformed rows."""
    # implement here
    pass

# Usage:
for row in stream_csv('big_file.csv'):
    process(row)

Expect these follow-ups

  • How would you parallelize processing across 4 CPU cores if each row's processing is CPU-bound?
pythoncsvgeneratorsstreamingdata-processing

As asked

You are writing a post-sync reconciliation script. Given a list of dicts from a third-party API, each with keys 'customer_id', 'amount', and 'date', write a function that returns per-customer totals and the date of their most recent record, sorted by total descending, so you can surface the highest-value customers to the account team. No pandas.

Sample answer outline

Use a defaultdict(dict) keyed by customer_id, accumulating total and tracking max date as an ISO string comparison. Then sort the resulting list by total descending with sorted(..., key=lambda x: -x['total']). Strong candidates handle edge cases: empty input, None amounts, invalid date strings, and ties in the sort.

Reference implementation (python)

Python
from collections import defaultdict

def summarize(transactions: list[dict]) -> list[dict]:
    """
    Returns: [{'customer_id': ..., 'total': ..., 'last_date': ...}, ...]
    sorted by total descending.
    """
    # implement here
    pass

Expect these follow-ups

  • If the list had 50 million rows and couldn't fit in memory, how would you approach this?
pythonaggregationdata-processingscripting

As asked

You have 500 customer IDs and need to call GET /api/customers/:id for each one. The API caps you at 10 concurrent connections. Write code that fetches all 500 with at most 10 in flight at any time.

Sample answer outline

Use a semaphore or a bounded queue. In JavaScript, a simple approach is p-limit or a manual Promise pool that keeps a running set of at-most-10 in-flight promises. In Python, asyncio.Semaphore with async with semaphore inside each coroutine, gathered with asyncio.gather. Candidates should also discuss error handling (one failure should not cancel all others, so catch per-request) and collecting results in order.

Reference implementation (python)

Python
import asyncio
import aiohttp

async def fetch_all_customers(ids: list[int], max_concurrent: int = 10) -> list[dict]:
    """
    Fetch each customer by ID with at most max_concurrent requests in flight.
    Return results in the same order as ids.
    """
    # implement here
    pass

Expect these follow-ups

  • How do you handle the case where 50 of the 500 requests return 429 rate-limited? Do you want to stop the whole batch or just those requests?
concurrencyasyncapi-clientpythonintegrations

As asked

You poll a customer's REST API every 5 minutes and store the response. Write a function that compares two response dicts and returns a list of changes, each describing the field path, the old value, and the new value.

Sample answer outline

Recursively traverse both dicts simultaneously. For each key, track the dot-notation path. If a key is in one but not the other, record an 'added' or 'removed' change. If both are dicts, recurse. If both are lists, compare element-by-element by index (note: position-based comparison may not be semantically correct for unordered lists). If the value differs and is a primitive, record a 'changed' entry with old/new values.

Reference implementation (python)

Python
def diff_json(old: dict, new: dict, path: str = '') -> list[dict]:
    """
    Returns list of {path, old_value, new_value, change_type: 'changed'|'added'|'removed'}
    """
    changes = []
    # implement here
    return changes

Expect these follow-ups

  • How would you handle arrays where items can be reordered? For example, a list of product variants that gets shuffled.
jsonrecursiondata-comparisonintegrationschange-detection

As asked

Write a SQL query that, for each customer, returns their total order value in the current calendar year and their rank within their industry segment, with ties ranked the same and no gaps in ranking.

Sample answer outline

Use a CTE or subquery to sum order values per customer filtered to the current year, then apply DENSE_RANK() OVER (PARTITION BY industry_segment ORDER BY total_value DESC). Strong candidates distinguish RANK (gaps after ties), DENSE_RANK (no gaps), and ROW_NUMBER (arbitrary tiebreak). They should also correctly filter the year using DATE_TRUNC or EXTRACT depending on the database.

Reference implementation (sql)

SQL
-- Tables:
-- customers(id, name, industry_segment)
-- orders(id, customer_id, total_amount, created_at)

SELECT
  -- write your query here
FROM customers c
JOIN orders o ON o.customer_id = c.id;

Expect these follow-ups

  • How does this query perform on a table with 100 million orders? What indexes would help?
sqlwindow-functionsanalyticspostgresreporting

As asked

You have 10,000 dicts from an API sync that need to be upserted into a Postgres table. Using psycopg2 or asyncpg, write code that does this in one or a few queries rather than 10,000 individual statements.

Sample answer outline

Use execute_values (psycopg2) or executemany with the multi-row INSERT syntax, then ON CONFLICT (unique_key) DO UPDATE. Alternatively, COPY the rows into a temp table and then INSERT ... SELECT ... ON CONFLICT from the temp table, which is fastest for very large batches. Strong candidates mention chunking into 1,000-row batches to limit transaction size and memory, and the importance of a unique index on the conflict column.

Reference implementation (python)

Python
import psycopg2
from psycopg2.extras import execute_values

def bulk_upsert(conn, records: list[dict]):
    """
    Records have keys: external_id, name, updated_at
    Conflict on external_id; update name and updated_at.
    """
    # implement here
    pass

Expect these follow-ups

  • What is the difference in performance between execute_values and a single INSERT with many value tuples?
postgrespythonbulk-insertupsertperformance