Interview guides · Bentley Systems
Bentley Systems Software Engineer Interview Guide
Bentley Systems Software Engineer interview guide: six reported rounds, C++, C#, Node.js and Azure questions, behavioral prep and a 7-day plan.
The 60-second brief
Bentley Systems builds CAD, 3D modeling, digital twin and cloud-based software for infrastructure work such as bridges, roads, rail, industrial plants and cities. Candidates describe engineering work that ranges from native modeling and geometry engines to enterprise services and web applications. The languages and platforms listed for the role are C++, C#/.NET, Java, JavaScript/TypeScript, Node.js, React and Microsoft Azure, with PostgreSQL and SQLite named for the database side. As a Software Engineer you write and test code in one of those stacks, take part in code reviews, and track down performance problems and memory leaks. Which stack you use day to day depends on the team.
This guide is for candidates interviewing for a Software Engineer position at Bentley Systems, whether the team is closer to native C++ and C# modeling work or to cloud and web platforms. It covers the six stages candidates report, the reported coding, object-oriented and low-level, web, database, cloud, design and behavioral questions, and a seven-day plan that moves from online-assessment style coding to memory and OOP depth, then web and cloud topics, then design and stories.
Candidates report six stages over roughly four to six weeks: resume screening, an online coding assessment, technical discussions, technical rounds, a take-home assignment or presentation in some cases, and final stages.
Focus areas
- CRUD Operations
- Programming Assignments (Coding Tasks)
- Data Structures (DSA)
Overview
The reported questions fall into four groups. The first is classic data-structure and algorithm work: reverse a singly linked list and find the second smallest element, remove duplicate characters without extra allocation, partition an unsorted array, reverse a string recursively and state its call-stack cost, and cut an O(N^2) approach down for large spatial data sets. The second group is language depth, mostly C++ and C#: malloc versus calloc, dangling pointers, virtual functions and vtables, virtual destructors, stack versus heap, smart pointers and garbage collection, the four OOP principles, and C# delegates, events and LINQ. The third covers Node.js, REST API security, SQL, Azure deployments and high availability with low latency. The fourth covers your own experience: a resume project, an extensible calculator design, a production performance bug, code reviews and changing requirements.
The stack varies by team, so choose your primary language early. Candidates describe C++ and C# teams as leaning toward memory, pointer and OOP questions, and cloud and web teams as leaning toward Node.js, REST, SQL and Azure. Pick the track that matches your application, then prepare the other groups at a lighter level. Whatever the track, the data-structure questions apply, and candidates report that complexity analysis and edge cases are expected with every solution.
Candidates also report that earlier work can resurface. Code from the online assessment may be revisited in later rounds, so keep what you submitted and be ready to explain and improve it. A take-home or presentation appears in some loops, with a CRUD application named as an example. Treat that as a small engineering deliverable: validation, error handling, tests and a short README, and a clear explanation of the design choices behind them.
Spoken explanation is a large part of this loop. Several reported questions are concept questions (vtables, the event loop, heap versus stack) where the answer is a clear explanation backed by a short code example. Practise saying the answer aloud, and sketch the memory layout or call order as you go.
The Bentley Systems interview process
These are the six stages candidates describe, in the order they report them. Which stages you meet depends on the region, level and team, and the take-home or presentation is reported as present in some cases only.
Resume Screening
The first stage is a review of your resume against the role. Candidates give no format beyond that, so the preparation happens before you apply: make the resume read clearly for one stack and one kind of team. Reported later questions ask you to walk through a resume project and its trade-offs, so every project you list needs a decision you can defend.
What to demonstrate
- How your listed experience matches the role's languages and fundamentals
- Whether your projects show decisions and trade-offs rather than a technology list
How to prepare
- Choose a primary language from C++, C#/.NET, Java or JavaScript/TypeScript and make it obvious on the page
- For each project write the problem, your own contribution, one design decision and the alternative you rejected
- Remove technologies you could not discuss for five minutes under questioning
Online Coding Assessment
Candidates report an online coding assessment on Codility or HackerRank covering algorithmic logic, debugging, and string and array manipulation, run against time limits. The reported coding questions on linked lists, strings, array partitioning and recursion are the right practice material. Candidates also report that this code can come up again later, so keep your submissions and be able to justify them.
What to demonstrate
- Working code on array, string and linked-list problems
- Debugging ability and handling of edge cases
- Time and space efficiency of the solution
How to prepare
- Solve in-place problems (partition an array, dedupe characters, reverse a list) in a plain editor and state the complexity afterwards
- Write your own edge-case list for each problem: empty input, a single element, all duplicates, already partitioned input
- After submitting any practice solution, rewrite it for lower memory use and compare the two
- Save every assessment solution you submit so you can explain it later
Technical Interactions
This stage is described as direct technical discussion with software leads and hiring managers. Expect conversation about how things work rather than only code on a screen. The reported concept questions fit here as a type: malloc versus calloc, dangling pointers, stack versus heap, OOP principles, the Node.js event loop, REST API security. Answer each with a definition, a small example and the failure mode.
What to demonstrate
- Depth of understanding of your primary language and its memory model
- Ability to explain technical ideas clearly in conversation
- How you reason about speeding up an algorithm on large data
How to prepare
- For each concept write a three-part answer: what it is, a five-line code example, and how it goes wrong
- Practise the O(N^2) to O(N log N) discussion with sorting, hashing and spatial indexing (grid, quadtree) as the options you name
- Be ready to talk through your online-assessment code and say what you would change
Technical Rounds
Candidates report multiple technical rounds mixing live coding, system design and code walkthroughs. Treat live coding as a spoken exercise: restate the problem, name a brute-force approach, improve it, then write and test. For design, the reported questions include an extensible calculator built on a baseline execution module and component design for high availability and low latency. For a code walkthrough, practise reading unfamiliar code and naming flaws and refactors.
What to demonstrate
- Live coding with correct complexity analysis
- Design reasoning: component boundaries, extensibility, availability and latency
- Reading and critiquing code you did not write
How to prepare
- Code aloud: write the solution, then run two or three test cases by hand before saying you are done
- Design the extensible calculator with an operation interface and a registry so a new operation needs no change to existing code
- Prepare a one-page answer on availability and latency covering redundancy, health checks, caching, timeouts and failure handling
- Take a messy function from your own work and write the review comments you would give on it
Take-home Assignment
Candidates report that in some cases the process includes a take-home coding assignment or a technical presentation; a CRUD application is the example given, and the presentation topic is described as an engineering one. The take-home is the stage where clean structure, error handling and tests are visible in what you submit, so plan a small but complete deliverable. If you present, rehearse a short design outline for a feature.
What to demonstrate
- A working application with sensible structure and error handling
- Tests and maintainability of what you submit
- Clarity when presenting a technical topic to engineers
How to prepare
- Build a small CRUD application end to end in your primary stack with input validation, consistent error responses, unit tests and a README
- Use parameterized queries and keep data access separate from request handling, so you can explain the structure
- Write a short presentation outline for a feature: problem, options, chosen design, risks, test plan
Final Stages
The final stage is reported as focused on architectural alignment, managerial discussion and team fit. Prepare to explain design decisions in your own projects and the trade-offs behind them, and to tell concrete stories about code review, shifting requirements and production debugging. Bring questions about the team's stack and how engineers work.
What to demonstrate
- Architectural judgment as shown in projects you have owned
- Collaboration and communication with managers and cross-functional partners
- How you handle changing requirements and difficult defects
How to prepare
- Rehearse the resume project walkthrough with architecture choices and trade-offs, aloud, until it is clear and brief
- Write STAR outlines for code review, changing requirements and a production performance bug or memory leak
- Prepare two or three questions about the team's stack, testing practice and review process
Where candidates lose points
Writing a linked-list or array solution without stating complexity or handling small inputs
After writing the reversal and second-smallest code, say its time and space cost, then test an empty list, one node, two nodes and a list where every value is equal. Decide what second smallest means when values repeat, and say that choice aloud.
Reciting the vtable definition without showing what goes wrong without virtual
Write the example: a derived object deleted through a base pointer. Without a virtual destructor the derived destructor does not run, which is undefined behavior. Add the pointer-to-vtable-per-object picture, and say what a smart pointer changes.
Treating the online assessment as finished once it is submitted
Candidates report that assessment code can come up again later. Keep a copy of each submission, re-read it after a day, note a leaner version and the tests you skipped, and be ready to defend or improve it aloud.
Delivering a take-home CRUD application that works only on the happy path
Add input validation, consistent error responses, parameterized queries, unit tests for the core logic and a README that says how to run it. A smaller feature set with these in place is stronger than a wide one without them.
Describing a resume project as a list of technologies rather than your decisions
For each project, state the problem, what you personally designed, one alternative you rejected and why, and what you would change now. Prepare one failure or performance problem per project so you can speak about trade-offs concretely.
Questions & practice
Write a program to reverse a singly linked list and retrieve the second smallest element.
Medium · Data Structures & Algorithms
Approach
- Reverse iteratively with three pointers (prev, curr, next): save curr.next, point curr.next at prev, advance both. That is O(n) time and O(1) extra space; the recursive version is also O(n) but uses O(n) call stack.
- Reversal does not change which value is second smallest, so I find it during the same traversal: keep min and second, and when a value beats min, shift min into second first.
- Pin down duplicates before coding: for 1 -> 1 -> 2, is the answer 1 (second in sorted order) or 2 (second distinct value)? The comparison is either strict or non-strict depending on that answer.
- Edge cases: an empty list, a single node, and a list where every value is equal have no second smallest, so I return an optional or a flag instead of a magic number like INT_MAX that could also be a real value.
- Return the new head and test on lists of length 0, 1, 2 and 5. Then walk the result to check that the old head's next is null, so the list has no cycle.
Follow-up
- How would you reverse only the nodes between positions m and n, or reverse the list in groups of k?
- How do you generalise to the k-th smallest value, and what does a size-k max-heap cost compared with sorting?
- What happens to your reversal if the list contains a cycle, and how would you detect one first?
Implement an algorithm to remove duplicate characters from a string efficiently without excessive memory alloc
Medium · Data Structures & Algorithms
Implement an algorithm to remove duplicate characters from a string efficiently without excessive memory allocation.
Approach
- Clarify the contract: keep the first occurrence of each character and preserve order? Is it case-sensitive? Is the input ASCII bytes or Unicode?
- For a fixed alphabet I keep a 256-entry boolean table (or a 32-byte bitset) and do one pass with a read index and a write index over a mutable buffer, copying a character only if it has not been seen. That is O(n) time and O(1) extra space because the table size does not grow with n.
- In C++ I compact in place and then call s.resize(write). Java and C# strings are immutable, so I make one char[] or StringBuilder of the input length and avoid repeated concatenation, which would copy the prefix each time.
- With no table allowed at all, I compare each character against the kept prefix: O(n^2) time, O(1) space. Sorting first gives O(n log n) but loses the original order.
- For Unicode, UTF-8 multi-byte sequences and UTF-16 surrogate pairs mean I iterate code points and use a hash set, so the space becomes O(distinct characters). Test the empty string, a single character, all-identical input and input that has no duplicates.
Follow-up
- How does the solution change if only adjacent duplicates should be removed?
- How would you remove every character that appears more than once, keeping none of its copies?
- How do you return the lexicographically smallest result that contains each distinct character once?
Given an unsorted array of integers, segregate elements or swap array elements to achieve a target distributio
Medium · Data Structures & Algorithms
Given an unsorted array of integers, segregate elements or swap array elements to achieve a target distribution.
Approach
- First pin down the target distribution: odds before evens, 0s before 1s, negatives before positives, values around a pivot, or three groups (0/1/2). Also ask whether relative order must be preserved.
- For two groups I use two pointers from both ends: advance left while it is in group A, retreat right while it is in group B, then swap. That is O(n) time with O(1) space, and it is not stable.
- For three groups I use the Dutch national flag pass with low, mid and high. A 0 swaps with low and both advance. A 2 swaps with high and only high moves back, because the value swapped in from high has not been examined yet.
- If order must be kept, a stable partition takes O(n) time with an O(n) buffer, or O(n log n) in place by divide and conquer with rotations. std::stable_partition picks between those depending on whether it can get memory.
- Edge cases: an empty array, an array with only one group, and negative numbers. In C++ and Java, -3 % 2 is -1, so I test x % 2 != 0 (or x & 1) and not x % 2 == 1.
Follow-up
- What is the minimum number of swaps needed to bring all elements <= k together? (Sliding window sized to the count of such elements.)
- How would you rearrange the array so positives and negatives alternate?
- Why does mid not advance after swapping with high in the three-way partition?
Write a recursive function to reverse a string on the spot and analyze its call stack space complexity.
Medium · Data Structures & Algorithms
Approach
- Recursive two-pointer swap: reverse(s, i, j) returns when i >= j, otherwise swaps s[i] and s[j] and calls reverse(s, i+1, j-1). Time is O(n) and each character is touched once.
- Stack space is O(n): there are about n/2 frames, each holding the indices and a return address. The in-place swap saves heap memory, but recursion still costs linear stack.
- Avoid the version that returns reverse(s.substr(1)) + s[0]. Each call copies a substring, so the total is O(n^2) time and O(n^2) allocated characters.
- C++, Java and Python do not guarantee tail-call elimination, and CPython's default recursion limit is 1000. A long input can therefore overflow, and the production version is the iterative two-pointer loop with O(1) extra space.
- Java, C# and Python strings are immutable, so 'in place' means working on a char array. Reversing UTF-16 code units breaks surrogate pairs and combining marks, so I state whether the input is ASCII.
Follow-up
- How would you reverse the order of words in a sentence in place? (Reverse the whole buffer, then reverse each word.)
- Can you rewrite it to be tail-recursive, and would your compiler optimise it?
- How would you check recursively whether a string is a palindrome, ignoring non-alphanumeric characters?
How do you optimize an algorithm from to or when processing large spatial data s
Medium · Data Structures & Algorithms
How do you optimize an algorithm from to or when processing large spatial data sets?
Approach
- Name the quadratic baseline precisely, for example comparing every pair of points or bounding boxes for proximity or overlap. Then note that if the answer itself can contain O(n^2) pairs, no algorithm beats output size, so the target becomes O(n log n + k).
- Sorting plus a sweep line handles interval overlap and 1-D or 2-D range problems in O(n log n + k). Divide and conquer gives the closest pair of points in O(n log n).
- A uniform grid (spatial hash) with cell size equal to the query radius means each point checks only its own and neighbouring cells (9 in 2-D, 27 in 3-D). That is near-linear for evenly spread data, and it degrades when points cluster into a few cells.
- Tree indexes for skewed data: a k-d tree builds in O(n log n) and answers nearest-neighbour queries in roughly O(log n) on typical data (O(n) worst case). Quadtrees and octrees adapt to density. R-trees index bounding boxes for overlap queries.
- For exact geometry, use a cheap broad phase (bounding boxes) to prune candidates before the expensive narrow-phase test. Compare floating-point coordinates with a tolerance, not ==.
Follow-up
- The objects move every frame. Would you rebuild the index or update it incrementally, and what does each cost?
- The dataset does not fit in memory. How would you tile or partition it, for example by Z-order or Hilbert curve keys?
- Your grid is fast in tests but slow on real data. What would you measure, and what would you change?
Explain the differences between malloc and calloc in memory allocation, and describe how dangling pointers
Medium · Object-Oriented Programming & Low-Level
Explain the differences between malloc and calloc in memory allocation, and describe how dangling pointers occur.
Approach
- malloc(size) returns uninitialised bytes. calloc(count, size) returns count*size bytes set to zero, and mainstream implementations return NULL if the multiplication would overflow, whereas a hand-written malloc(count * size) can silently wrap.
- Zeroing is not always an extra cost: for large requests the allocator can map fresh pages that the OS already zeroes. Both return NULL on failure and both are released with free. Neither runs C++ constructors; new does, and memory from new must be released with delete, never free.
- A dangling pointer still holds an address whose lifetime has ended. Three common cases: using a pointer after free or delete, returning the address of a local stack variable, and keeping a pointer or iterator into a std::vector that has since reallocated. Dereferencing it is undefined behaviour.
- Related bugs to name: double free, and realloc moving the block so that every old alias dangles. Setting the pointer to NULL after free protects only that one copy, not other aliases.
- Prevention: give each allocation a single owner (std::unique_ptr, RAII containers) and catch use-after-free in testing with AddressSanitizer or Valgrind.
Follow-up
- What does realloc do when it cannot grow the block in place, and what happens to existing pointers into it?
- How is a dangling pointer different from a memory leak, and can one bug cause both?
- Why is mixing malloc with delete, or new with free, undefined behaviour?
What is the virtual keyword in C++, how does a virtual table (vtable) work, and why are virtual destructors ne
Medium · Object-Oriented Programming & Low-Level
What is the virtual keyword in C++, how does a virtual table (vtable) work, and why are virtual destructors necessary?
Approach
- virtual gives dynamic dispatch: a call through a base pointer or reference runs the most-derived override. Mark overrides with override so a signature mismatch is a compile error, and use = 0 for pure virtual functions to make a class abstract.
- The standard does not mandate vtables, but the usual implementation gives each polymorphic class a table of function pointers and each object a hidden vptr that the constructor sets. A virtual call loads the vptr, indexes the slot and makes an indirect call.
- Costs: one pointer per object, an indirect call, and usually no inlining unless the compiler can devirtualise, which final helps with. Inside a constructor or destructor, virtual calls resolve to the class currently being built or destroyed, not the derived one.
- Deleting a derived object through a base pointer whose destructor is not virtual is undefined behaviour. In practice the derived destructor is skipped and its resources leak. The rule: a base class meant for polymorphic deletion gets a public virtual destructor; otherwise it gets a protected non-virtual one.
- I would sketch Base with a virtual draw() and Derived owning a buffer, show delete basePtr with and without the virtual destructor, and say which destructors run in each case.
Follow-up
- How does object layout change with multiple inheritance, and why can a vtable entry point to a thunk?
- What problem does virtual inheritance solve in a diamond hierarchy, and what does it cost?
- What happens if a base constructor calls a pure virtual function?
Explain core Object-Oriented Programming (OOP) principles—Polymorphism, Encapsulation, Inheritance, and Abstra
Medium · Object-Oriented Programming & Low-Level
Explain core Object-Oriented Programming (OOP) principles—Polymorphism, Encapsulation, Inheritance, and Abstraction—with concrete code examples.
Approach
- Use one running example for all four principles, for example a Shape hierarchy in a drawing tool, so each principle appears in code and not only as a definition.
- Encapsulation: private state plus methods that protect an invariant. A Polygon keeps its vertices private and exposes addVertex(), so a cached perimeter can never go stale. Abstraction: callers depend on a Shape interface with area() and draw() and never see the geometry inside.
- Inheritance models is-a and shares code, but it must respect Liskov substitution. Square deriving from a mutable Rectangle breaks callers that set width and height independently, so composition is often the better choice.
- Polymorphism: at runtime through virtual/override (a loop over vector<unique_ptr<Shape>> calling draw()), and at compile time through overloading and templates or generics. Name the trade-off: runtime dispatch costs an indirect call, while templates generate one copy of the code per type.
- Close with a design-pattern link where it fits: Factory to create shapes from input, and Strategy to swap rendering, both resting on the abstraction.
Follow-up
- When would you use an abstract class rather than an interface in C# or Java, given that interfaces can now have default methods?
- Give an example where inheritance caused a bug and composition fixed it.
- How does the Liskov substitution principle apply to the Rectangle/Square example?
Describe the difference between stack and heap memory allocation, and explain how garbage collection or smart
Medium · Object-Oriented Programming & Low-Level
Describe the difference between stack and heap memory allocation, and explain how garbage collection or smart pointers manage memory lifecycle.
Approach
- Stack: one per thread, LIFO, lifetime tied to scope, allocation is a pointer adjustment, and its size is fixed by the OS or linker settings, so deep recursion or large local arrays overflow it. Heap: lifetime decided at runtime, shared across threads, slower allocation through an allocator, and subject to fragmentation.
- C++ uses RAII. std::unique_ptr is single ownership with no reference-count overhead and is move-only. std::shared_ptr keeps an atomic reference count in a control block, and make_shared puts object and control block in one allocation. Reference cycles never reach zero, so one side of a back-reference should be std::weak_ptr.
- C# and Java use a tracing, generational garbage collector: short-lived objects are collected cheaply, survivors are promoted, and the heap can be compacted. Collection timing is non-deterministic, so files, sockets and locks need IDisposable with using (or try-with-resources in Java).
- Correct the myth that value types always live on the stack: a C# struct stored in an array or as a field of a class lives on the heap with it.
- Leaks still happen under a GC when something keeps a reference alive: event subscriptions, static caches and long-lived collections. Find them by comparing heap snapshots and following the retention path.
Follow-up
- Show a shared_ptr cycle that leaks, and how weak_ptr breaks it.
- How can a garbage-collected program still run out of memory, and how would you find the cause?
- Why might returning a large object by value in C++ be cheap? (Move semantics and copy elision.)
What are C# delegates, events, and LINQ queries, and how do they differ from functional features in other obje
Medium · Object-Oriented Programming & Low-Level
What are C# delegates, events, and LINQ queries, and how do they differ from functional features in other object-oriented languages?
Approach
- A delegate is a type-safe reference to a method: Func<...> and Action<...> cover most signatures. Delegates are multicast (+= adds to the invocation list), and lambdas convert either to a delegate or, when the target is Expression<Func<...>>, to an expression tree.
- An event wraps a delegate so that outside code can only subscribe (+=) or unsubscribe (-=) and only the owning class can raise it. I raise it with Handler?.Invoke(this, args). A publisher holds strong references to its subscribers, so a forgotten -= keeps them alive: a classic .NET leak.
- LINQ is query or method syntax over IEnumerable<T>. Operators such as Where and Select are deferred, so they run when enumerated, while ToList and Count run immediately. Enumerating the same deferred query twice runs it twice.
- IQueryable<T> receives expression trees that a provider such as Entity Framework translates to SQL. A method the provider cannot translate either throws or forces client-side evaluation, depending on the version.
- Comparisons: Java uses functional interfaces plus lazy, single-use streams. C++ has lambdas, std::function (type-erased, may allocate) and C++20 ranges. JavaScript functions are first-class objects with no separate delegate type.
Follow-up
- Show a bug caused by deferred execution, for example a query that captures a loop variable or a list that changes before enumeration.
- What is the difference between Func<T, bool> and Expression<Func<T, bool>> when passed to Where?
- How would you prevent an event subscription from leaking a short-lived subscriber?
Explain the Node.js event loop, asynchronous execution, and how JavaScript Promises handle non-blocking I/O.
Medium · Web Technologies, Databases & Cloud Syst
Approach
- JavaScript runs on one thread, and libuv drives the event loop in phases: timers (setTimeout/setInterval), pending callbacks, poll (I/O), check (setImmediate) and close callbacks. Each callback runs to completion.
- After each callback, Node drains the process.nextTick queue and then the Promise microtask queue before moving on. A then-callback therefore always runs asynchronously, even on an already-resolved promise, and endless recursive nextTick calls can starve I/O.
- Network I/O uses the OS's non-blocking mechanisms (epoll, kqueue, IOCP). File system calls, dns.lookup, some crypto and zlib run on the libuv thread pool (4 threads by default, set by UV_THREADPOOL_SIZE), so heavy file work can queue behind itself.
- Promises are pending, fulfilled or rejected, and settle once. async/await is syntax over them: await suspends the function and resumes it as a microtask. Use Promise.all for fail-fast parallelism and Promise.allSettled when every result is needed.
- CPU-bound work, such as a large JSON.parse or a tight loop, blocks every request. Move it to worker_threads or a separate process, or split it into chunks.
Follow-up
- What is the output order of console.log, process.nextTick, Promise.resolve().then, setTimeout(0) and setImmediate, and which pair can vary when run from the main module?
- What happens to an unhandled promise rejection in current Node versions?
- How would you find out what is blocking the event loop in a production service?
Write a SQL query involving JOIN operations, triggers, or cursors to extract data from relational databases
Medium · Web Technologies, Databases & Cloud Syst
Write a SQL query involving JOIN operations, triggers, or cursors to extract data from relational databases like PostgreSQL or SQLite.
Approach
- Start from the grain of the result and join outward. Use INNER JOIN for matches only and LEFT JOIN to keep unmatched rows. Put conditions on the right-hand table in ON, not WHERE, or the LEFT JOIN quietly becomes an inner join.
- Anti-joins ('rows with no match') use NOT EXISTS or LEFT JOIN ... WHERE right.id IS NULL. Avoid NOT IN with a subquery that can return NULL: x NOT IN (1, NULL) is never true, so the query returns no rows.
- Before aggregating, check for fan-out: joining one-to-many and then summing a parent column inflates it, so aggregate in a subquery or CTE first. Index the join and filter columns.
- Triggers: in PostgreSQL, a trigger function (plpgsql) is attached BEFORE or AFTER, FOR EACH ROW or FOR EACH STATEMENT, and reads NEW and OLD. Typical uses are updated_at stamps and audit tables. SQLite triggers are written inline between BEGIN and END and support only FOR EACH ROW.
- Cursors process rows one at a time. I prefer a set-based UPDATE ... FROM or INSERT ... SELECT, and keep cursors for streaming a large result in batches (DECLARE, then FETCH 1000 inside a transaction in PostgreSQL).
Follow-up
- Rewrite a cursor loop that updates each row as a single set-based statement.
- What are the risks of business logic in triggers (hidden side effects, recursion, bulk-load cost)?
- How would you find which part of a slow join is expensive? (EXPLAIN ANALYZE and the row estimates.)
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
- 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.
- 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.
- 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.
- 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?
What strategies and mechanisms do you employ to secure RESTful APIs against common vulnerabilities?
Medium · Web Technologies, Databases & Cloud Syst
Approach
- Transport and identity: TLS everywhere, OAuth 2.0 / OpenID Connect for users, short-lived access tokens, and full JWT validation (signature against an allow-listed algorithm, exp, iss, aud), never just decoding the token.
- Authorization on every object, not only on the route: check that the caller owns or may access the specific resource id. Broken object-level authorization tops the OWASP API Security Top 10. Allow-list writable fields to prevent mass assignment.
- Injection: bind parameters in SQL instead of building strings, validate input against a schema (types, lengths, ranges), and encode output. Cap request body sizes and page sizes.
- Abuse and exposure: rate-limit per client and token, configure CORS to known origins only, return generic error bodies without stack traces, and keep secrets in a managed vault rather than in config files.
- Detection: log authentication failures and access decisions with request ids, and alert on spikes. Then name the trade-off: stateless JWTs scale easily but are hard to revoke before they expire.
Follow-up
- How would you revoke a JWT before it expires?
- Should a browser client keep tokens in localStorage or an httpOnly cookie, and what does each choice expose you to (XSS vs CSRF)?
- How would you authenticate service-to-service calls differently from user calls?
How do you manage cloud infrastructure and distributed service deployments on Microsoft Azure?
Medium · Web Technologies, Databases & Cloud Syst
Approach
- Infrastructure as code: Bicep/ARM templates or Terraform in version control, applied by a pipeline (Azure DevOps or GitHub Actions) with a plan or what-if step reviewed before apply. Environments are separated by subscription or resource group.
- Choose compute to fit the workload and say why: App Service for web apps, Azure Functions for event-driven work, AKS or Container Apps for containerised microservices. Use Service Bus or Event Grid to decouple services.
- Deploy without downtime: App Service deployment slots with a swap, or rolling and canary releases on AKS. Gate promotion on health checks, and keep the previous version ready for rollback.
- Secrets and identity: managed identities instead of connection strings in code, Key Vault for the secrets that remain, and least-privilege RBAC per service.
- Operations: Application Insights and Azure Monitor for traces, metrics and alerts; autoscale rules on measured load; availability zones or a paired region where the uptime target requires it.
Follow-up
- How do you roll back a release that shipped with a database schema change? (Expand/contract migrations.)
- How would you rotate a secret without restarting every service?
- How would you notice that configuration drifted from what the IaC describes?
What are key architectural considerations when designing system components for high availability and low laten
Medium · Web Technologies, Databases & Cloud Syst
What are key architectural considerations when designing system components for high availability and low latency?
Approach
- Remove single points of failure: several stateless instances across availability zones behind a load balancer with health checks, so any instance can serve any request and a failed one drops out of rotation.
- State the availability arithmetic: components in series multiply (two independent 99.9% dependencies give about 99.8%), while redundancy in parallel raises availability. Fewer hard dependencies on the request path is often the bigger win.
- Data tier: synchronous replication protects committed writes but adds write latency; asynchronous replication is faster but can lose recent writes on failover. Pick from the recovery-point target, and name the CAP trade-off under a network partition.
- Latency: cache at the right layer (in-process, a distributed cache, a CDN) with an explicit invalidation rule. Keep non-critical work off the request path using queues, and track p99, not the average.
- Resilience: timeouts on every call, retries with exponential backoff and jitter on idempotent operations only, circuit breakers and bulkheads so one slow dependency cannot exhaust threads, and graceful degradation when a dependency is down.
Follow-up
- How would you prevent a cache stampede when a hot key expires?
- What changes between active-passive and active-active multi-region, especially for writes?
- A dependency's p99 doubles. How does that reach your service, and what limits the damage?
Walk through a complex technical project on your resume, detailing your personal architectural choices and tra
Medium · Behavioral, Resume & System Scenarios
Walk through a complex technical project on your resume, detailing your personal architectural choices and trade-offs.
Approach
- Pick one project where you made the architectural calls yourself. Open with the problem and its constraints in two sentences (users, data size, latency or memory budget), then sketch the components and the data flow.
- For two or three decisions, give the alternative you rejected and why, in first person: 'I chose a spatial index over a full scan because ...'. Keep a clear line between your own work and the team's.
- Back the choices with numbers you can defend, such as before/after latency, memory, build time or defect counts, and say how you measured them.
- Name something that went wrong or that you would design differently now, and what you learned. A walkthrough with no cost or regret sounds rehearsed.
- Practise with a diagram you can redraw quickly, and be ready to go one level deeper on any box: data model, failure handling, concurrency and testing.
Follow-up
- Why did you not use the obvious alternative, and what would have made it the right choice?
- What part of this design breaks first at ten times the load or data?
- If you rebuilt it today, what would you change first?
How would you design an extensible calculator application assuming you are given a baseline calculator executi
Medium · Behavioral, Resume & System Scenarios
How would you design an extensible calculator application assuming you are given a baseline calculator execution module?
Approach
- Clarify scope: which operations ship first, whether input is an expression string or button presses, the numeric type (double, decimal or arbitrary precision), and what the baseline execution module already exposes.
- Open/closed core: an Operation interface (symbol, arity, precedence, associativity, apply) and a registry or factory that maps tokens to operations. Adding modulo or power is then a new class plus one registration, with no change to the evaluator.
- Pipeline: tokenizer, then parser (shunting-yard to RPN, or recursive descent / Pratt parsing for precedence), then evaluator. Wrap the given baseline module behind an Adapter so existing arithmetic is reused and can later be swapped out.
- Extras via patterns: Command objects for undo/redo and history, Strategy for the number type, and optionally plugin discovery to load operations at runtime.
- Errors and tests: division by zero, malformed input, unary versus binary minus, overflow, and floating-point surprises such as 0.1 + 0.2. Write unit tests per operation plus parser tests for precedence and associativity.
Follow-up
- How do you add functions with variable arity, such as max(a, b, c), or user-defined variables?
- How would you support undo and redo across a sequence of operations?
- How would third-party operations load at runtime without being able to crash the evaluator?
Describe a situation where you had to debug a difficult performance bottleneck or memory leak in a production
Medium · Behavioral, Resume & System Scenarios
Describe a situation where you had to debug a difficult performance bottleneck or memory leak in a production environment.
Approach
- Open with the symptom and how it was measured: p99 latency climbing under load, or memory that grows steadily across requests (a leak) instead of the sawtooth of normal collection. Include when it started and what had changed.
- Show the narrowing sequence in first person: how I reproduced it outside production, then which tool isolated it. Examples: a CPU profiler or flame graph for hot paths; heap snapshots compared over time for leaks (dotMemory or dotnet-gcdump, heaptrack or Valgrind for C++, Chrome DevTools for Node).
- Name the actual root cause and its retention path or hot path, for example an unsubscribed event handler holding objects alive, an unbounded cache, an N+1 query, or a lock held across I/O.
- Give the fix and its verification: before/after numbers from the same measurement, plus what stops a repeat (a bounded cache, a regression or load test, an alert on memory or latency).
- The answer falls apart when it is vague: no numbers, 'it was the database', or a team-level account where your own steps are unclear.
Follow-up
- How did you tell the root cause apart from something that only happened at the same time?
- What would you have done if you could not reproduce it outside production?
- What monitoring would have caught this earlier?
Suggested practice checklist
The plan follows the order candidates describe: online-assessment coding first, then language depth for the technical conversations, then web, database and cloud topics, then design, the take-home and behavioral stories. Each day names the reported questions it practises.
Day 1 — Map the loop and your own material
- Write the six reported stages on one page and next to each note what you must bring to it
- Decide your primary language (C++, C#/.NET, Java or JavaScript/TypeScript) and whether you are targeting a native-engine or a cloud/web team
- Pick your strongest resume project and write its problem, your contribution, two design decisions and what you would change
Deliverable: A one-page loop map, a written language and team-track choice, and a project walkthrough outline with its design decisions.
Day 2 — Online-assessment coding: lists, strings, arrays
- Reverse a singly linked list iteratively, then find the second smallest value in one pass, and test the empty list, one node and all-equal values
- Remove duplicate characters from a string using a fixed-size seen table, then redo it in place and note what changes for Unicode input
- Partition an unsorted array into a target distribution (for example odd and even values) with two pointers and O(1) extra space
- Write the time and space complexity and the edge-case list beside each solution
Deliverable: Three working solutions, each with a stated complexity and a written edge-case test list.
Day 3 — Recursion, complexity and spatial data
- Write a recursive in-place string reverse with two indexes, state its call-stack depth, and say what happens for a very long string
- Take an O(N^2) pairwise comparison over 2D points and reduce it with sorting plus a sweep, then with a uniform grid or quadtree, noting when each helps
- Re-open yesterday's solutions and explain each aloud as if defending it in a later round
Deliverable: A recursive reverse with stack analysis, a one-page note comparing the optimization techniques, and a spoken defence of each day-two solution.
Day 4 — C++ and C# memory and OOP depth
- Write a base and derived class, delete through a base pointer with and without a virtual destructor, and sketch the vtable layout
- Write short notes on malloc versus calloc, then code a dangling pointer and fix it with std::unique_ptr
- Explain stack versus heap and compare garbage collection with smart-pointer ownership
- Write one small example each of the four OOP principles, and one each of a C# delegate, an event and a LINQ query
Deliverable: Working code for the virtual-destructor case and the dangling-pointer fix, plus written answers on memory, OOP and C# features with an example each.
Day 5 — Web, SQL and cloud
- Write a Node.js snippet mixing a promise, a timer and synchronous code, predict the output order, and explain it using the event loop
- Write a REST API security checklist: authentication and authorization, input validation, parameterized queries, rate limiting and transport security
- Write a SQL query with a JOIN on PostgreSQL, then a trigger and a cursor example, noting what SQLite handles differently, and try the practice SQL question
- Outline how you would deploy and run a service on Azure, and what you would add for high availability and low latency
Deliverable: A predicted-and-verified event-loop output, a REST security checklist, working SQL for a join, trigger and cursor, and a one-page deployment and availability outline.
Day 6 — Design, debugging and the take-home
- Design an extensible calculator around a baseline execution module: an operation interface, a registry, and unit tests showing a new operation added without editing existing code
- Write your production performance-bottleneck or memory-leak story: symptom, how you measured, root cause, fix, and what you checked afterwards
- Build a small CRUD application with validation, error handling, parameterized queries, tests and a README
- Write a short presentation outline for one feature design
Deliverable: A tested calculator design, a written debugging story, a finished CRUD application with a README, and a presentation outline.
Day 7 — Behavioral stories and a full mock
- Write STAR outlines for the code review question and for changing requirements or tight deadlines
- Rehearse the resume project walkthrough aloud, then try the practice question on unblocking an engineer
- Do a mixed mock: one coding problem aloud, one memory or OOP explanation, one story
Deliverable: Two written STAR outlines, a rehearsed project walkthrough and a mock-session record listing what to fix.
Behavioral and project discussion prep
Candidates report behavioral and project questions alongside the technical ones: a resume project with your architectural choices, a production performance problem, code review practice and handling shifting requirements with other teams. For a role that spans engines, services and web interfaces, prepare stories where you made a technical decision, measured a result and worked with other people on the outcome.
- Describe a situation where you had to debug a difficult performance bottleneck or memory leak in a production environment.
- Walk through a complex technical project on your resume, detailing your personal architectural choices and trade-offs.
- Tell me about a time you presented a technical design or analysis to a group of senior engineers and how you handled their questions.
How do you approach code reviews when evaluating performance, maintainability, and architectural standards?
Medium · Behavioral, Resume & System Scenarios
Approach
- Choose a real review where you changed an outcome, ideally one on each side: a review you gave that caught something, and feedback you received that you acted on. State the context in two sentences, then spend the answer on method.
- Describe a repeatable order. Read the description and tests first, check correctness and edge cases, then performance (algorithmic cost, needless allocation or copying, an N+1 query), then maintainability (naming, coupling, test coverage), then fit with the architecture and module boundaries.
- Show how you separate blocking issues from nits and say so in the comment. Phrase comments as a concrete observation with a suggested alternative, and back performance claims with a profile or benchmark rather than opinion.
- Name one specific finding, such as a copy inside a hot loop, a resource released on only one path, or a public interface that leaked an implementation detail, and what the author did with your comment.
- Close with what you learned. A sunk answer reads as gatekeeping: a list of style rules, no evidence, and no mention of how the author felt or what the fix cost.
Follow-up
- What do you block a merge on, and what do you leave as a nit?
- How do you review a very large change, or one from someone more senior than you?
- How do you handle an author who disagrees with your comment?
How do you handle changing project requirements or tight technical deadlines when collaborating with cross-fun
Medium · Behavioral, Resume & System Scenarios
How do you handle changing project requirements or tight technical deadlines when collaborating with cross-functional teams?
Approach
- Pick a story where the requirement or deadline moved after work had started, and say who changed it (product manager, another team, a customer) and what it threatened. Keep the situation to a few sentences.
- Show your first move: find out exactly what changed and why, then work out what it does to scope, schedule and risk. Lay out two or three options (cut scope, ship in phases, move the date, add people) with the cost of each.
- Describe how you communicated. Put the trade-off in writing to the people who owned the decision, and get an explicit choice. A decision made out loud in a meeting and never recorded is how requirements drift again.
- Say what you protected under pressure: the core path's tests, a feature flag or a staged rollout, and the items you consciously deferred. Then give the outcome, with something measurable such as what shipped and what slipped.
- Avoid the two weak versions: quietly working longer hours without telling anyone, or blaming the other team. End with the process change you made, such as written acceptance criteria or a short regular check-in.
Follow-up
- What did you cut, and who agreed to it?
- How do you push back on a deadline you think is unrealistic?
- What would you do if the change landed after the code was already complete?
Unblock an engineer without taking the keyboard
Easy · mentoring · leases · at-least-once
A teammate has spent two days on a job handler that occasionally writes duplicate rows. They are certain the queue is delivering twice by mistake. You suspect a lease expiring under a slow handler, so the job is running concurrently with itself. Describe how you have unblocked someone in this position: what you asked before offering a hypothesis, what you showed them rather than told them, and what you left them owning. Then say what you would do if their theory turned out to be the right one.
Approach
- Ask before diagnosing, and ask for things answerable from data they already have: the attempt count on the job rows that produced duplicates, the handler's observed duration against its lease expiry, and whether the duplicate rows share a natural key that a unique constraint could have caught.
- Teach the shape rather than the answer. A lease cannot distinguish a dead worker from a slow one, so a handler that outruns its lease is running twice by design, and deploys deliver the other half by killing handlers mid-run on every rollout. Both of their candidate theories produce identical duplicate rows, which is why the evidence has to come from timings rather than from argument.
- Hand over a checklist they execute: a natural key on every write the handler performs so the second copy collides rather than appends, the record of intent written before any external effect, a lease heartbeat while running, and the metric that shows it working.
- Keep ownership with them deliberately. Pair on the first write, then step back; if you finish it yourself you have closed one ticket and left the same person stuck on the next redelivery.
Follow-up
- How would you distinguish a genuine double-delivery from a lease expiry using only the data already stored?
- Their handler calls an external endpoint before recording that it did. What do you tell them to change first?