cswork / libraryOpen practice workspace

Interview guides · A+E Group

A+E Group Software Engineer Interview Guide

A+E Group Software Engineer interview guide: the reported three-stage loop, coding, system design and behavioral questions, and a 7-day prep plan.

The 60-second brief

Candidates describe A+E Group as an architecture and engineering setting where software engineers build for civil, mechanical and architectural designers. The reported work is internal tooling: custom plugins, scripts that automate repetitive design tasks, integrations between engineering platforms, and code that handles large design datasets. Candidates also mention dashboards and quality-assurance scripts for design output, and describe the work as reducing manual tasks for designers.

This guide is for Software Engineer candidates preparing for A+E Group. It covers the three reported stages, the fifteen reported questions (five coding, five system design, five behavioral) and a seven-day plan. The requirements candidates describe list C#, C++ or Python, design patterns, Git and SQL, with 3+ years of experience typical and AEC or CAD/BIM exposure (Revit, AutoCAD, Civil 3D) as a bonus rather than a requirement.

Candidates report three stages over roughly three to five weeks: a Recruiter Screen, a Technical Assessment built on coding exercises, and Panel Interviews with software engineering leaders and stakeholders from design teams. Stages can vary by team and seniority.

Focus areas

  • Software Engineering
  • Requirements Analysis
  • Design Thinking

Overview

The reported work sits between software and physical design. Candidates describe plugins, automation scripts, integrations and tooling for designers, with large design datasets and engineering file formats as the raw material. That shapes the coding questions: the reported set pairs classic exercises (reverse a linked list, find the top three error codes in a log) with ones that borrow the domain (overlapping 2D bounding boxes, flattening a nested parts hierarchy, searching spatial coordinates). Candidates also report scenarios such as routing a pipe along the shortest path through a 3D grid while avoiding exclusion zones, and speeding up a script that takes ten minutes to batch-process mechanical design files. Practise the classics until they are automatic, then practise saying how the same code behaves on a very large input.

The system design questions are mostly about internal platforms: uploading and validating large design files from remote engineers, a REST API feeding a project dashboard, a schema for parts, suppliers and project usage, availability for a tool designers use daily, and moving legacy project data from an on-premise server to a cloud database. Candidates also report scenario prompts such as syncing a civil engineering modelling tool with a project dashboard and logging and recovering errors in a distributed internal tool. Prepare to cover data flow, failure handling and data integrity for each of them.

The behavioral set is about working with people who are not software engineers: explaining a technical issue to a non-technical stakeholder, building a tool for a team's workflow problem, handling shifting requirements, disagreeing with a senior engineer, and a failed project. Because the panel is reported to include stakeholders from design teams, prepare stories where the user was a designer or another domain expert, and be ready to state the operational effect of your code, not just the technology.

The A+E Group interview process

Candidates report three stages over about three to five weeks, in the order below. Specific stages may differ by team and seniority.

Recruiter Screen

Candidates describe the first stage as a conversation with a recruiter to assess fit for the role. Treat it as the place to line up your background with the requirements candidates describe: C#, C++ or Python, Git, SQL, design patterns, and any exposure to AEC tools such as Revit, AutoCAD or Civil 3D. A concise account of what you built, for whom and with what result sets up the later stages, and it is the right moment to ask which language and stack the team uses and what the technical stage looks like.

What to demonstrate

  • Fit for the role, as candidates describe the screen
  • How your background lines up with the role's listed requirements (C#, C++ or Python, Git, SQL, design patterns)

How to prepare

  • Write a two-minute walk through your career that names the language (C#, C++ or Python), SQL and Git work you have done, and the user each project served.
  • Prepare one example of software you built for non-software users, since the role is described as working with designers and engineers.
  • List questions to ask the recruiter: which stack the team uses, how the technical stage is run, and whether the team is remote, in the office or mixed.

Technical Assessment

Candidates describe this stage as an evaluation of baseline technical competence through coding exercises. The reported coding questions are good practice material for it: overlapping 2D bounding boxes, a script that returns the top three error codes from a large log, flattening a nested JSON parts hierarchy, optimising a coordinate search over a large spatial array, and reversing a linked list with its complexity. Practise in the language you will use, write runnable code, and say complexity out loud for every solution.

What to demonstrate

  • Working, readable code for realistic data-handling problems
  • Choice of data structure and awareness of time and memory cost on large inputs

How to prepare

  • Write the bounding-box overlap, the log top-three counter and the iterative JSON flatten from a blank file, with a small test table for each.
  • For the spatial search, outline a ladder of approaches: linear scan, sort plus binary search, grid or hash bucketing, then k-d tree or quadtree, and say when each pays off.
  • Practise string handling, file parsing and basic object-oriented design in your chosen language.
  • Build the habit of covering edge cases before you call a solution done: empty input, malformed log lines, touching or zero-area boxes, and very deep nesting.

Panel Interviews

Candidates describe the final stage as panel interviews with software engineering leaders and stakeholders from design teams. The mix of audiences suggests preparing two registers: technical depth for engineers and plain-language explanation for designers. The reported system design questions (design file upload and validation, a metadata REST API, a parts and suppliers schema, availability, legacy migration) and the behavioral questions are the practice material for this kind of stage.

What to demonstrate

  • System design reasoning: components, data flow, failure handling and integrity
  • Ability to explain technical decisions to people from a different engineering discipline
  • Collaboration experience with domain experts, conflict and changing requirements

How to prepare

  • Sketch a design for each of the five reported design questions on one page: components, data model, failure paths and what you would build first.
  • Rehearse explaining one of those designs twice, once to an engineer and once to a mechanical designer, without shared jargon.
  • Prepare five stories covering the reported behavioral prompts, each with a measurable result and a named non-software user.

Where candidates lose points

Solving the bounding-box overlap for the obvious case only

State the convention first (axis-aligned, min and max corners) and compare each axis separately: two boxes overlap when both axis intervals overlap. Decide aloud whether touching edges count, and test containment, identical boxes, zero-width boxes and swapped corners.

Loading the whole error log into memory or sorting every count to find the top three

Stream the file line by line, count codes in a dictionary, and select the top three with a size-three heap or a partial selection. Say what you do with malformed lines and with ties.

Flattening a nested parts hierarchy with plain recursion and no rules for keys, arrays or depth

Decide the key format (path joined with a separator), how list items are indexed, what empty objects produce and how key collisions are handled. Use an explicit stack if depth can be large, and mention cycles if the data is not guaranteed to be a tree.

Answering the design-file upload or legacy migration question with one happy-path pipeline

Cover resumable or chunked upload, validation before a file is marked usable, concurrent uploads of the same file, versioning, and for the migration a dry run, row-count and checksum comparison, and a rollback plan.

Telling behavioral stories with no non-software user and no result

For each story name the user (ideally a designer or other domain expert), the problem in their terms, what you built or decided and a measurable outcome, then keep technical detail to what the listener needs.

Questions & practice

Write a program to detect if two 2D bounding boxes overlap.

Medium · Coding and Algorithmic Problem Solving

Approach

  1. I pin the representation first: axis-aligned boxes as (xmin, ymin, xmax, ymax). If the caller passes two arbitrary corners, I normalise them with min/max before comparing anything.
  2. Two boxes overlap unless one lies entirely to one side of the other, so the test is a.xmin < b.xmax and b.xmin < a.xmax and a.ymin < b.ymax and b.ymin < a.ymax. That is O(1) time and O(1) space.
  3. I ask whether boxes that only touch count as overlapping. That choice decides whether I use < or <=. Zero-area boxes (a point or a line) need the same decision.
  4. With floating-point coordinates I compare against a small epsilon tolerance so that shared edges produced by rounding do not flip the result.
  5. To get the overlap area as well, I compute max(0, min(a.xmax, b.xmax) - max(a.xmin, b.xmin)) and the same in y, then multiply. A positive product means the boxes overlap.

Follow-up

  • How would you find every overlapping pair among n boxes faster than O(n^2)? (Sweep and prune sorted on xmin, a uniform grid, or an R-tree.)
  • What changes if the boxes can be rotated? (Use the separating axis theorem on the edge normals of both rectangles.)
  • How would you extend the check to 3D bounding boxes?

Given a large log file of system errors, write a script to parse the file and return the top three most freque

Medium · Coding and Algorithmic Problem Solving

Given a large log file of system errors, write a script to parse the file and return the top three most frequent error codes.

Approach

  1. I stream the file line by line rather than loading it, so memory depends on the number of distinct codes and not on file size. I extract each code with one precompiled regex and count malformed lines separately instead of crashing.
  2. I count with a hash map (collections.Counter in Python), then take the top three with a size-3 heap (heapq.nlargest or Counter.most_common(3)). That is O(N) over the lines plus O(U log 3) over U distinct codes, versus O(U log U) for a full sort.
  3. In C# the same plan is File.ReadLines (which streams lazily) into a Dictionary<string,int>, then either a PriorityQueue<string,int> capped at three entries or LINQ OrderByDescending(kv => kv.Value).ThenBy(kv => kv.Key).Take(3).
  4. I settle the edge cases out loud: fewer than three distinct codes, ties at third place (I break them on the code itself so the output is deterministic), mixed case or padded codes, and bad bytes (open with errors='replace').
  5. For a very large file I split it at newline-aligned byte offsets, count each chunk in a separate process and sum the counters. The merge is exact because counts add.

Follow-up

  • The logs are gzipped and rotated across many files on several servers. How does the script change?
  • How would you report the top three over a sliding last-hour window instead of the whole file?
  • What if the distinct codes do not fit in memory? (Hash-partition to disk and count each partition, or use an approximate Count-Min Sketch plus a heap.)

Implement a function to flatten a deeply nested JSON object representing a mechanical parts hierarchy.

Medium · Coding and Algorithmic Problem Solving

Approach

  1. I agree on the output format first: a flat dict of path to leaf value, with dict keys joined by a separator ('assembly.frame.bolt') and list items written as an index ('parts[2].qty').
  2. The core is a depth-first walk. A dict extends the path with each key, a list extends it with each index, and a scalar or null is written to the output. Time is linear in the number of nodes, but the output strings can total O(nodes x depth) characters.
  3. Because the hierarchy can be deeply nested, I use an explicit stack instead of recursion; CPython's default recursion limit is 1000 frames. In C# I parse with System.Text.Json into a JsonNode and walk it with a Stack<(string Path, JsonNode Node)>, branching on JsonObject, JsonArray and JsonValue.
  4. I handle the edge cases: empty dicts or lists (keep them as {} or [] leaves rather than silently dropping them), keys that already contain the separator (escape them or emit tuple paths), and collisions between a literal 'a.b' key and a nested a then b.
  5. For a parts hierarchy I mention an alternative output: rows of (part_id, parent_id, depth, path), which load straight into a table and can be queried as a tree.

Follow-up

  • Write the inverse function that unflattens the paths back into the nested object.
  • The file is several gigabytes. How would you flatten it without loading it all? (A streaming JSON parser that emits paths as it reads.)
  • The same subassembly appears under many parents and is referenced by id. How does the output change?

How would you optimize a search algorithm looking for a specific coordinate within a massive array of spatial

Medium · Coding and Algorithmic Problem Solving

How would you optimize a search algorithm looking for a specific coordinate within a massive array of spatial data?

Approach

  1. I clarify the query first: an exact coordinate match, the nearest point, or everything inside a range. I also ask whether the data is static or updated, whether it is 2D or 3D, and how many queries run against one dataset.
  2. For a single query, a linear scan at O(n) is already optimal and building an index does not pay off. I make the scan fast with contiguous numeric arrays and vectorised comparisons, and memory-map the data if it is larger than RAM.
  3. For many exact lookups I build a hash set of coordinate tuples (O(n) build, O(1) average lookup). Floats need snapping to a tolerance grid first, and a point near a cell boundary means checking the neighbouring cells too.
  4. For range and nearest-neighbour queries I use a spatial index: a k-d tree (O(n log n) build, roughly O(log n) average nearest-neighbour query in low dimensions, O(n) worst case), a uniform grid for evenly spread points, or an R-tree when the data are boxes or live on disk.
  5. A cheaper alternative is to sort the points by Morton (Z-order) code, which interleaves the bits of the quantised coordinates. Exact lookup is then a binary search in O(log n), and nearby points stay mostly adjacent in memory.

Follow-up

  • How do you find all points within radius r of a query point, and what is the cost?
  • Points are inserted continuously while queries run. Which index would you keep, and how would you rebalance it?
  • The data sits in a relational database instead of memory. How would you index it there?

Write a method to reverse a linked list, and explain its time and space complexity.

Medium · Coding and Algorithmic Problem Solving

Approach

  1. I reverse iteratively with three pointers: prev = None, curr = head. In each step I save nxt = curr.next, set curr.next = prev, then advance prev = curr and curr = nxt. When curr is None, prev is the new head.
  2. The cost is O(n) time and O(1) extra space, since each node is visited once and only its next pointer changes.
  3. The recursive version also takes O(n) time but uses O(n) call-stack space, so a long list can overflow the stack. I mention that before choosing the iterative version.
  4. I trace through an empty list, a single node and two nodes, and check that the old head's next ends up as None, so the reversal does not leave a cycle.

Follow-up

  • Reverse only the nodes between positions m and n in one pass.
  • Reverse the list in groups of k, and leave a final group shorter than k unchanged.
  • How does reversal change for a doubly linked list, and how would you use reversal to check whether a list is a palindrome in O(1) space?

Stop tag and share joins from fanning out a page

Medium · join fan-out · semi-joins · many-to-many

resource_tag is (resource_id, tag_id) with PK (resource_id, tag_id); resource_share is (resource_id, shared_with_user_id, permission). The tagged-and-shared listing inner-joins resource to both, filters tenant_id, tag_id = ANY($2) and shared_with_user_id = $3, orders by updated_at DESC and takes 50. Pages come back with fewer than 50 distinct resources and the total in the header is far too high. Explain the row multiplication, rewrite both the page query and the count query so each is correct, and name the index each one needs. PostgreSQL 16.

Approach

  1. Do the arithmetic against the predicates that are actually there. An inner join emits one row per matching child row, and both joins are filtered: tag_id = ANY($2) admits only the requested tags, shared_with_user_id = $3 admits one user's share rows. So a resource holding three of the requested tags and shared with $3 once yields three rows, not one — the multiplier is its count of matching tags times its share rows for that single user, and that second factor is 1 unless the table admits duplicate (resource_id, shared_with_user_id) pairs. LIMIT 50 then limits rows rather than resources, and COUNT(*) counts pairs — the header is the product, not the population.
  2. Reject DISTINCT as the fix. It deduplicates after the product has been built, so the planner must materialise and sort the fanned-out set before the LIMIT can apply, and it leaves any SUM or AVG in the same select list wrong.
  3. Rewrite both filters as semi-joins, keeping resource as the only row source: AND EXISTS (SELECT 1 FROM resource_tag rt WHERE rt.resource_id = r.resource_id AND rt.tag_id = ANY($2)) and the same shape against resource_share. A semi-join stops at the first match per resource and preserves the driving index order, so ORDER BY updated_at DESC, resource_id DESC LIMIT 50 still stops after 50 rows.
  4. Count with the same predicates and no join at all: SELECT count(*) FROM resource r WHERE r.tenant_id = $1 AND r.status = 'active' AND EXISTS (...) AND EXISTS (...). Nothing multiplies a resource, so the number is the population.

Follow-up

  • The filter changes from 'any of these tags' to 'all of these tags'. Rewrite it and state what it costs relative to the ANY form.
  • A resource can be shared with the same user twice under different permissions. Does your count change, and should it?

Hold a per-tenant active cap against concurrent creates

Hard · write skew · isolation levels · constraints

A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.

Approach

  1. Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
  2. Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
  3. Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
  4. State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.

Follow-up

  • A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
  • The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?

Design a system that allows remote engineers to upload, validate, and store large design files concurrently.

Medium · System Architecture and Integration

Approach

  1. I keep file bytes off the API servers. The client asks a metadata service for an upload session, then sends the file in parallel chunks directly to object storage using pre-signed multipart URLs. A failed chunk is retried on its own, so an unreliable remote connection can resume instead of restarting.
  2. The metadata store holds one row per upload with project, user, size, client-computed SHA-256 and a state machine: initiated, uploading, uploaded, validating, accepted or rejected. Stateless API instances can then scale horizontally.
  3. Validation runs asynchronously. A completion event puts a job on a queue, and workers verify the checksum, detect the file type from its content rather than its extension, run format and virus checks, and only then move the object from a quarantine prefix to the accepted location. Jobs are idempotent so a retry is safe.
  4. For concurrency, every accepted upload becomes an immutable version keyed by content hash. Moving a file's current-version pointer is a compare-and-set on the expected version number, so two engineers saving the same file get a conflict instead of a silent overwrite.
  5. For operations, a lifecycle rule aborts incomplete multipart uploads, a dead-letter queue catches files that keep failing validation, and I track metrics on upload failures and validation backlog.

Follow-up

  • Two engineers edit the same design file at the same time. What does each of them see?
  • Validation takes many minutes for a large file. How does the uploader learn the result?
  • A validation worker crashes halfway through a job. What guarantees the file is neither lost nor validated twice?

How would you architect a REST API to serve project metadata to an internal dashboard?

Medium · System Architecture and Integration

Approach

  1. I model resources from what the dashboard renders: GET /projects with filters (status, owner, updated_since), GET /projects/{id}, and sub-resources such as /projects/{id}/milestones. Where one dashboard view needs several resources, I add a summary endpoint so the page avoids N+1 calls.
  2. Lists use keyset pagination on an immutable key, (created_at, id) or just id, with a matching composite index, instead of OFFSET, so cost does not grow with page depth. I do not page the list on updated_at: a project edited mid-scroll gets a new timestamp, moves past the cursor and is returned twice or skipped. (updated_at, id) is only for an incremental updated_since sync, where the client dedupes by id.
  3. For caching I return an ETag so the dashboard can send If-None-Match and receive 304 Not Modified, set a short Cache-Control, and invalidate any server-side cache on writes. If the metadata comes from several source systems, I sync it into one read model rather than calling them all on every request.
  4. Authentication goes through the organisation's SSO with tokens, and authorisation is checked per project. Errors use standard status codes (400, 401, 403, 404, 412, 5xx) with a consistent error body that clients can branch on.
  5. Versioning: additive changes go out without a new version, breaking changes get /v2 or a version header, and a deprecation window gives older dashboard builds time to move. Edits use PATCH with If-Match for optimistic concurrency.

Follow-up

  • The dashboard needs near-live updates. Would you use polling with ETags, server-sent events or WebSockets, and why?
  • How do you rename a field without breaking dashboard builds that are already deployed?
  • The project list endpoint slows down as the number of projects grows. Where do you look first?

Walk me through how you would design a database schema for a system that tracks mechanical parts, their suppli

Medium · System Architecture and Integration

Walk me through how you would design a database schema for a system that tracks mechanical parts, their suppliers, and their usage across multiple projects.

Approach

  1. Core tables: part (part_id PK, part_number UNIQUE, name, unit, attributes), supplier (supplier_id PK, name, contact fields) and project (project_id PK, code UNIQUE, name, status). Each entity is stored once, normalised to third normal form.
  2. Parts and suppliers are many-to-many, so I add part_supplier (part_id FK, supplier_id FK, supplier_part_number, unit_cost, currency, lead_time_days, is_preferred, valid_from, valid_to). Rows carry effective dates, so a price change adds a row instead of overwriting history.
  3. Usage is project_part_usage (project_id FK, part_id FK, supplier_id FK, quantity CHECK > 0, unit_cost_at_use). Snapshotting the cost means a later supplier price change does not rewrite a past project's totals.
  4. Assemblies use a bill-of-materials adjacency table, part_component (parent_part_id, child_part_id, quantity), which I expand with WITH RECURSIVE. I guard against cycles in application logic or with a path check in the CTE.
  5. I index the foreign-key columns explicitly, because PostgreSQL does not index referencing columns automatically, and add (part_id, project_id) for where-used queries. Deletes use ON DELETE RESTRICT plus a discontinued flag, so a part still in use cannot vanish. Attributes that vary by part type go in a JSONB column or type-specific tables rather than EAV.

Follow-up

  • Write the query for the total cost of a project, including parts nested inside assemblies.
  • Which projects use a given bolt anywhere in their assembly tree, and how do you keep that query fast?
  • A supplier is acquired and its catalogue merges into another supplier's. How does the schema handle that?

What strategies would you use to ensure high availability for an internal tool that our designers rely on dail

Medium · System Architecture and Integration

What strategies would you use to ensure high availability for an internal tool that our designers rely on daily?

Approach

  1. I start by defining availability for the tool's users: which actions must work, during which hours, and the target and recovery objectives (RTO and RPO). That target decides how much redundancy is worth paying for.
  2. I remove single points of failure: at least two stateless app instances behind a load balancer, separate liveness and readiness health checks, instances spread across availability zones, and a database with a replicated standby and automatic failover. Backups are restored on a schedule, because an untested backup does not count.
  3. Most outages come from changes, so I deploy with rolling or blue-green releases and a canary, use expand-then-contract schema migrations so old and new code both work, put risky features behind flags, and keep rollback to one step.
  4. For dependencies such as license servers or external engineering platforms, every call gets a timeout, retries with exponential backoff and jitter, and a circuit breaker. When a dependency is down, the tool degrades to read-only or cached data instead of failing outright.
  5. For observability I measure SLIs from real requests and synthetic checks, alert on error-budget burn rather than on every blip, and write runbooks so whoever is on call can act quickly.

Follow-up

  • The database primary fails in the middle of the working day. Walk through what happens, and how much data could be lost with asynchronous replication.
  • How do you add a column to a large, busy table without downtime?
  • How do you show that the tool actually met its availability target last quarter?

Explain how you would safely migrate legacy project data from an old on-premise server to a new cloud database

Medium · System Architecture and Integration

Explain how you would safely migrate legacy project data from an old on-premise server to a new cloud database.

Approach

  1. I begin with an inventory and profile of the legacy data: schemas, row counts, encodings, nulls, duplicates, orphaned foreign keys, timezone-naive timestamps, attached files, and every script or application that reads or writes the old server.
  2. I write the schema mapping and transformations and decide between lift-and-shift and remodelling. Then I rehearse the full migration on a copy, measure how long it takes, and fix data-quality problems before the real run.
  3. For low downtime I do a bulk load followed by log-based change data capture to keep the cloud database in sync. Cutover is short: freeze writes, apply the final changes, verify, and switch connection strings. If a maintenance window is acceptable, a single offline copy is simpler and I say so.
  4. Batches are idempotent and restartable: they are keyed by primary key, checkpointed and upserted, and data moves over an encrypted channel using least-privilege credentials.
  5. Verification uses per-table row counts, checksums over sorted key and value columns, foreign-key integrity checks, sampled record diffs and application smoke tests. Rollback means keeping the old server intact and read-only with a clear decision deadline.

Follow-up

  • Users keep writing to the old server during the migration. How do you avoid losing those writes?
  • How do you prove to stakeholders that no project records were lost or altered?
  • Halfway through cutover, the validation checksums disagree for one table. What do you do?

Listing latency scales with page size, not with filters

Easy · n+1 · latency · query batching

The tenant listing endpoint reads resource filtered by tenant_id and status, ordered by updated_at DESC, and returns each row plus the owner's display name from app_user and the actor of that resource's latest resource_revision. p99 is 55 ms at 10 rows per page and 1.4 s at 200. Database telemetry shows 401 statements per request, each under 1 ms, and nothing in the slow-query log. Diagnose the cause and give the fix, stating the statement count per request and the p99 you expect afterwards.

Approach

  1. Read the counters before forming a theory. 401 statements for 200 rows is one driver query plus two per row, and sub-millisecond execution with an empty slow-query log rules out a bad plan. The time is round trips, which is why it is invisible in every per-query metric and scales with rows returned rather than with filter selectivity.
  2. Name the two per-row statements from their normalised text: a single-row app_user lookup by user_id, and a resource_revision lookup by resource_id ordered by version DESC LIMIT 1. Confirm by dropping those two response fields and watching the statement count fall to one. That locates the calls in the serialisation layer, not the repository.
  3. Check that the arithmetic accounts for the whole gap. Measure one round trip to the replica in isolation; 400 trips at roughly 3 ms of network plus 0.2 ms of execution is about 1.3 s on top of a 55 ms baseline, which matches. If the multiplication had fallen short, the N+1 would only be part of the story and you would keep looking.
  4. Batch both lookups. Collect owner_user_ids and resource_ids from the driver query, then issue WHERE tenant_id = $1 AND user_id = ANY($2) for the users, and PostgreSQL's SELECT DISTINCT ON (resource_id) ... WHERE resource_id = ANY($2) ORDER BY resource_id, version DESC for the latest revision, which the UNIQUE (resource_id, version) index serves directly. On an engine without DISTINCT ON, use a lateral join or a row_number window. Three statements per request at any page size.

Follow-up

  • The page size is capped at 200 today. What breaks first if it is raised to 2,000, and is it still this bug?
  • How do you stop the next N+1 from reaching production, given that no individual query is slow and the endpoint's tests pass?

Suggested practice checklist

The plan follows the reported loop: coding fundamentals first for the technical assessment, then data-handling and design for the panel, and behavioral rehearsal last.

Day 1 — Map the loop and your stack

  • Write one paragraph per reported stage (Recruiter Screen, Technical Assessment, Panel Interviews) with what you will need for each.
  • Choose your interview language from C#, C++ or Python and list the SQL, Git and design-pattern topics you must refresh.
  • Pick three past projects and note the user, the problem and a measurable result for each.
  • Optional: skim the Revit API or AutoCAD .NET API add-in model (transactions, external commands) and write one sentence on how you would build and ship a plugin that automates a repetitive design task.

Deliverable: A one-page loop map, a chosen language with a refresh list, three project summaries with user and result, and an optional one-line CAD plugin pitch.

Day 2 — Geometry and linked list basics

  • Write the 2D bounding-box overlap function and a table of test cases covering touching, contained, identical and zero-width boxes.
  • Extend the same function to 3D boxes and write down what changes.
  • Write the linked-list reversal iteratively and recursively, and state time and space complexity for each.

Deliverable: Runnable overlap and reversal code with a test table and written complexities.

Day 3 — Data processing: logs and nested JSON

  • Write a script that streams a log file and returns the top three error codes, with handling for malformed lines and ties.
  • Write an iterative JSON flatten that produces path keys and handles lists and empty objects.
  • Generate a large synthetic input for both and compare running time and memory.

Deliverable: Two working scripts, test inputs, and a short note on measured time and memory.

Day 4 — Spatial search, 3D pathing and performance

  • Write a search ladder for finding a coordinate in a large array of points (linear scan, sort plus binary search, grid bucketing, k-d tree or quadtree), then implement the grid or sorted-array version and time it against the scan.
  • For the piping-path scenario, write BFS on a 3D grid with blocked cells for exclusion zones, then upgrade it to A* with a Manhattan-distance heuristic and compare nodes expanded.
  • For the slow batch-processing scenario, profile a sample script (cProfile or your language's profiler), decide whether it is I/O-bound or CPU-bound, then fix the hot path or spread files across processes with multiprocessing.
  • Work through the practice debugging question on latency growing with page size, to practise finding a slow path.

Deliverable: A timed search implementation, a working BFS and A* on a 3D grid, and a short profiling write-up naming the bottleneck and the fix.

Day 5 — Data model and metadata API

  • Sketch the schema for mechanical parts, suppliers and project usage, including the join tables, keys and the three queries you would run most.
  • Outline the REST resources, keyset pagination on an immutable key, filtering and error format for the project metadata dashboard.
  • Do the two SQL practice questions to keep joins and concurrency fluent.

Deliverable: A schema diagram with three queries, an API outline, and completed SQL practice.

Day 6 — Large files, availability and migration

  • Design the design-file upload system: chunked upload, validation step, storage, metadata, concurrency and failure recovery.
  • List the availability measures for a daily-use internal tool: redundancy, health checks, backups, graceful degradation and monitoring.
  • Write a migration plan from an on-premise server to a cloud database with dry run, verification and rollback.

Deliverable: Three one-page designs, each with components, failure paths and a first build step.

Day 7 — Behavioral stories and full rehearsal

  • Write a story for each of the five reported behavioral prompts, with situation, your decision, result and what you would change.
  • Prepare an answer to pushing back on an unfeasible request from a senior mechanical engineer: the goal behind it, the evidence for the constraint, the alternative you offered and the decision.
  • Practise explaining one earlier design to an engineer and then to a non-engineer.
  • Run a full mock: one coding exercise aloud, one design, then two stories.

Deliverable: Six rehearsed stories and one recorded mock run with notes on what to tighten.

Working with engineers and designers

Candidates report that the panel includes stakeholders from design teams, and describe the role as building tools for civil, mechanical and architectural designers. Prepare stories where your user was not a software engineer and where you can state what changed for that user. For the prompt about pushing back on a senior mechanical engineer: restate the goal behind the request, show the specific constraint with evidence (a CAD API limit, data volume, a measured run time), offer a feasible alternative or a phased version, and say what was decided.

  1. How do you push back on a feature request from a senior mechanical engineer when you know it is technically unfeasible?

Tell me about a time you had to explain a complex technical issue to a non-technical stakeholder.

Medium · Behavioral and Cross-Functional Leadersh

Approach

  1. Choose an issue whose consequence the listener cared about, such as a late batch run, a data-loss risk during a file sync or a limitation in a plugin API, so the story has a stake beyond the jargon.
  2. Set the scene in two sentences, then spend most of the answer on the translation: which terms you dropped, and what comparison from the listener's own work you used (for a designer, something like drawing revisions).
  3. Structure what you said as impact first, then the cause in one sentence, then two or three options with the cost and delay of each, then your recommendation and the decision you needed from them.
  4. Show you checked understanding (asked them to restate it, sent a one-page summary or a diagram) and give the outcome: the decision made and what changed afterwards.
  5. The mistake that sinks this answer is a long technical explanation with no decision at the end, or a story that implies the listener was slow.

Follow-up

  • How did you know they had understood, rather than just nodded?
  • What did you leave out on purpose, and was that the right call?
  • What would you have done if they had rejected your recommendation?

Describe a situation where you identified a major inefficiency in a team's workflow and built a tool to fix it

Medium · Behavioral and Cross-Functional Leadersh

Describe a situation where you identified a major inefficiency in a team's workflow and built a tool to fix it.

Approach

  1. Open with how you found the inefficiency: you watched the workflow, counted the manual steps or repeats, and measured the cost in hours or errors before writing any code.
  2. Explain why a tool was the right fix compared with a process change or an existing product, and who you talked to so the tool matched the real workflow rather than your assumption.
  3. Describe the build briefly (language, scope, what you left out of version one) and the rollout: a pilot with one or two users, feedback, then wider adoption.
  4. Close with before and after numbers, who maintains the tool now, and what you did about documentation so it outlived your attention.
  5. The mistake is building from your own guess of the problem, or claiming impact you cannot measure.

Follow-up

  • How did you measure the time or errors saved?
  • How did you get people to switch from their old routine?
  • Who maintains the tool now, and what has broken since?

How do you handle a situation where a project's requirements are constantly changing?

Medium · Behavioral and Cross-Functional Leadersh

Approach

  1. Answer as a working method, then back it with one real example. Start by asking what is driving each change; often the underlying goal is stable even when the requests are not.
  2. Show how you isolated the parts most likely to move. In tooling for designers, a strong example is moving design rules, tolerances or project standards out of code into configuration that engineers could edit and validate themselves, so a new standard or spec change mid-delivery did not need a code release.
  3. Make the cost of change visible: keep a short written change log, restate scope and impact on delivery with each new request, and get the requester to agree to the trade-off.
  4. Use short iterations with demos to the users so wrong assumptions surface early, and agree what done means for each increment.
  5. The mistake is accepting every change silently and then missing the date, or insisting on a frozen spec and ignoring the users.

Follow-up

  • When did you push back on a change, and how?
  • How did you stop the code quality from degrading under repeated changes?
  • How did you estimate work when the scope kept moving?

Tell me about a time you disagreed with a senior engineer on a technical design. How did you resolve it?

Medium · Behavioral and Cross-Functional Leadersh

Approach

  1. Pick a disagreement about a real technical choice (a data model, a framework, a sync approach) and describe both positions fairly, including the strongest argument for the senior engineer's view.
  2. Say how you moved from opinion to evidence: a small prototype, a benchmark, a failure scenario or a written comparison against criteria both of you accepted, such as maintainability, delivery time and risk.
  3. Show how you conducted yourself: you asked questions, listened to the context you lacked, and kept the discussion on the design rather than the person.
  4. State the resolution honestly, whether you were persuaded, persuaded them, or agreed to try theirs and revisit with data, and the result.
  5. The mistake is a story where you were simply right and the senior engineer gave in, or where you went around them.

Follow-up

  • What if the senior engineer had still disagreed after the evidence?
  • Were you ever wrong in that situation, and what did you learn?
  • How did the working relationship change afterwards?

Describe a project that failed. What did you learn, and what would you do differently today?

Medium · Behavioral and Cross-Functional Leadersh

Approach

  1. Choose a failure you owned part of, with real cost (a missed release, a tool nobody used, a migration that had to be redone), not a disguised success.
  2. State plainly what went wrong and the root cause in your own decisions, such as skipping user input, underestimating data volume or leaving a risk unvoiced, without blaming others.
  3. Mention the warning signs you saw or missed, and when you acted on them.
  4. Finish with the concrete change in your practice and where you applied it later: for example, an early demo to users, a load test on realistic data or a written risk list.
  5. The mistake is a vague lesson with no changed behaviour, or a failure so minor it shows no judgment.

Follow-up

  • When did you first see that the project was in trouble, and what did you do?
  • What would you do differently in the first week if you restarted?
  • Where have you applied that lesson since?