cswork / libraryOpen practice workspace

Interview guides · Ascent AI Technologies

Ascent AI Technologies Software Engineer Interview Guide

Ascent AI Technologies Software Engineer interview guide: 2 reported rounds, inventory diff, JSON parsing and system design questions, plus a 7-day plan.

The 60-second brief

Candidate reports say little about a product line. What they do describe is the work a Software Engineer is pointed at: warehouse inventory management systems, logistical problems, complex data flows, performance in constrained environments, and algorithmic challenges. The reported topics (system design, JSON parsing, C++, hash maps and a warehouse inventory comparison) fit that description, so the guide treats inventory data and its movement as the running example for both the coding and the design questions.

This guide is for a Software Engineer candidate preparing for Ascent AI Technologies. Candidates describe the role as end-to-end feature development with product managers and other teams, production code, code reviews, architecture sessions and ownership of specific modules. The guide covers the two reported stages, all ten reported questions with the technique a strong answer uses, the behavioral side of the loop, and a seven-day plan. It does not cover other roles at the company.

Candidates report two stages over roughly 2-4 weeks: a Recruiter Screen, then Technical Deep Dives on system architecture and coding. The sequence is reported to shift a little with the team.

Focus areas

  • System Design
  • JSON Parsing
  • C++ Programming

Overview

Candidates describe a short loop. A recruiter first checks your interest and background. After that come several technical sessions focused on system architecture and coding. Ten questions are reported. Five are filed as design: a Snake or Fruit Snake game, real-time inventory tracking, choosing data structures for a scalable architecture, high availability with low latency, and finding bottlenecks in a distributed design. The rest are coding-style: comparing two warehouse inventory JSON objects for changes, additions and removals, speeding up lookups over large in-memory datasets, stating time and space complexity, handling JSON edge cases, and keeping code maintainable in a time-constrained session. The JSON edge-case question is phrased as a behavioral prompt, so it works best as a short story with technical content.

The topics candidates list are system design, JSON parsing, C++, hash map data structures and the warehouse inventory comparison. Less common areas they mention are concurrent state changes in an inventory system, failure recovery in distributed setups, custom data structures for game or inventory logic, and memory footprint in constrained runtimes. That mix tells you where to spend time: be fluent with a map or dictionary in your chosen language, be able to write a JSON diff from nothing, and have a repeatable method for sketching an inventory service.

Treat inventory as one theme seen from two sides. The coding question compares two snapshots in memory; the design question keeps stock levels correct as updates arrive continuously. Practise them as a pair. The snapshot diff is a batch job with an O(n + m) hash-map solution. The live system needs a write path, a read path, a way to resolve two updates to the same item, and a recovery story. Moving fluently between the two shows you understand the data, not only the algorithm.

Candidates also report being pushed to defend choices and to rework a design when conditions change, for example when data volume grows. Rehearse that aloud. For every answer, prepare the one-sentence reason for each choice and the first thing you would change if the volume tripled or a dependency failed. Explaining complexity and trade-offs clearly is part of the preparation, not an add-on.

The Ascent AI Technologies interview process

Candidates describe two stages. The second is a series of technical sessions, and the order can vary slightly with the team.

Recruiter Screen

Candidates describe the Recruiter Screen as a first conversation in which a recruiter checks your interest and your background. Nothing reported suggests technical problems here, so treat it as a chance to present a clear story and to find out how the rest of the loop will run. Come with a short, spoken walk through your experience that leans on the work this role is described around: data flow, performance, inventory or logistics-style systems, and production code.

What to demonstrate

  • Interest in the role and the company, as the reported stage description states
  • Fit between your background and the Software Engineer role
  • How clearly you can summarise your own experience

How to prepare

  • Write and rehearse a spoken walk through your CV that puts forward one project involving data flow or performance work, and one where you owned a module.
  • Prepare a specific reason for applying that refers to the reported work areas, such as inventory systems or algorithmic problems, instead of general praise.
  • Make a list of questions for the recruiter: which language the coding sessions use, whether design and coding are separate sessions, and who conducts them.
  • Check that you can name the language you will code in (the requirements candidates describe mention C++, Java or Python) and say how long you have used it.

Technical Deep Dives

Candidates describe Technical Deep Dives as a series of sessions led by senior engineers or leadership, focused on system architecture and coding challenges. The reported question types are design (a game, real-time inventory tracking, data structure choice, availability and latency, bottlenecks) and coding (inventory JSON comparison, lookup optimization, complexity analysis, JSON edge cases, code quality under time pressure). Prepare both halves. For design, work through requirements, APIs, data flow and storage trade-offs. For coding, write correct code with a hash map at its centre and state its complexity. Candidates also report follow-up challenges that ask you to defend a decision or iterate on a design.

What to demonstrate

  • System architecture: gathering requirements, defining API boundaries, following data from input to storage and retrieval
  • Coding with maps and JSON, including correctness, Big-O analysis and handling of malformed input
  • Explaining trade-offs behind a choice of structure, database or pattern
  • Reworking an answer when a follow-up changes the constraints

How to prepare

  • Write the warehouse inventory diff from scratch in your chosen language and state its time and space cost, then answer aloud how you would speed up lookups over a very large dataset.
  • Sketch a real-time inventory tracking design on one page: requirements, API list, write and read paths, storage choice with a stated trade-off, and how two updates to the same item are resolved.
  • Prepare a spoken answer for the Snake or Fruit Snake design, naming the board, snake body and food-placement structures and asking what the fruit does before you assume.
  • Practise the volume-tripled and dependency-down follow-ups on each of your designs, and be ready to change one component and say what it costs.

Where candidates lose points

Comparing the two inventory snapshots with nested loops or repeated list scans, and jumping to code without asking what identifies an item

Ask what the key is (SKU or id), what counts as changed (any field or only quantity), and whether items can repeat. Index one snapshot in a map, make one pass over the other, report added, removed and changed, and state O(n + m) time with O(n) extra space.

Treating a missing field, a null and a zero as the same value when parsing JSON

List the three cases before you code and say what each means for the data. In an inventory diff, a missing SKU is a removal, a null quantity is bad data, and zero is a valid stock level. Write a test for each case.

Listing design components (a queue, a cache, a database) without requirements, API boundaries or a write path versus a read path for the inventory or game design

Open with scope and scale questions, write the API calls, then trace data from input to storage and retrieval. Add components only where a stated requirement needs them and say what each one costs.

Naming a database or pattern without a trade-off, and ignoring concurrent updates to the same item

For each storage choice, say what you gain and what query or consistency you give up. For inventory, state how two simultaneous updates to one SKU are resolved, for example with a version check or a serialised per-item log.

Freezing or restarting when a follow-up changes the constraints, such as tripled volume or a dependency outage

Keep your design modular so one component can change. Practise saying what breaks first, which single change you would make, and what that change costs, while continuing to explain your reasoning aloud.

Questions & practice

Given two JSON objects representing warehouse inventory, how would you identify changes, additions, and remova

Medium · Coding and Problem Solving

Given two JSON objects representing warehouse inventory, how would you identify changes, additions, and removals efficiently?

Approach

  1. Clarify the shape: an object keyed by SKU, or an array of item records with an id field? Do nested fields such as location or lot need comparing, and is the output a summary or a field-level change list?
  2. If the input is an array, index each side into a hash map keyed by SKU (for example std::unordered_map<std::string, json>). Keys only in the new map are additions, keys only in the old map are removals, and keys in both whose values differ are changes. That is O(n + m) average time and O(n + m) space, against O(n x m) for the nested-loop version.
  3. For changes, diff recursively and record a path plus old and new values (for example sku -> quantity: 5 -> 3), so nested objects produce precise output instead of a whole-record replacement.
  4. Settle the edge cases explicitly: duplicate SKUs within one file (reject or aggregate), null versus a missing field, type mismatches such as "5" versus 5, float quantities that need a tolerance, and arrays inside an item where order may or may not matter.
  5. Know the library shortcut and its limits: nlohmann::json::diff returns an RFC 6902 JSON Patch, but it compares arrays by position, so a reordered item list shows up as many changes. Keying by SKU first avoids that.

Follow-up

  • Both files are tens of gigabytes: how do you diff them without loading them into memory?
  • How would you detect a SKU that was renamed rather than removed and re-added?
  • How do you make the output stable and easy to test, for example with a sorted order?

How do you optimize lookup times when processing large datasets in memory?

Medium · Coding and Problem Solving

Approach

  1. Start from the lookup pattern: exact key, range, prefix, or many lookups against data that never changes. If a loop scans a list for every query, building an index once (O(n)) turns k queries from O(k x n) into O(n + k) on average.
  2. Hash maps give O(1) average lookups. In C++, call reserve() up front to avoid repeated rehashing, choose a good hash for composite keys, and remember the worst case is O(n) per lookup when keys collide.
  3. For static data, a sorted std::vector with binary search (O(log n)) often beats std::unordered_map in practice: contiguous memory, better cache locality and no per-node allocation. Open-addressing hash tables are another cache-friendly option.
  4. Cut memory and copy costs: store compact records, intern or hash long string keys, and use std::string_view keys only when the owning strings outlive the map. For membership checks on huge sets, a Bloom filter can screen out most misses cheaply.
  5. Profile before and after. State the trade-off: every extra index speeds up reads but costs memory and has to be updated on every write.

Follow-up

  • An attacker controls the keys: what is hash flooding, and how do you defend against it?
  • The data is updated by one thread while several threads read it: how do you keep lookups fast and safe?
  • The data grows past available RAM: what do you change?

Explain the time and space complexity of your proposed solution for a given data processing task.

Medium · Coding and Problem Solving

Approach

  1. Name the variables before giving a bound: n records, m records in a second input, k average key length, d nesting depth. Then cost each step of the solution separately and add them up.
  2. Separate average from worst case and say why: hash-map inserts and lookups are O(1) on average but O(n) in the worst case, so building an index over n items is O(n) average. Sorting is O(n log n) worst case for std::sort.
  3. Count the hidden costs: hashing or comparing a string key is O(k), parsing JSON is linear in the input size, recursion over nested JSON uses O(d) stack, and vector push_back is amortized O(1) with an occasional O(n) reallocation.
  4. Report space as auxiliary versus input versus output: a diff that builds two maps uses O(n + m) extra space, and the output can itself be as large as the input, so say whether the bound is output-sensitive.
  5. Close by comparing with the alternative you rejected (for example nested loops at O(n x m) time and O(1) extra space) and say which constraint, such as input size or memory, made you choose this one.

Follow-up

  • Can you do it with O(1) extra space, and what does that cost in time?
  • Your keys are long strings: does your bound still hold as stated?
  • Which term dominates for realistic input sizes, and how would you check?

What strategies do you use to ensure code quality and maintainability during a time-constrained coding session

Medium · Coding and Problem Solving

What strategies do you use to ensure code quality and maintainability during a time-constrained coding session?

Approach

  1. Spend the first minutes on the contract: write the function signature, the input and output types, and two or three examples, including an edge case, before writing any logic.
  2. Write a short outline as comments, then fill it in with small, well-named helpers (parseItems, indexBySku, diffRecords) so the main function reads like the outline and each piece can be checked on its own.
  3. Handle edge cases with guard clauses at the top (empty input, missing keys, duplicates), use the standard library instead of hand-rolled structures, and pass large objects by const reference in C++ to avoid accidental copies.
  4. Get a correct simple version working first, state its complexity, then optimize the part that matters. Keep names meaningful and avoid magic numbers, even under time pressure.
  5. Test before saying you are done: trace one example by hand, then run the edge cases. If time runs out, list the tests and refactors you would add next.

Follow-up

  • Which unit tests would you write first for this function, and why those?
  • Where did you knowingly cut a corner, and what would it cost in production?
  • With another hour, what would you change in this code?

Explain why the owner filter ignores the listing index

Medium · composite index · query plans · sort elimination

The only index on resource is (tenant_id, status, updated_at DESC, resource_id DESC). A new endpoint returns one user's resources across all statuses, newest created first: WHERE tenant_id = $1 AND owner_user_id = $2 ORDER BY created_at DESC LIMIT 20. On a tenant with 2M rows it takes 900 ms and EXPLAIN shows a sort above a large scan. Explain precisely why the existing index cannot serve it, give the index that can, and state which of these the new index still will not help: owner_user_id alone across tenants; the same query ordered by updated_at. PostgreSQL 16.

Approach

  1. Separate the two jobs an index does. For filtering, a composite btree is seekable only on a left prefix, so with no predicate on status the scan can at best range over tenant_id and test owner_user_id per row; PostgreSQL 16 has no btree skip scan to jump the unconstrained column.
  2. For ordering, the index is sorted by (status, updated_at) within a tenant and not by created_at, so the LIMIT cannot stop early: every matching row is read and then sorted. That is the 'Sort Method: top-N heapsort' line, and it is why the plan reads 2M rows to answer with 20.
  3. Derive the replacement from the access path — equality, equality, then the ordering column: CREATE INDEX CONCURRENTLY ON resource (tenant_id, owner_user_id, created_at DESC). The scan seeks to the (tenant, owner) range and walks 20 entries in order, so the Sort node disappears along with the row-read.
  4. Treat INCLUDE (title, status) as conditional, not free. An index-only scan still visits the heap for any row whose page is not marked all-visible, so on a table taking 1.2k writes/second the win depends on autovacuum keeping the visibility map current, and the wider index costs more on every insert.

Follow-up

  • 90% of rows are status='active'. Would a partial index WHERE status = 'active' change your answer, and for which of the three queries?
  • A dashboard runs this for 40 owners in one page load. What changes about the design?

Write the update path that detects a concurrent edit

Medium · optimistic concurrency · transactional outbox · audit history

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

Approach

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

Follow-up

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

How would you design a "Snake" or "Fruit Snake" game?

Medium · System Design and Architecture

Approach

  1. Pin the rules first: grid size, whether walls kill or wrap, one fruit or several, growth per fruit, and whether this is a single-player local game or an online one. The design changes a lot between those.
  2. Model the snake as a deque of cells (head at the front) plus a hash set of occupied cells. Each tick pushes the new head and pops the tail unless the snake just ate, so a move and a collision check are both O(1).
  3. Order the tick carefully: compute the new head from the direction, remove the tail first (unless growing), then check walls and the occupied set. Otherwise moving into the cell the tail is leaving looks like a false self-collision.
  4. Spawn fruit uniformly over empty cells. Rejection sampling slows down as the board fills, so keep a vector of free cells plus a cell-to-index map and remove with swap-and-pop, which makes both spawning and updates O(1).
  5. Separate model, input and rendering: a Game class owns Board, Snake and a FruitSpawner, input is buffered per tick, and a 180-degree reversal is ignored. Inject the RNG seed and the clock so the game logic can be unit-tested deterministically.

Follow-up

  • How would you add several fruit types with different scores or effects without touching the core tick?
  • How does the design change for a two-player online version: who is authoritative for collisions and fruit spawns?
  • What breaks in your free-cell bookkeeping when the snake fills almost the whole board?

Describe your approach to designing a system for real-time inventory tracking.

Medium · System Design and Architecture

Approach

  1. Scope the data model: stock keyed by (SKU, location) with on-hand and reserved quantities, and the event types that change it (receive, pick, ship, transfer, adjust after a cycle count). Ask which reads need to be exact and which can lag.
  2. Record every change in an append-only ledger and maintain a current-quantity table from it. The ledger gives you audit and replay, and the table serves fast reads.
  3. Prevent overselling with an atomic conditional write, for example UPDATE stock SET qty = qty - n WHERE sku = ? AND loc = ? AND qty >= n, and treat zero affected rows as a conflict, not a success. Optimistic version columns work too.
  4. Make writes idempotent: scanners and services retry after timeouts, so each event carries a unique id that is stored with a unique constraint, which turns a duplicate into a no-op instead of a double decrement.
  5. For real-time fan-out, publish committed changes (outbox or change data capture) to a stream partitioned by SKU and location so per-item order holds, then update caches and push to dashboards. Dashboards may be slightly stale, but the reservation path always checks the authoritative row.

Follow-up

  • Two orders try to reserve the last unit at the same moment: walk through exactly what each one sees.
  • A handheld scanner works offline for an hour and then syncs: how do you merge its events?
  • One SKU becomes a flash-sale hot key: what do you change in the write path?

How do you determine the appropriate data structures for a complex, scalable architecture?

Medium · System Design and Architecture

Approach

  1. Start from the access patterns, not the structure: list the operations (point lookup, range query, ordered iteration, top-k, prefix match, membership) with their rough frequency and the read/write ratio.
  2. Map each pattern to a structure and its cost: a hash map for key lookup (O(1) average, O(n) worst case under collisions), a balanced tree or B-tree for ordered and range queries (O(log n)), a heap for top-k or scheduling (O(log n) push/pop), a trie for prefix search, and a hash map plus doubly linked list for an LRU cache.
  3. When memory is the constraint, name the probabilistic options and what they give up: a Bloom filter for membership (false positives, never false negatives) and a count-min sketch for approximate frequencies (it can only overcount).
  4. Carry the reasoning into storage and scale: B-tree indexes suit read-heavy point and range access, LSM trees suit write-heavy workloads, and consistent hashing spreads keys across nodes so adding a node moves only a fraction of them.
  5. Tie it to a concrete case, such as inventory lookup by SKU in a hash map plus an ordered index on quantity for low-stock queries, and say what each extra index costs on every write.

Follow-up

  • When does a sorted array with binary search beat a hash map in practice?
  • You need both fast lookup by id and iteration in insertion order: what do you build?
  • The dataset no longer fits on one machine: which of these choices still holds?

What factors do you consider when designing for high availability and low latency?

Medium · System Design and Architecture

Approach

  1. Turn both words into numbers first: an availability target (99.9% allows about 8.8 hours of downtime a year) and a latency target stated as a percentile such as p99, not an average.
  2. For availability, remove single points of failure: several stateless instances behind a load balancer with health checks, spread across zones, data replicated with automatic failover. Say what synchronous replication costs in write latency, and what data asynchronous replication can lose on failover.
  3. For latency, shorten the critical path: cache hot reads (cache-aside with TTLs and an invalidation plan), keep data close to callers, pool connections, and move non-essential work such as notifications onto a queue.
  4. Contain failures so they do not spread: timeouts on every remote call, retries with exponential backoff and jitter only for idempotent operations, circuit breakers, bulkheads, and graceful degradation such as serving cached data when a dependency is down.
  5. Name the trade-off: during a network partition a system either stays available with possibly stale data or rejects requests to stay consistent. Choose per operation, for example a stock display versus a reservation.

Follow-up

  • The cache cluster restarts and every request hits the database: what happens, and how do you prevent it?
  • A service fans out to 20 backends per request: why does its p99 get worse, and what can you do?
  • One availability zone goes down: walk through what fails over and what users notice.

Explain your process for identifying bottlenecks in a distributed system design.

Medium · System Design and Architecture

Approach

  1. Trace one request end to end and list every hop: client, load balancer, service, cache, database, queue, external calls. A bottleneck is a property of a path, not of a box.
  2. Estimate before measuring: peak QPS, payload sizes and per-hop latency. Little's law (concurrency = arrival rate x time in system) tells you how many in-flight requests, threads or connections each hop must hold.
  3. Then measure: per-hop p50 and p99 latency from distributed tracing, and utilization, saturation (queue depth, connection-pool waits) and errors for each resource. A resource near full utilization with a growing queue is the bottleneck.
  4. Check the usual suspects: a single primary taking every write, a hot partition or key, lock contention, N+1 queries, synchronous fan-out, and serialization or GC pauses.
  5. Match the fix to the cause (replicas or caching for read load, sharding or batching for write load, queues with backpressure for bursts), then measure again: once one bottleneck is relieved, the next one shows up.

Follow-up

  • p50 is fine but p99 is ten times worse: where do you look first?
  • One shard is at full CPU while the others are idle: what caused it, and how do you rebalance?
  • A queue's depth keeps growing: how do you tell whether the producer or the consumer is at fault?

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

  1. 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.
  2. 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.
  3. 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.
  4. 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 two reported stages: one day for the recruiter screen, then days on the inventory diff and JSON, the inventory design, the Snake and data structure questions, and availability and bottlenecks, ending with a full mock.

Day 1 — Map the loop and script the recruiter screen

  • Write the reported sequence (Recruiter Screen, then Technical Deep Dives) and note that the order can shift with the team.
  • Write a spoken CV walk-through that puts forward one data-flow or performance project and one module you owned.
  • Write your reason for applying, tied to inventory and logistics-style systems, and a list of questions for the recruiter about language and session format.

Deliverable: A one-page loop map, a written CV walk-through, a reason for applying, and a list of recruiter questions.

Day 2 — Inventory diff and fast lookups with hash maps

  • Write the warehouse inventory comparison in your chosen language, using a map keyed by SKU: one pass to report added, removed and changed items.
  • Write six test cases: both empty, identical, only additions, reordered keys, a changed nested field, and a duplicate SKU.
  • Answer aloud how you would speed up lookups over a large in-memory dataset: hash versus ordered map, pre-sizing, a sorted merge, and memory cost.

Deliverable: A working inventory diff function, a six-case test list, and a spoken answer on lookup optimization.

Day 3 — JSON edge cases and complexity statements

  • List the JSON failure classes (missing, null, empty, type drift, large integers, duplicate keys, deep nesting, truncated input) and write a validator that reports the path of each bad field.
  • State time and space complexity for your day-2 diff, then for a sort-and-merge alternative, and say when you would choose each.
  • Write the JSON edge-case story: situation, failure classes handled, mechanism, result.

Deliverable: A path-reporting validator, written complexity statements for two diff approaches, and a one-paragraph JSON story.

Day 4 — Design real-time inventory tracking

  • Write requirements and an API list (adjust stock, reserve, query), then separate the write path from the read path.
  • Choose a store for current state and for an update log, and write the trade-off you accepted.
  • Work out how two updates to one SKU are resolved, using a version check, and sketch recovery after a failed write.

Deliverable: A one-page inventory design with an API list, storage trade-off, and a concurrency and recovery section.

Day 5 — Snake game and choosing data structures

  • Design Snake: board, snake body as a deque, a set for occupied cells, a direction input queue, a tick loop, food placement from the free cells, and constant-time collision checks.
  • Extend it to fruit variants by asking what each fruit does, then show where that logic lives.
  • Answer aloud how you choose data structures from access patterns, using three examples from the game and the inventory service.

Deliverable: A written Snake design with data structures and complexity, plus a spoken data-structure-selection answer with three examples.

Day 6 — High availability, low latency and bottlenecks

  • Write a checklist separating availability levers (redundancy, failover, retries with limits) from latency levers (caching, fewer hops, pre-computation) and name the tension between them.
  • Write a bottleneck method: measure, find the saturated resource, change one thing, measure again; apply it to your day-4 design at triple the volume.
  • Work through the owner-filter index question as a worked example of diagnosing a slow query.

Deliverable: A two-column availability and latency checklist, a written bottleneck method applied to the inventory design, and a worked index diagnosis.

Day 7 — Full mock and the code-quality routine

  • Run a mock: write the inventory diff cold, then talk through the inventory design aloud, including a follow-up that changes volume.
  • Write your routine for time-constrained coding: clarify inputs, outline the steps, write clear names, add test cases, state complexity.
  • Rehearse the JSON story and one story each on a deadline trade-off and a code review disagreement.

Deliverable: A completed mock, a written code-quality routine, and three rehearsed behavioral stories.

Defending decisions and owning code

The reported loop lists only two stages and neither is a dedicated behavioral round. Candidates do describe the role as one with code reviews, module ownership and work with product managers, along with soft skills such as clear communication and constructive debate, and they mention structuring behavioral answers with STAR. Rehearse short stories about owning code, defending a design and disagreeing well, and keep each one tied to a technical decision you made.

  1. What strategies do you use to ensure code quality and maintainability during a time-constrained coding session?
  2. Tell me about a design you defended after someone pushed back. What did you change and what did you keep?
  3. Describe a code review disagreement with a colleague and how it was settled.
  4. Tell me about a module you owned end to end and the technical debt you found in it.
  5. Describe a system you built that had to change when data volume grew.

How do you handle edge cases when parsing complex JSON structures?

Medium · Coding and Problem Solving

Approach

  1. Choose a story where you owned the parsing or ingestion, such as a partner feed, an API payload or an inventory export, and where bad input caused or nearly caused a real problem. Give the situation in two sentences, then spend most of the answer on what you checked and decided.
  2. Name concrete failure classes you now handle: a missing key versus an explicit null versus an empty string or array (they can mean different things, such as a SKU with no quantity versus quantity zero); numbers sent as strings; integers beyond 2^53 losing precision in some parsers; duplicate keys silently overwritten; unexpected extra fields; very deep nesting that overflows a recursive parser; truncated input, trailing commas and encoding or BOM problems.
  3. Describe the mechanism, not the intention: validate at the boundary against a schema or typed struct, report the path of the bad field (for example items[3].quantity), and decide per field whether to reject, coerce with a logged warning, or quarantine the record. Say that you never swallowed an exception into a default value.
  4. Close with the result and what stuck: regression tests built from the real bad payloads, a count of rejected or quarantined records you monitored, and one thing you would do differently. The mistake that sinks this answer is a generic checklist with no story, or claiming a parser that handles every input.

Follow-up

  • How would you parse a JSON file too large to fit in memory?
  • When a record fails validation, do you drop it, repair it or stop the whole batch, and who decides?
  • How do you test the parser against payloads you have not seen yet?

Ship under a deadline and bound the debt you chose

Medium · pagination · technical debt · tradeoffs

You have four days to ship a tenant-facing listing endpoint. The version you would defend uses keyset pagination over (tenant_id, status, updated_at DESC, resource_id DESC); the version you can finish uses LIMIT/OFFSET with no matching index. Describe a deadline call you actually made of this shape: what you shipped, what you knowingly deferred, how you bounded the damage with a mechanism rather than an intention, and the specific numeric condition that would force the follow-up. Name who you told and where you wrote it down.

Approach

  1. Name the deferred failure precisely instead of calling it slow. OFFSET n makes the database produce and discard n rows, so cost grows with page depth; without an index matching the sort, every matching row is read and sorted before the limit applies; and rows inserted between two page fetches shift across the boundary so items are skipped or repeated with nothing in the response to signal it.
  2. Bound the blast radius with something mechanical rather than a promise: cap maximum page depth, cap page size, restrict the endpoint to one internal caller, or keep it behind a flag. State which failure each cap removes and which it leaves standing.
  3. Attach a number to the trigger and wire it to an alarm: the first tenant crossing N resources, or the endpoint's p99 crossing its share of the 400 ms budget, so the debt announces itself instead of waiting to be remembered.
  4. Write it where the next engineer looks, which is the code and the ticket, not a chat message: what was deferred, why, the cap, and the trigger.

Follow-up

  • At what page depth does the offset version breach your latency budget, given your page size and row counts?
  • What breaks first when you switch to keyset pagination later, and what does a client holding an old page token see?

Turn a code review disagreement into a decision

Easy · code review · optimistic concurrency · communication

A colleague's change updates a row with UPDATE resource SET version = version + 1 WHERE resource_id = $1 AND version = $2 and treats an affected-row count of zero as a successful no-op. You read that as a silently lost update; they think returning 200 is friendlier to clients than returning a conflict. Describe how you have handled a review disagreement of this shape: what goes in the comment, when you leave the thread, and who decides. Then write the comment you would leave here, in under 80 words.

Approach

  1. Sort the disagreement before writing anything. A silently discarded write is a correctness claim about data; the choice between 409 and 412 is taste. Only the first justifies blocking a merge, and saying which one you are doing is most of the value of the comment.
  2. Make the claim reproducible in the comment itself with an interleaving rather than a principle: A reads version 7, B reads version 7, B commits version 8, A's predicate matches zero rows, A is told it succeeded and A's edit is gone.
  3. Offer the alternative with its cost attached: return 409 carrying the current version and the revision that won, so the client can re-read and re-apply. Note that automatic retry is not the fix, because a retry re-reads the winner's state and reapplies an intent formed against data that no longer exists.
  4. Apply an escalation rule you can state: two round trips on the thread, then a call, and the service's owner decides rather than the reviewer. A reviewer who cannot be overruled is a bottleneck with extra steps.

Follow-up

  • Where would you put the test that fails if someone reintroduces the swallowed zero rowcount?
  • The author says clients cannot handle a 409. How do you check whether that is true?