cswork / libraryOpen practice workspace

Interview guides · Bgc Group

Bgc Group Software Engineer Interview Guide

Bgc Group Software Engineer interview guide: three reported rounds, C++ and MFC questions, coding and behavioral prep, and a 7-day plan.

The 60-second brief

Bgc Group is described to candidates as a financial services company, and the Software Engineer role is described as building and maintaining applications that support trading operations and risk management. The same description lists designing, coding, testing, debugging and reviewing code, plus gathering requirements with product managers and data analysts, in a team that follows agile practices. The requirements candidates list put C++ and MFC first, with algorithms and data structures behind them. Java, Python, knowledge of financial systems and trading platforms, cloud and microservices exposure, and Git appear as extras. Nothing reported names specific products, teams or customers, so treat anything beyond that as a question for the recruiter.

This guide is for candidates interviewing for a Software Engineer position at Bgc Group. It covers the three reported rounds (Initial Screening, Technical Interviews, Project Discussion), the reported technical, coding, case-study and behavioral questions, and a seven-day plan centred on C++, MFC, algorithmic complexity and your own project stories. It does not cover other roles or other Bgc Group teams.

Candidates report three rounds over roughly three to five weeks: an Initial Screening, a series of Technical Interviews, and a Project Discussion. The first is described as run by an HR representative, the second as coding and algorithm work with team members and managers, and the third as a walk through your earlier projects.

Focus areas

  • C++
  • MFC (Microsoft Foundation Classes)
  • Algorithmic Complexity (Big-O / performance awareness)

Overview

The topics candidates report most are C++, MFC (Microsoft Foundation Classes), Visual C++, algorithmic complexity and the queue data structure. That points to a loop where language depth matters as much as general problem solving. Expect to explain how C++ differs from Java, how memory is managed in C++, how you handle multithreading, and the principles of object-oriented programming, and to describe a real project that used MFC.

The reported coding questions are classic data-structure exercises: implement a queue using stacks, solve a problem involving trees or maps, detect a cycle in a linked list, optimize a given algorithm, and explain your approach to a LeetCode-style challenge. Candidates also mention live coding and take-home assignments. Practise writing these in C++ without an IDE crutch, and state time and space complexity for every solution before you are asked.

Two reported case-study questions are open-ended: designing a new feature for a trading platform, and optimizing an existing system under a set of constraints. A third, the trade-off between performance and maintainability, is usually answered as a story from your own work. Since the role is described as supporting trading and risk management, practise reasoning about correctness first (what happens to a half-finished update, a duplicate message, a stale value) and speed second, and measure before you change anything.

The loop ends with a Project Discussion and the behavioral prompts cover challenge, prioritization, a difficult teammate, motivation and team communication. Prepare two or three projects you can describe at the level of decisions, defects and results, and make one of them the MFC or Visual C++ project if you have one. If you do not have MFC experience, say so plainly and bring the closest desktop or event-driven work you have done.

The Bgc Group interview process

Candidates describe the loop as three stages: Initial Screening, Technical Interviews and Project Discussion. The descriptions below are what candidates report, not a published process, and the reported questions are not tied to a specific stage.

Initial Screening

Candidates describe the Initial Screening as a conversation with an HR representative that checks basic qualifications and fit. Treat it as the point where your CV is matched against the requirements candidates list: C++, MFC, algorithms and data structures, and agile practice. Have a short, factual account of your C++ and MFC work ready, and use the call to ask what the later stages look like.

What to demonstrate

  • Basic qualifications against the role requirements candidates list
  • Fit for the position, as the stage is reported
  • How clearly you describe your C++ and MFC experience

How to prepare

  • Write a short CV walk-through that puts C++, any MFC or Visual C++ work and one algorithm-heavy project first
  • Map each must-have requirement (C++, MFC, algorithms and data structures, agile, problem solving) to one concrete thing you did
  • Prepare a specific answer to what motivates you to stay in software engineering, with one recent example
  • Ask which language the coding stages use and whether any take-home work is involved

Technical Interviews

Candidates describe a series of Technical Interviews with team members and managers, focused on coding exercises and algorithm challenges. This is where the data-structure questions (queue from stacks, linked-list cycle, trees and maps, optimizing an algorithm) and the C++ fundamentals (memory management, multithreading, OOP, C++ against Java) are practised. Write code that compiles and run it on edge cases rather than describing it.

What to demonstrate

  • Working code written live, since the format is coding exercises
  • Choice of data structure and algorithm, with time and space complexity
  • Depth of C++ knowledge, the most-reported topic
  • How you explain your reasoning while solving

How to prepare

  • Write the two-stack queue, Floyd cycle detection and one tree or map problem in C++ and state complexity for each
  • Rehearse a memory-management answer built on RAII, smart pointers and ownership, not just new and delete
  • Prepare a threading answer: shared state, the primitive you chose, how you avoided deadlock, how you tested it
  • Take one brute-force solution and walk through measuring it, finding the hot spot and improving it

Project Discussion

Candidates describe the Project Discussion as a conversation about your previous projects, covering your experience and how you solved problems. Because MFC and Visual C++ are reported topics, the strongest material is a project where you can explain architecture, a hard defect and a trade-off you made. Describe your own decisions in the first person, with the result you saw.

What to demonstrate

  • Experience on real projects, as the stage is reported
  • Problem solving shown through decisions you made
  • Ability to explain technical choices and trade-offs clearly

How to prepare

  • Write a one-page brief for two or three projects: goal, your role, design, hardest bug, trade-off, outcome
  • Prepare the MFC project story with the challenges you faced, or the closest GUI or event-driven work if you have none
  • Have a performance-versus-maintainability story with a measurement and a before and after
  • List what you would change about each project today, and why

Where candidates lose points

Answering the C++ versus Java question as a list of slogans (C++ is faster, Java has garbage collection) with no model behind it.

Organise the answer by ownership and memory (manual and RAII against garbage collection), compilation and runtime model, object model and inheritance, templates against generics, and undefined behavior. End with when you would choose each.

Stopping the memory-management answer at new and delete.

Cover stack versus heap, RAII, std::unique_ptr and std::shared_ptr, breaking shared_ptr cycles with weak_ptr, the rule of five, and the usual defects: leaks, double free, dangling pointers. Be able to write a small class that owns a resource correctly.

Submitting queue-from-stacks or linked-list cycle code with no complexity statement and no edge cases.

For the two-stack queue, explain the lazy transfer from the input stack to the output stack and why pop is amortised O(1). For cycle detection, contrast the visited set (O(n) memory) with Floyd's slow and fast pointers (O(1) memory), and test the empty list, a single node and a self-loop.

Saying you would optimize an algorithm or system without measuring it first.

Start from a measurement: profile, find the hot spot, state the complexity of the current approach, then change one thing and measure again. Say which constraint (time, memory, readability) you are trading.

Giving a vague project story in the Project Discussion, with team-level language and no decision of your own.

For each project, name your role, one design decision with the alternatives, the hardest defect and how you found it, and the result. For an MFC project, be able to explain the message loop and UI-thread rules; if you lack MFC experience, say so and describe the closest work.

Questions & practice

Explain how memory management works in C++.

Medium · Technical / Domain Questions

Approach

  1. Name the storage durations: automatic (stack, freed at scope exit), static, thread-local, and dynamic (heap, via new/delete or an allocator). Only dynamic storage needs explicit ownership decisions.
  2. Raw new/delete rules: pair new with delete and new[] with delete[]. A mismatch, a double delete or use after delete is undefined behaviour, and a missed delete leaks. Point out that an exception thrown between new and delete leaks unless something owns the pointer.
  3. RAII and smart pointers: unique_ptr gives sole, move-only ownership with no overhead under the default deleter. shared_ptr keeps an atomic reference count in a control block, and make_shared puts object and block in one allocation. weak_ptr breaks reference cycles that would otherwise never reach zero.
  4. Rule of zero, three and five: when a class owns a raw resource, define or delete the destructor, copy and move operations together, or better, hold the resource in a member that already does (rule of zero). Move semantics transfer buffers instead of copying them.
  5. Tooling and hot paths: find leaks and overruns with AddressSanitizer or Valgrind. In latency-sensitive loops, avoid repeated heap allocation by calling reserve, using object pools, or using an arena allocator.

Follow-up

  • Why does make_unique matter for exception safety in a call like f(unique_ptr<A>(new A), g())?
  • Two objects hold shared_ptrs to each other. What happens, and how do you fix it?
  • How would you track down a slow memory leak in a long-running process?

Implement a queue data structure using stacks.

Medium · Coding / Algorithms

Approach

  1. Use two stacks, an in-stack and an out-stack. enqueue pushes onto the in-stack. dequeue and front first check the out-stack; if it is empty, pop every element from the in-stack onto it, which reverses the order to FIFO, then take its top.
  2. Complexity: each element is pushed and popped at most twice in total, so every operation is amortised O(1), with a worst case of O(n) for the single dequeue that triggers a transfer. Space is O(n).
  3. Mind std::stack's API: pop() returns void and top() on an empty stack is undefined behaviour. Read top() and then pop(), and check empty() on both stacks first. Report an empty queue by throwing, by returning std::optional<T>, or by an assert, and say which you chose.
  4. Mention the simpler alternative and why it loses: moving everything on each enqueue keeps the out-stack ordered but makes enqueue O(n).
  5. Test with interleaved operations (enqueue 1, 2; dequeue; enqueue 3; dequeue; dequeue) to show that elements still in the out-stack are served before newer ones.

Follow-up

  • Now implement a stack using queues. What does each operation cost?
  • Can every operation be worst-case O(1) instead of amortised?
  • How would you make this queue safe to use from a producer thread and a consumer thread?

Solve a problem involving trees or maps.

Medium · Coding / Algorithms

Approach

  1. Clarify which structure the problem needs before coding: a binary tree or BST question (traversal, height, validation, lowest common ancestor) or a map question (counting, grouping, complement lookup).
  2. For trees, write recursive DFS that returns a value per subtree, for example height = 1 + max(left, right). Validate a BST by passing min and max bounds down, not by comparing only parent and child. Use BFS with a std::queue for level-order output. Recursion uses O(h) stack, and a skewed tree has h = n, so offer an iterative version with an explicit stack.
  3. For maps, choose deliberately. std::map is a balanced tree (red-black in common implementations) with O(log n) operations, ordered iteration and lower_bound. std::unordered_map is a hash table with average O(1) and worst case O(n), no ordering, and a rehash invalidates its iterators.
  4. Watch the C++ trap: map[key] inserts a default value when the key is missing. When only reading, use find, count or contains (C++20).
  5. State the final time and space, and test edge cases: an empty tree or empty input, a single node, duplicate keys, negative values, and a fully skewed tree.

Follow-up

  • Why is checking each node only against its direct children not enough to validate a BST?
  • When would you choose std::map over std::unordered_map even though lookups are slower?
  • Write an in-order traversal without recursion.

Write a function to detect cycles in a linked list.

Medium · Coding / Algorithms

Approach

  1. Use Floyd's tortoise and hare. The slow pointer moves one node and the fast pointer two. If fast or fast->next becomes null there is no cycle; if they meet, there is one. Time O(n), extra space O(1).
  2. Guard every advance: check fast != nullptr && fast->next != nullptr before setting fast = fast->next->next, or a list with an odd or even number of nodes will dereference null.
  3. To find where the cycle starts, reset one pointer to the head after the meeting and advance both by one step; they meet at the cycle's first node. To get the cycle length, hold one pointer still and count steps until the other returns to it.
  4. Alternative: store visited node pointers in an unordered_set. It is also O(n) time but O(n) space. Marking or rewiring nodes also works but destroys the list, so say why you would avoid it.
  5. Test edge cases: an empty list, a single node without a cycle, a single node pointing to itself, and a cycle that loops back to the head.

Follow-up

  • Prove why moving one pointer back to the head finds the cycle's entry node.
  • Remove the cycle so the list ends in null.
  • How would you detect a cycle in a directed graph instead of a linked list?

Optimize a given algorithm for better performance.

Medium · Coding / Algorithms

Approach

  1. Measure before changing anything: state the current complexity, then find the actual hot spot with a profiler (perf, Visual Studio Profiler, VTune) on representative input sizes rather than guessing.
  2. Fix the algorithm first, since it changes the growth rate. Replace a nested-loop O(n^2) search with a hash set lookup (O(n) average) or sort plus two pointers (O(n log n)). Precompute prefix sums so range-sum queries cost O(1) after O(n) setup. Memoise repeated subproblems.
  3. Then constant factors in C++: call reserve on vectors, pass by const reference and move instead of copying, keep data contiguous for cache locality instead of node-based structures, and take allocations out of hot loops.
  4. State the trade-off you made (extra memory for a lookup table, or preprocessing time for faster queries) and whether it holds at the input sizes that matter.
  5. Prove the change is correct and faster: keep the slow version as an oracle in randomised tests, and benchmark before and after with the same inputs and a build with optimisations on.

Follow-up

  • Memory is tight and you can't afford the hash table. What do you trade instead?
  • How do you show the speed-up is real and not measurement noise?
  • When can an O(n log n) approach beat an O(n) one in practice?

Explain your approach to solving a specific coding challenge on Leetcode.

Medium · Coding / Algorithms

Approach

  1. Have one problem ready that has a non-obvious step, such as an LRU cache: an unordered_map from key to std::list iterator plus a doubly linked list in recency order, with list::splice moving an entry to the front in O(1), so get and put are O(1).
  2. Tell it in a fixed order: restate the problem and constraints, give the brute force and its complexity, name the insight that removes the repeated work, then give the final complexity in time and space.
  3. Justify each data structure by the operation it makes cheap (O(1) lookup, O(1) reordering, ordered iteration), not by habit.
  4. Mention one wrong turn and the counterexample that exposed it. Then list the edge cases you tested: capacity 1, updating an existing key, and evicting at exactly full capacity.
  5. Be ready to write the core in C++ on the spot. A story about a problem you cannot reproduce cleanly costs more than it earns.

Follow-up

  • What changes if the input arrives as a stream you can't store?
  • State the invariant that makes your solution correct.
  • Can you cut the memory use, and what does that cost in time?

Replace offset paging on the resource feed with keyset

Easy · keyset pagination · composite index · sort order

resource holds resource_id, tenant_id, owner_user_id, title, body_ref, version, status ('draft','active','archived','deleted'), created_at, updated_at, deleted_at, with an index on (tenant_id, status, updated_at DESC, resource_id DESC). The listing endpoint returns active resources for one tenant, newest update first, 50 per page, today with LIMIT 50 OFFSET n. Tenants reach page 400 and rows are created while they read. Write the keyset query, define what the cursor carries and how it is encoded, and say which part of the index each predicate uses. Assume PostgreSQL 16.

Approach

  1. Name the two failures separately. OFFSET 20000 makes the server produce and discard 20,000 rows, so page cost grows with depth rather than with page size. Independently, any write that changes how many rows sort above the offset moves the window between two fetches, and the direction decides which anomaly you get: an insert lands at the head of updated_at DESC and pushes already-returned rows down past the boundary, so they are returned a second time; a delete above the offset, or a row whose updated_at is bumped above the cursor, pulls rows up and one is never returned at all. Nothing in the response reveals either.
  2. Write the seek: WHERE tenant_id = $1 AND status = 'active' AND (updated_at, resource_id) < ($2, $3) ORDER BY updated_at DESC, resource_id DESC LIMIT 50. The row-value comparison is one index range rather than a disjunction, and both columns are NOT NULL, which is what makes that comparison well defined.
  3. Map each predicate onto the index: tenant_id and status are equality on the leading columns, (updated_at, resource_id) is the range, and the ORDER BY matches the index order so no Sort node appears and the scan stops after 50 rows. The DESC in the definition only matters for mixed directions — a plain ascending btree on the same columns is read backwards for this query.
  4. Put both sort columns in the cursor and nothing the client can tamper with into another tenant: base64 of (updated_at, resource_id), validated server-side, with tenant_id taken from the principal.

Follow-up

  • The client asks for 'jump to page 400'. What do you offer instead, and what does the honest version cost?
  • Sort order becomes user-selectable across four columns. How many indexes is that, and which would you refuse to add?

Find version gaps and relay lag with window functions

Medium · window functions · event tables · frame semantics

outbox_event holds event_id, aggregate_type, aggregate_id, aggregate_version, event_type, payload, status ('pending','published','dead'), attempts, created_at, published_at. A projection is missing rows and you must decide whether the relay skipped events or the consumer dropped them. Write three queries over the last seven days: one listing every aggregate_id whose published aggregate_version sequence has a hole, one giving per-day counts with a running total, and one returning the newest published event per aggregate. For each, say where the window function is evaluated relative to WHERE and LIMIT. PostgreSQL 16.

Approach

  1. Gaps: compute lead(aggregate_version) OVER (PARTITION BY aggregate_id ORDER BY aggregate_version) in a subquery, then filter next_version <> aggregate_version + 1 in the outer query. Window functions are evaluated after WHERE, GROUP BY and HAVING and before the outer ORDER BY and LIMIT, so the predicate cannot sit in the same WHERE clause and PostgreSQL 16 has no QUALIFY.
  2. Say what the seven-day filter does to the answer: it truncates every partition, so the first row per aggregate has no predecessor inside the window and a hole spanning the boundary is invisible. Widen the window, or join to resource.version as the authority for the true maximum.
  3. Running total: SELECT date_trunc('day', created_at) AS d, count() AS n, sum(count()) OVER (ORDER BY date_trunc('day', created_at) ROWS UNBOUNDED PRECEDING). An aggregate inside a window call is legal because grouping runs before windowing. The grouping key is unique per row here so ROWS and RANGE agree, but write the frame anyway — over ungrouped rows with tied timestamps the default RANGE frame pulls in every peer row and the total jumps.
  4. Newest per aggregate: DISTINCT ON (aggregate_id) ... ORDER BY aggregate_id, aggregate_version DESC is the cheap PostgreSQL-only form when an index matches that order; row_number() OVER (PARTITION BY aggregate_id ORDER BY aggregate_version DESC) = 1 is the portable form and needs a subquery for the same evaluation-order reason as the gap query.

Follow-up

  • Relay failover redelivers events. Does a duplicate break the gap query, and how would you detect one from this table alone?
  • Turn the gap check into a continuous monitor rather than a query someone runs after an incident. What does it watch?

What are the key differences between C++ and Java?

Medium · Technical / Domain Questions

Approach

  1. Start with execution: C++ compiles ahead of time to native code for one target platform, while Java compiles to bytecode that the JVM interprets and JIT-compiles at run time, so the same build runs anywhere a JVM exists.
  2. Memory: in C++ an object's lifetime is deterministic. Stack objects die at scope exit and destructors run immediately, which is what makes RAII work. Java reclaims heap objects with a garbage collector, and non-memory resources are closed with try-with-resources, not destructors.
  3. Object model: C++ has value semantics, pointers and references, multiple inheritance of classes, and functions are virtual only when declared so. In Java every class-type variable is a reference, a class has single inheritance plus multiple interfaces, and instance methods are virtual by default.
  4. Generics: C++ templates are instantiated at compile time for each type, which costs code size and can run faster. Java generics use type erasure, so List<Integer> and List<String> share one runtime class and primitives must be boxed.
  5. Safety trade-off: out-of-bounds access or a dangling pointer is undefined behaviour in C++, whereas Java throws ArrayIndexOutOfBoundsException or NullPointerException. For latency-sensitive trading code, say why GC pauses and deterministic destruction matter to the choice.

Follow-up

  • How do C++ templates and Java generics differ once the program is running?
  • What does Java offer in place of a destructor for releasing a file handle or socket?
  • For a new service, when would you pick Java over C++?

Describe a project where you utilized MFC. What challenges did you face?

Medium · Technical / Domain Questions

Approach

  1. Pick one real application and give its shape quickly: a document/view or dialog-based app, built from CWinApp, a CFrameWnd or CDialog, and message maps (BEGIN_MESSAGE_MAP, ON_COMMAND, ON_WM_PAINT) that route Windows messages to handlers. Use DDX/DDV and UpdateData to move data between controls and members.
  2. Threading challenge: MFC window objects are not safe to use from a thread other than the one that created them. Run long work in a worker started with AfxBeginThread and pass results back by PostMessage with a custom message (WM_APP + n), with the HWND rather than a CWnd* crossing threads, so the UI never freezes.
  3. Painting challenge: to fix flicker, draw into a memory DC (CreateCompatibleDC plus a compatible bitmap), BitBlt it once in OnPaint, and return TRUE from OnEraseBkgnd. To stop GDI leaks, restore the old object after SelectObject and free the objects you created.
  4. Build and maintenance issues worth naming if you hit them: Unicode vs MBCS builds (CString, _T macros), mixing MFC with newer C++ or STL code, and keeping UI logic out of the view so it can be tested.
  5. Close with the outcome and what you would do again. If your MFC exposure is thin, say so plainly and map the same problems from Win32 or another GUI framework you have used.

Follow-up

  • Why not update a list control directly from the worker thread?
  • How does a WM_COMMAND from a menu item reach your handler through the message map?
  • If you had to modernise this UI, would you replace MFC or wrap it, and why?

Discuss the principles of Object-Oriented Programming (OOP).

Medium · Technical / Domain Questions

Approach

  1. Cover the four principles with a C++ example each. Encapsulation: private members guarded by methods that keep an invariant. Abstraction: an abstract base class with pure virtual functions. Inheritance: deriving to reuse an interface. Polymorphism: calling through a base reference or pointer.
  2. Explain runtime polymorphism mechanically: virtual functions dispatch through a vtable pointer in each object, which costs an indirect call and usually blocks inlining. A base class deleted through a base pointer needs a virtual destructor, or the behaviour is undefined.
  3. Contrast compile-time polymorphism: function overloading and templates resolve at compile time with no vtable cost, at the price of code size and longer builds.
  4. Show design judgement: prefer composition over inheritance when the relation is 'has-a', keep to Liskov substitution (the Square deriving from Rectangle problem), and use SOLID only where it explains a concrete choice.
  5. Name the C++ pitfalls: object slicing when a derived object is passed by value as its base, and a virtual call inside a constructor or destructor dispatching to the base version, not the derived one.

Follow-up

  • What goes wrong if a polymorphic base class has a non-virtual destructor?
  • Give a case where you replaced inheritance with composition and say what it fixed.
  • What does a virtual call cost compared with a direct call?

How would you approach designing a new feature for our trading platform?

Medium · Problem-Solving / Case Studies

Approach

  1. Clarify scope before designing: who uses the feature (traders, risk or operations staff), what it must do, and the non-functional targets: latency, correctness, auditability and availability during market hours. Write these down as the requirements you will design against.
  2. Pick a concrete example, such as a pre-trade limit check on order entry or a price alert, and lay out the components: the client or API entry point, a validation and risk-check step, persistence, and an event stream for downstream consumers. Show how one request moves through them.
  3. Get correctness details right: represent prices and quantities as integer ticks or a decimal type, not binary floating point. Give each order a client-generated idempotency key so a retry cannot create a duplicate. Keep an append-only audit log with timestamps.
  4. Name the trade-offs: a synchronous risk check adds latency to every order but blocks bad orders before they leave; an asynchronous check is faster but only catches problems afterwards. Say which you would choose and why.
  5. Plan the rollout: test against replayed recorded market data, put the feature behind a flag, monitor error rates and latency, and keep a rollback path.

Follow-up

  • The client times out and resends the order. How do you guarantee it executes only once?
  • How would you test this feature without touching a live market?
  • What should happen if the risk service is unavailable: fail open or fail closed?

Given a set of constraints, how would you optimize our existing system?

Medium · Problem-Solving / Case Studies

Approach

  1. Turn the constraints into numbers first: which metric matters (p99 latency, throughput, memory, cost), its current value and target, and what cannot change (the public API, the hardware, the deadline).
  2. Establish a baseline and find the bottleneck by measurement: CPU profile, I/O wait, lock contention, allocation rate and network calls. Use Amdahl's law to size the gain: if a fraction p of the time speeds up by a factor s, the overall speed-up is 1 / ((1 - p) + p / s).
  3. Match the fix to the bottleneck: caching (with an explicit invalidation rule), batching small calls, adding an index or rewriting a slow query, moving work off the request path through a queue, splitting a hot lock into finer-grained locks, or changing a data structure.
  4. Validate under load: compare p50 and p99 before and after with the same traffic shape, release through a canary, and keep the old path available for rollback.
  5. Weigh the gain against maintainability: say which optimisations add complexity the team must carry, and stop once the target is met.

Follow-up

  • You add a cache. How do you keep it consistent with the source of truth?
  • Median latency is fine but p99 is bad. Where do you look first?
  • Which part of the system would you deliberately leave unchanged, and why?

Read latency spikes on a sixty-second sawtooth

Medium · cache stampede · single-flight · ttl

The cached listing read path serves about 14k reads/second at an 85% hit rate. p99 sits at 35 ms for 57 seconds, jumps to 900 ms for 3, and repeats. During each spike the primary shows several hundred identical listing queries starting within the same millisecond, all carrying one large tenant's id. Cache entries use a 60-second TTL. Give the mechanism, the ordered checks, the fix, and the correctness hazard your fix must not introduce.

Approach

  1. Match the period to a configured number before theorising about load. A spike every 60 seconds against a 60-second TTL is an entry expiring, and you confirm it by correlating spike timestamps with the entry's write time rather than with the traffic curve. If the period had matched a cron or a GC interval instead, this is a different investigation.
  2. Establish the concurrency of the miss. Several hundred identical queries in one millisecond means the miss path has no coalescing: every request that arrives between expiry and repopulation recomputes. The herd size is that key's arrival rate times its recompute time, so at 1.2k reads/second for the hot key and a 250 ms recompute you expect about 300 concurrent misses, which matches what is observed.
  3. Add single-flight on the miss path so one caller per key recomputes under a short-lived lock while the rest wait for its result. Prefer stale-while-revalidate where the read tolerates it: return the expired value immediately and refresh asynchronously, which removes the latency spike rather than serialising it into a queue of waiters.
  4. De-synchronise the keys. Write TTLs with jitter, for example 60 seconds plus or minus 10%, so a deploy or a mass invalidation does not align every key on the same second and turn a per-key herd into a fleet-wide one.

Follow-up

  • The same sawtooth appears on a key that is invalidated on write rather than expired. Is that the same bug?
  • How does your answer change if the recompute takes 4 seconds instead of 250 ms?

Suggested practice checklist

Seven days built from the three reported rounds and the topics candidates report most: C++ fundamentals, MFC, algorithmic complexity and the queue data structure, then case studies and behavioral stories. Each day ends with something you can check.

Day 1 — Map the loop and inventory your projects

  • Write out the three reported rounds (Initial Screening, Technical Interviews, Project Discussion) and what each is described as covering
  • Rate yourself from one to five on each listed requirement: C++, MFC, algorithms and data structures, agile practice
  • Pick two or three projects for the Project Discussion and note the MFC or Visual C++ one

Deliverable: A one-page loop map, a requirement self-rating, and a shortlist of projects with the weakest area marked.

Day 2 — C++ against Java and memory management

  • Write a comparison across memory model, compilation and runtime, object model, templates against generics, and undefined behavior
  • Write a short C++ program using std::unique_ptr, then reproduce a leak and a double free with raw pointers and fix them
  • Answer both questions aloud and record yourself

Deliverable: A written comparison, a compiling RAII program with its two bug versions, and two recorded spoken answers.

Day 3 — OOP, MFC and multithreading

  • Write each OOP principle with a C++ example, including virtual functions and virtual destructors
  • Write your MFC project story with its challenges, or the nearest GUI or event-driven project if you have none
  • Write a bounded producer-consumer in C++ using std::mutex and std::condition_variable, with a clean shutdown

Deliverable: An OOP one-pager, a written MFC story, and a working producer-consumer program with a spoken explanation of its locking.

Day 4 — Coding: queue from stacks, cycles, trees and maps

  • Implement a queue from two stacks in C++ with push, pop, peek and empty, and write the amortised argument
  • Implement linked-list cycle detection with a hash set and with Floyd's pointers, and test empty, single-node and self-loop lists
  • Solve one tree problem and one map problem in C++ and state complexity for each

Deliverable: Compiling C++ files for all three problems with test cases and written complexity.

Day 5 — Optimizing and explaining solutions

  • Take a brute-force solution, find its bottleneck, improve it, and write complexity before and after
  • Explain a LeetCode-style solution aloud in stages: restate, brute force, insight, code, complexity, edge cases

Deliverable: Before-and-after notes for the optimization, a recorded solution explanation, and a written diagnosis for the practice exercise.

Day 6 — Case studies and the performance trade-off

  • Outline a design for a new feature you choose for a trading or risk application, such as an alert when exposure passes a limit, covering data, failure cases and testing
  • Write how you would optimize an existing system given three constraints you invent, ranking what you would measure first
  • Write your performance-versus-maintainability story with the measurement behind it

Deliverable: A one-page feature design, a ranked optimization plan, and a written trade-off story.

Day 7 — Behavioral stories and a full mock

  • Write structured notes for the challenge, prioritization, difficult-teammate and communication prompts, each with a decision and a result
  • Write a specific answer to what motivates you to stay in software engineering
  • Run a mock: a short CV pitch, one coding problem in C++, then a walk through one project, and record it

Deliverable: A story sheet covering five prompts and a recorded mock with notes on what to fix.

Behavioral and project-story preparation

The behavioral prompts reported for this loop ask about challenges, prioritization, working with a difficult teammate, motivation, team communication and a performance versus maintainability trade-off. Combined with a Project Discussion round, the strongest preparation is a small set of real project stories, each showing a decision you made, the evidence you used and the result. Prepare the multithreading question as a story from your own work too.

How do you handle multithreading in your applications?

Medium · Technical / Domain Questions

Approach

  1. Anchor the answer in one real application: name the shared state, which threads touched it (for example a UI thread, worker threads, an I/O thread), and the primitive you chose for each piece, such as std::mutex with std::lock_guard, std::atomic for a single counter or flag, or std::condition_variable for hand-off.
  2. Cover the producer-consumer pattern: a bounded queue guarded by a mutex, with a condition variable waited on using a predicate so spurious wakeups and lost signals are handled. Say how shutdown works (a stop flag or sentinel, then join).
  3. State how you prevent deadlock: a fixed lock order, std::scoped_lock when two mutexes are needed, short critical sections, and no callbacks or blocking I/O while holding a lock. Mention data races as the other failure and how you found them (ThreadSanitizer, stress tests, thread ids in logs).
  4. If the project used MFC, say that window and control objects belong to the thread that created them, so a worker thread hands results to the UI thread by posting a message instead of touching controls directly, and that you used AfxBeginThread or std::thread for the background work.
  5. Show judgement: say where you avoided threads or used a thread pool to keep the design simple, and what you measured to justify the concurrency.

Follow-up

  • When is std::atomic enough, and when do you need a mutex?
  • How would you diagnose a deadlock that only appears in production?
  • What goes wrong if a worker thread updates a UI control directly?

Describe a time when you faced a significant challenge in a project. How did you overcome it?

Medium · Behavioral / Leadership

Approach

  1. Choose a technical challenge you personally resolved, ideally from a project you can also discuss in the Project Discussion. State the situation in two sentences: what the system did, what broke or was impossible, and what the constraint was.
  2. Spend most of the answer on how you diagnosed it: the symptoms, the hypotheses you ruled out, the evidence (logs, profiler output, a minimal reproduction) that pointed to the cause.
  3. Name the options you considered and why you picked one, including the cost you accepted. Then give the result with a measurable before and after, such as error rate, latency or build time, only if you actually have the figures.
  4. Close with what you would do differently and what you changed afterwards (a test, an alert, a review checklist) so the problem would not return.
  5. Avoid a story whose challenge was other people being late or wrong, and avoid describing only the long hours you worked.

Follow-up

  • Which alternatives did you reject, and why?
  • How did you confirm the fix addressed the root cause rather than the symptom?
  • What would you do differently if you met the same problem again?

How do you prioritize tasks when working on multiple projects?

Medium · Behavioral / Leadership

Approach

  1. Give a method, then prove it with one example. A workable method: list every item with its deadline, who is blocked by it, and the cost of delay; rank by impact and urgency; check dependencies; then tell each stakeholder what moves and when.
  2. Use a concrete case with real numbers, such as a production defect arriving mid-sprint while a feature deadline was fixed. Say what you put first, what you deferred, and who agreed to the deferral.
  3. Show that you renegotiate scope early instead of working silently on everything: you raised the conflict, offered options (cut scope, move the date, add help) and got a decision.
  4. Mention how you keep the plan visible, for example a short written list or a sprint board that teammates and product managers can read, and how you re-rank when priorities change.
  5. Avoid answering with only a tool or a slogan; the story of a decision with a cost is what makes the answer believable.

Follow-up

  • Two managers each say their request is the urgent one. What do you do?
  • Tell me about a time a priority changed halfway through a sprint.
  • What did you drop, and how did you tell the people affected?

Can you discuss a situation where you had to collaborate with a difficult team member?

Medium · Behavioral / Leadership

Approach

  1. Pick a conflict with a real technical or working-style disagreement, not a personality dispute you cannot explain. Describe the other person's position fairly, because the answer is judged partly on whether you can.
  2. Show what you did first: a direct, private conversation to understand their constraints, rather than escalating or routing around them. Say what you learned about why they behaved that way.
  3. Move the disagreement onto evidence: a short design note, a benchmark, a prototype or a code-review thread, so the decision rested on facts. Say what was decided and who decided.
  4. Report the outcome and the working relationship afterwards, and one thing you changed in your own behavior.
  5. The mistake that sinks this answer is blaming the other person or describing how you avoided them. If it needed escalation, say when you chose to escalate and why.

Follow-up

  • What would you have done if the conversation had not worked?
  • How did your working relationship with that person look afterwards?
  • What did this teach you about your own part in the conflict?

What motivates you to stay in the software engineering field?

Medium · Behavioral / Leadership

Approach

  1. Give a specific, true answer instead of a general love of problems. Tie it to moments in the craft: finding the cause of a slow routine with a profiler, understanding why a memory bug appeared only under load, or shipping something that other people used.
  2. Back it with evidence that the motivation is current: something you learned or built in the last year, such as reading about the C++ memory model, a side project or a hard review you learned from.
  3. Connect it to this role using only what candidates report: C++ work, algorithms and data structures, and software that supports trading and risk management. Say what about that kind of work you want, without claiming knowledge of the company's internal projects.
  4. Keep it short and personal, and include what would make the work less motivating, which shows you have thought about it honestly rather than recited a script.

Follow-up

  • What have you learned recently that changed how you write code?
  • What would make you consider leaving software engineering?
  • Why does this particular role fit what you just described?

How do you ensure effective communication with your team?

Medium · Behavioral / Leadership

Approach

  1. Describe the mechanisms you use instead of saying you communicate well: a short written design note before a large change, status updates that state the blocker, the ask and the date, clear code-review comments, and a stand-up habit if your team works in agile cycles.
  2. Adapt to the audience. The role is described as working with product managers and data analysts as well as engineers, so give an example where you explained a technical trade-off in terms of effect on delivery, risk or results for a non-engineer.
  3. Include one story where communication failed (a misunderstood requirement that caused rework) and the practice you introduced afterwards, such as restating the requirement in writing or agreeing acceptance examples before coding.
  4. Mention how you share bad news: early, with the cause, the options and your recommendation, so no one finds out from a missed date.
  5. Avoid a list of tools. Name what you did and what changed because of it.

Follow-up

  • Tell me about a misunderstanding that caused rework and how you prevented a repeat.
  • How do you explain a technical trade-off to someone who is not an engineer?
  • How do you tell a stakeholder that a deadline will slip?

Discuss a time when you had to make a trade-off between performance and maintainability.

Medium · Problem-Solving / Case Studies

Approach

  1. Tell a story in which you measured before changing anything: a profiler or benchmark showed the hot path, so only that code was made harder to read. Give the figure that justified the decision, such as the share of runtime or the latency target that was missed.
  2. Make the trade-off concrete, for example replacing a node-based std::map with a sorted vector or a pooled allocator in an inner loop, or batching calls, and say what readability or flexibility you gave up.
  3. Show how you contained the cost: the optimized code sat behind a small interface, carried a comment explaining why, and had tests and a benchmark so teammates could change it safely.
  4. Say what you tried first. Often an algorithmic change (a better data structure, avoiding repeated work) gives more than micro-optimization and costs less in clarity.
  5. The answer fails if you optimized without measuring, or if you claim maintainability always wins. Show you can argue for either side with evidence.

Follow-up

  • How did you decide performance was good enough to stop?
  • How did you keep the optimized code safe for teammates to change?
  • Did you ever revisit or reverse that decision?