cswork / libraryOpen practice workspace

Interview guides · Afiniti

Afiniti Software Engineer Interview Guide

Afiniti Software Engineer interview guide: four rounds, SQL, memory and concurrency, probability puzzles, behavioural prompts and a 7-day prep plan.

The 60-second brief

Afiniti's core business, as candidates describe it, is AI-based routing of interactions between enterprises and their customers. A Software Engineer there builds and maintains the infrastructure behind that routing. The work is reported to involve large volumes of data, analytics and decisions made in near real time. Day to day that means writing and tuning code, finding bottlenecks in SQL queries and memory use, fixing production issues, joining architecture discussions, and turning business requirements into technical specifications, often alongside data scientists and product managers.

This guide is for candidates interviewing for a Software Engineer position at Afiniti. It covers the four reported rounds, the computer science fundamentals (heap and stack, shared memory and concurrency, SOLID, design patterns, cohesion and coupling), SQL and database modelling, probability and logic puzzles, the system design and coding areas candidates describe, and the behavioural prompts. The role lists C++, Python or Node.js as the main language, so practise your code in whichever of those you know best.

Candidates report 4 rounds over roughly 3-5 weeks: an Initial Screening Call, Technical Assessments, Deep-Dive Technical Discussions, and IQ and Personality Assessments.

Focus areas

  • SQL
  • Python
  • Node.js

Overview

The work described for this role is infrastructure that handles a lot of data quickly, and performance tuning is named explicitly: slow SQL and heavy memory use are the two bottlenecks candidates mention. That shapes the reported questions. You are asked to contrast heap and stack allocation, explain shared memory and concurrency, write a query for the second-highest salary, and say how you would speed up a slow statement. Prepare each of these with a concrete example in your own language, not a textbook definition.

The reported questions fall into four groups. Computer science and design: heap versus stack, shared memory, SOLID, design patterns, cohesion versus coupling. Data and SQL: joining two tables, second-highest salary, tuning a slow statement, and modelling a movie theater or a social media retweet system. Statistics and puzzles: the shooting-star probability question, the Central Limit Theorem, Bayes Theorem and the classic 8-Ball puzzle. Behavioural: pivoting after a business change, tight deadlines and extra hours, debugging in production, and explaining technical decisions to non-technical stakeholders.

Candidates also describe broader areas. On the design side, they mention multi-tenant systems with many concurrent users, distributed state consistency, MongoDB versus PostgreSQL trade-offs, and message queues with background processing. On the coding side, they mention linked lists, trees and hash maps, debugging a snippet for memory leaks, and a thread-safe queue in C++, Python or Node.js. Expect to write code in a shared editor or on a whiteboard while explaining it.

Candidates also report that anything listed on your resume can be probed down to its internals, and that probability questions come up even when the role is not a data science one. Spend your time on three things: language-level fundamentals you can demonstrate in code, SQL you can write and tune from memory, and probability you can work through aloud. Then keep two or three behavioural stories ready.

The Afiniti interview process

Candidates describe 4 stages over roughly 3-5 weeks. Treat them as reported experience, not a published schedule, and expect team and seniority to change the detail.

Initial Screening Call

Candidates report a call with a recruiter or hiring manager about your background. It is the first conversation, and since candidates also report deep questions on anything listed on a resume, the way you describe your projects here sets up the later technical stages. Approach it as a short, structured account of your work rather than a recital of job titles.

What to demonstrate

  • How clearly you summarise your background and the projects you list
  • Whether your main language (C++, Python or Node.js) and SQL experience match the role's listed requirements
  • How you explain what you built and why you chose it

How to prepare

  • Write a one-paragraph career summary and a short list of projects, each with the language, data store and one decision you made and its trade-off.
  • For every technology on your resume, be able to explain one internal detail, such as how your language manages memory or threads.
  • Decide which of C++, Python or Node.js you will code in, and say so early.

Technical Assessments

Candidates report coding challenges, SQL queries and technical discussions in this stage. It is where the coding and SQL question types are practised: data structures such as linked lists, trees and hash maps, SQL like joins, the second-highest salary and tuning, and fundamentals such as SOLID and design patterns. Write working code and narrate your choices as you go.

What to demonstrate

  • Working code in your chosen language, with the reasoning behind your data structure choices
  • SQL you can write and optimise, including joins and ranking
  • Core OOP and design vocabulary backed by examples

How to prepare

  • Write the second-highest salary query three ways and test each with duplicate salaries and a one-row table.
  • Implement a thread-safe queue in your language and explain what protects the shared state.
  • Prepare one before-and-after code example for each SOLID principle and one design pattern you have used in real code.

Deep-Dive Technical Discussions

Candidates report discussions with senior engineers or department heads aimed at technical expertise. The architecture topics candidates mention are multi-tenant systems, distributed state consistency, database technology trade-offs such as MongoDB versus PostgreSQL, and queues with background processing. Prepare to justify choices, name what each choice costs, and defend them when challenged.

What to demonstrate

  • Depth of technical expertise across systems and databases
  • Reasoning about trade-offs in multi-tenant and high-concurrency designs
  • How you defend an architectural decision

How to prepare

  • Outline a multi-tenant design: how tenants are isolated in the data model, where queues take background work, and what happens under heavy load.
  • Write a one-page comparison of MongoDB and PostgreSQL for a relational workload with heavy reporting queries.
  • Practise explaining cohesion versus coupling with a module you refactored, and model a movie theater booking or retweet schema from scratch.

IQ and Personality Assessments

Candidates report that this final stage may include tests of raw intelligence and cultural alignment. Candidates also describe IQ-style puzzles and probability problems, though the format of this stage is not described in detail. Prepare by practising puzzle and probability reasoning on paper, and answer any personality questions honestly and consistently with the stories you have told elsewhere.

What to demonstrate

  • Logical and probabilistic reasoning on puzzle-style problems
  • Consistency between your personality responses and the way you describe your working style
  • Clear reasoning when a problem is unfamiliar

How to prepare

  • Work the shooting-star probability question two ways and check that the answers agree.
  • Practise explaining Bayes Theorem and the Central Limit Theorem with a concrete example each.
  • Work through the 8-Ball puzzle on paper, writing your reasoning at each step.

Where candidates lose points

Reciting SOLID and design patterns as definitions with no code

For each SOLID principle, keep a short snippet that breaks it and a fixed version in your own language, and name one pattern you used and the problem it solved.

Writing a second-highest salary query that fails on duplicates or tiny tables

Ask whether tied salaries count once, then use DENSE_RANK or a distinct subquery, and say what it returns when fewer than two distinct salaries exist. Test it on ties and a single row.

Answering the slow-SQL question with only "add an index"

Describe your sequence: read the query plan, check the row estimates and join order, look for functions on filtered columns and unneeded columns, then choose an index, and confirm the plan changed.

Treating the shooting-star question as 15% times four, or applying Bayes Theorem without naming the prior

State the independence assumption, compute the complement (1 minus 0.85 to the fourth power, about 0.48), and for Bayes write the prior, likelihood and evidence before substituting numbers.

Listing C++, Python or Node.js on your resume but being unable to explain memory or threading in it

For your main language, prepare how allocation works, how threads or the event loop behave, and what protects shared state in a thread-safe queue.

Questions & practice

What are the differences between heap and stack memory allocation?

Medium · Technical & Domain Knowledge

Approach

  1. Start with lifetime and ownership. Each thread gets its own call stack: a frame is pushed on call and popped on return, so allocation is a pointer bump and the memory is freed automatically when the scope ends. Heap memory (malloc/new) lives until something frees it, whatever scope created it.
  2. Compare cost and limits. A stack allocation is O(1) with good cache locality, but the stack size is fixed when the thread is created, so deep recursion or a large local array overflows it. Heap allocation goes through an allocator (free lists, size classes), is slower, can fragment, and is shared by all threads, so allocators need locks or per-thread caches.
  3. Show the C++ failure modes: returning a pointer to a local gives a dangling pointer, and a missing delete leaks. Tie heap lifetime to a stack object with RAII (std::unique_ptr, std::vector) so the destructor frees it.
  4. Cover managed runtimes. In CPython and V8 (Node.js) objects live on the heap and the stack frames hold references; CPython frees by reference counting plus a cycle collector, V8 by a generational GC. Go uses escape analysis to decide stack or heap; the JVM JIT can remove allocations for objects that never escape.

Follow-up

  • Why can a recursive function crash with a stack overflow when the iterative version of the same logic runs fine?
  • How would you find a memory leak in a long-running C++ service (Valgrind, AddressSanitizer/LeakSanitizer, heap profiling)?
  • Allocation in a hot loop is slowing a service down. How would you reduce it (reserve, object pools, arenas)?

Can you explain the concept of shared memory and how it relates to concurrency?

Medium · Technical & Domain Knowledge

Approach

  1. Define the scope. Threads in one process share the address space (heap, globals, open files) but each has its own stack and registers. Separate processes share memory only through an explicit mapping such as shm_open plus mmap, or Python's multiprocessing.shared_memory.
  2. Name the hazard. A data race happens when two threads touch the same location without synchronization and at least one of them writes; in C++ that is undefined behaviour. counter++ is a read-modify-write, so two threads can both read 5 and both write 6.
  3. List the tools and their trade-offs: a mutex for critical sections, a read-write lock for read-heavy data, condition variables to wait for state changes, and atomics/compare-and-swap for single words. Memory ordering (happens-before) decides when one thread's write becomes visible to another. Acquire locks in a fixed global order to avoid deadlock.
  4. Cover the listed languages. CPython's GIL lets only one thread run bytecode at a time (in the default build), but counter += 1 is still not atomic across threads, so a Lock is still needed. Node.js runs JavaScript on one event-loop thread; worker_threads share memory only through SharedArrayBuffer and the Atomics API.
  5. Offer the alternative: message passing (queues, actors) or immutable data avoids shared mutable state. A thread-safe queue is the usual bridge: a mutex plus not_empty/not_full condition variables, with waits inside a while loop to handle spurious wakeups.

Follow-up

  • Implement a thread-safe bounded queue. What happens if you wait on the condition variable with an if instead of a while?
  • What is false sharing, and how would you detect and fix it?
  • Two threads are deadlocked in production. How do you confirm it, and how do you restructure the locking?

If there is a 15% probability of a shooting star appearing in 15 minutes, what is the probability of seeing on

Medium · Statistics & Problem Solving

If there is a 15% probability of a shooting star appearing in 15 minutes, what is the probability of seeing one in an hour?

Approach

  1. State the assumptions: the rate is constant and non-overlapping 15-minute windows are independent. An hour is then four independent windows.
  2. Use the complement. P(no star in 15 minutes) = 0.85, so P(none in an hour) = 0.85^4 = 0.5220 and P(at least one) = 1 - 0.5220 = 0.478, about 47.8%.
  3. Name the trap: adding 4 x 15% = 60% double-counts hours with more than one star, and the sum would pass 100% after seven windows.
  4. Confirm with a Poisson model. 1 - e^(-λ) = 0.15 gives λ = -ln(0.85) ≈ 0.1625 per 15 minutes, so the hourly rate is ≈ 0.650 and 1 - e^(-0.650) ≈ 0.478, the same answer because the Poisson process is memoryless.
  5. Show the inverse: given an hourly probability p, the 15-minute probability is 1 - (1 - p)^(1/4). Saying this shows you understand the model rather than a memorised number.

Follow-up

  • What is the probability of seeing at least one star in 30 minutes?
  • If the chance of a star in an hour is 50%, what is the chance in 10 minutes?
  • Under the Poisson model, what is the expected number of stars in an hour, and what is the probability of exactly one?

Can you explain the Central Limit Theorem and its practical application?

Medium · Statistics & Problem Solving

Approach

  1. State it precisely. For independent, identically distributed variables with finite mean μ and finite variance σ², the distribution of the sample mean approaches Normal(μ, σ²/n) as n grows, whatever the shape of the original distribution. It describes the mean, not the raw data.
  2. Give the working consequence: the standard error is σ/√n, so a 95% confidence interval for a mean is x̄ ± 1.96·s/√n. For a proportion, the standard error is √(p̂(1 - p̂)/n). This is what z-tests and A/B test comparisons rely on.
  3. Point out the √n scaling: halving the margin of error needs four times the sample.
  4. List the caveats. Heavy tails with infinite variance (Cauchy) break it. Strong skew needs a larger n, and n ≥ 30 is only a rule of thumb. Dependent data (time series, many events per user) violates independence and understates the error.
  5. Offer a demonstration: draw many samples of size n from an exponential distribution, plot the histogram of their means, and watch it become bell-shaped as n grows.

Follow-up

  • How does the Central Limit Theorem differ from the law of large numbers?
  • How many samples do you need to estimate a conversion rate to within ±1 percentage point at 95% confidence? (Worst case p = 0.5 gives about 9,604.)
  • Your metric is very heavy-tailed. How would you build a confidence interval (bootstrap, log transform, trimming)?

What is Bayes Theorem, and how would you apply it to a conditional probability problem?

Medium · Statistics & Problem Solving

Approach

  1. Write the formula and its parts: P(A|B) = P(B|A)·P(A) / P(B), where P(A) is the prior, P(B|A) the likelihood, and P(B) the evidence, expanded by total probability as P(B|A)P(A) + P(B|¬A)P(¬A).
  2. Work an example out loud. Prevalence is 1%, sensitivity 99%, false-positive rate 5%. P(disease | positive) = 0.0099 / (0.0099 + 0.0495) = 1/6 ≈ 16.7%.
  3. Check it with natural frequencies. Out of 10,000 people, 100 are sick and 99 of them test positive; 9,900 are healthy and 495 of them test positive. 99 of the 594 positives are sick, which is 1/6.
  4. Name the trap: base-rate neglect, meaning answering with the 99% sensitivity and ignoring the 1% prior.
  5. Connect it to engineering. The posterior becomes the next prior for repeated evidence, and naive Bayes classifiers (spam filters) apply the theorem under the assumption that features are conditionally independent given the class.

Follow-up

  • The same person tests positive a second time, independently. What is the new probability?
  • Solve the Monty Hall problem with Bayes' theorem.
  • Why does naive Bayes often classify well even though its independence assumption is wrong?

How would you approach the classic "8-Ball" logic puzzle?

Medium · Statistics & Problem Solving

Approach

  1. Confirm the version first. The common one is eight identical-looking balls, one of them heavier, and a balance scale: find the heavy ball in the fewest weighings.
  2. Split 3-3-2. Weigh three against three. If they balance, the heavy ball is among the remaining two, and one more weighing of one against one finds it. If one side goes down, take those three and weigh one against one: the heavier side is the ball, and if they balance it is the third.
  3. Prove two is the minimum. Each weighing has three outcomes (left, right, balance), so k weighings separate at most 3^k cases. One weighing gives 3 < 8 and two give 9 ≥ 8. In general, n balls need ceil(log3 n) weighings.
  4. Explain the common mistake: splitting 4-4 throws away the 'balanced' outcome and needs three weighings.
  5. Mention the harder variant: when the odd ball may be heavier or lighter, there are 2n cases, so 12 balls (24 cases ≤ 27) need three weighings and a more careful split.

Follow-up

  • What is the minimum number of weighings for 9 balls? For 27?
  • Twelve balls, one of them heavier or lighter, and three weighings: outline the strategy.
  • How would you write code that simulates your strategy and checks it against every possible heavy ball?

How would you join two tables to display a specific result set?

Medium · Data & SQL Proficiency

Approach

  1. Pin the grain and keys first: what one output row represents, and which column links the tables (for example, orders.customer_id to customers.id).
  2. Pick the join type from which unmatched rows must survive. INNER keeps matches only, LEFT keeps every row of the driving table with NULLs on the right, FULL OUTER keeps both sides. Write an anti-join as LEFT JOIN ... WHERE o.id IS NULL, or NOT EXISTS.
  3. Watch filter placement. A condition on the right table in WHERE removes the NULL rows and quietly turns a LEFT JOIN into an inner join; put it in the ON clause instead. With LEFT JOIN, COUNT(o.id) returns 0 for a customer with no orders, while COUNT(*) returns 1.
  4. Check fan-out before aggregating. Joining a one-to-many table repeats parent rows and inflates SUM, so aggregate the child table in a CTE first and then join, or use COUNT(DISTINCT ...) only where it is correct.
  5. Cover NULL keys and performance. NULL never equals NULL, so those rows never match. NOT IN against a subquery that contains a NULL returns no rows, so prefer NOT EXISTS. Index the join key on the large side and read EXPLAIN to see whether the planner chose a nested loop, hash join or merge join.

Follow-up

  • List the customers who have never placed an order. Write it two different ways.
  • After your join, a revenue total doubled. How do you find out why and fix it?
  • Write a self-join that shows each employee next to their manager's name.

Write a SQL query to find the second-highest salary from an employee table.

Medium · Data & SQL Proficiency

Approach

  1. Clarify first: with ties, does 'second-highest' mean the second distinct salary value? And what should the query return when there is only one distinct salary? The usual answer is NULL.
  2. Subquery version: SELECT MAX(salary) FROM employee WHERE salary < (SELECT MAX(salary) FROM employee). It handles ties and returns NULL naturally when no second value exists. With an index on salary, each MAX can be answered from the index.
  3. Window version: rank with DENSE_RANK() OVER (ORDER BY salary DESC) in a CTE and filter rank = 2. It returns every employee at that salary. ROW_NUMBER picks an arbitrary one among ties, and RANK skips (two people tied at the top push the next salary to rank 3).
  4. LIMIT version: SELECT DISTINCT salary ... ORDER BY salary DESC LIMIT 1 OFFSET 1 returns an empty set, not NULL, so wrap it as a scalar subquery: SELECT (SELECT ...) AS second_highest.
  5. Handle NULL salaries. MAX ignores them, but PostgreSQL sorts NULLs first under DESC (MySQL sorts them last), so add WHERE salary IS NOT NULL to the ORDER BY versions.

Follow-up

  • Generalise it to the Nth-highest salary.
  • Return the second-highest salary in each department.
  • Write it without window functions or LIMIT, using a correlated COUNT(DISTINCT ...).

How would you optimize a slow-performing SQL statement?

Medium · Data & SQL Proficiency

Approach

  1. Measure before changing anything. Get the actual plan with EXPLAIN ANALYZE (add BUFFERS in PostgreSQL), compare estimated and actual row counts, and find the expensive node: a sequential scan on a large table, a nested loop with a large outer side, or a sort or hash spilling to disk.
  2. Fix access paths. Build a composite index with equality columns first and the range column last. Use a covering index (INCLUDE in PostgreSQL) to allow index-only scans. Remember that an index on a low-selectivity column is often ignored.
  3. Make predicates sargable. Wrapping an indexed column in a function (DATE(created_at) = ...), a leading-wildcard LIKE '%x', or an implicit type cast blocks the index. Rewrite these as ranges (created_at >= d AND created_at < d + 1 day) or add an expression index.
  4. Rewrite the query shape. Select only the columns you need, turn correlated subqueries into joins or EXISTS, aggregate before joining a one-to-many table, and replace large OFFSET pagination with keyset pagination (WHERE id > last_seen ORDER BY id LIMIT n).
  5. Check statistics and the bigger levers. Run ANALYZE when estimates are far off, then consider partitioning, materialized views or caching. State the cost: every index slows inserts and updates and uses storage.

Follow-up

  • The query is fast on your machine and slow in production. What differs, and how do you check?
  • Why might the planner ignore an index you just created?
  • How do you prove your change helped and did not slow down writes?

What are the SOLID principles of Object-Oriented Programming (OOP)?

Medium · Technical & Domain Knowledge

Approach

  1. Single responsibility: a class should have one reason to change. Example: split a report class that both queries data and formats HTML, so a layout change never touches the query code.
  2. Open/closed: add behaviour by adding a new implementation of an interface instead of editing a growing if/else or switch on type. Liskov substitution: a subtype must work anywhere its base type is expected. The classic violation is Square extending Rectangle, where setting the width silently changes the height.
  3. Interface segregation: prefer small role interfaces (Readable, Writable) over one large interface that forces clients to stub methods they never call. Dependency inversion: high-level policy depends on an abstraction (a Repository interface), and the concrete database class implements it, so a test can pass in a fake.
  4. Separate dependency inversion (the principle) from dependency injection (one technique that applies it: passing collaborators in through the constructor).
  5. Name the trade-off: applied mechanically, SOLID produces many tiny classes and layers of indirection. Back each principle with a change it made cheaper in code you have actually worked on.

Follow-up

  • Describe a Liskov violation you have seen in real code and how you fixed it.
  • In Python or JavaScript, with duck typing, do interfaces still matter? How would you express one (abc.ABC, typing.Protocol, TypeScript interfaces)?
  • When would you knowingly break open/closed or single responsibility?

How would you describe and implement design patterns in your code?

Medium · Technical & Domain Knowledge

Approach

  1. Group the patterns before naming them. Creational: Factory Method, Builder, Singleton. Structural: Adapter, Decorator, Facade. Behavioural: Strategy, Observer, Command, State. Then say a pattern is a name for a solution to a recurring problem, not a goal in itself.
  2. Walk through one implementation in detail. Strategy: define an interface with one method, write concrete classes for each algorithm (for example, retry policies), and let the context hold a reference that is chosen at runtime. In Python a plain function passed as an argument often replaces the class hierarchy.
  3. Explain the Singleton trade-offs: hidden global state, harder testing, and initialisation races. In C++11 and later, a function-local static is initialised thread-safely. Passing one shared instance in through the constructor is usually cleaner.
  4. Name the patterns the listed stacks already use: Node.js EventEmitter is Observer, Python decorators and Express middleware are close to Decorator/Chain of Responsibility, and context managers or RAII handle resource lifetime. Point out the Observer pitfall: listeners that are never removed keep objects alive (a memory leak).
  5. Close with the cost: every pattern adds indirection, so introduce one when a second variant actually appears, not in advance.

Follow-up

  • What is the difference between Decorator and plain inheritance, and when would you choose each?
  • How do Adapter and Facade differ?
  • Tell me about a time a design pattern made code worse, and what you did about it.

What is the difference between cohesion and coupling in system design?

Medium · Technical & Domain Knowledge

Approach

  1. Define both terms and the direction you want. Cohesion is how closely the responsibilities inside one module belong together (aim high). Coupling is how much one module depends on the internals of another (aim low).
  2. Give the classic scales. Cohesion runs from coincidental and logical (worst) up to sequential and functional (best). Coupling runs from content and common/global coupling (worst) through control coupling (passing flags that steer another module's logic) down to data coupling (passing only the values needed).
  3. Lift it to system design. Two services writing the same database tables are tightly coupled through the schema. A versioned API contract or events on a message queue loosen that, and a queue also removes temporal coupling (the consumer can be down), at the cost of eventual consistency and harder debugging.
  4. Say how you would measure it: how many modules change for one feature (change amplification), import or dependency graphs, circular imports, and fan-in/fan-out counts.
  5. State the trade-off: splitting too finely creates chatty network calls between services, which is coupling moved onto the network. Draw boundaries where cohesion is highest.

Follow-up

  • Show a tightly coupled piece of code and refactor it.
  • Can removing duplication (DRY) increase coupling? When would you accept duplication instead?
  • How does putting a message queue between two services change their coupling and their failure modes?

Explain how you would model a database for a movie theater or a social media retweet system.

Medium · Data & SQL Proficiency

Approach

  1. Movie theater core tables: theater, auditorium (theater_id), seat (auditorium_id, row_label, seat_number), movie, showtime (movie_id, auditorium_id, starts_at), booking (user_id, showtime_id, status, created_at) and ticket (booking_id, showtime_id, seat_id). Put UNIQUE(showtime_id, seat_id) on ticket so the database itself rejects a double booking.
  2. Seat holds under concurrency: insert a hold row with expires_at, or lock the seat rows with SELECT ... FOR UPDATE inside a transaction. Keep the unique constraint as the final guard, and expire unpaid holds with a background job.
  3. Retweet core tables: users, tweets (id, author_id, body, created_at), follows (follower_id, followee_id, primary key on both), and retweets (user_id, tweet_id, created_at, primary key on both, which stops duplicate retweets). Alternatively, store a retweet as a tweet row with retweet_of_id, which also supports quote tweets.
  4. Index for the reads: (author_id, created_at DESC) for profile and timeline queries, and (tweet_id) on retweets for counts. Keeping a denormalised retweet_count makes reads cheap but creates a write hot spot on viral tweets, so use sharded counters or asynchronous aggregation there.
  5. State the method: normalise to third normal form, list the top queries, then denormalise only where a measured read path needs it. For timelines, name the trade-off between fan-out on write and fan-out on read.

Follow-up

  • Two users click the same seat at the same moment. Walk through exactly what happens in your schema.
  • How do you handle an un-retweet while keeping the count correct?
  • How does the timeline design change for an account with millions of followers?

Describe a situation where you had to debug a complex issue in a production environment.

Medium · Behavioral & Scenario-Based

Approach

  1. Open with the symptom and its impact in numbers (error rate, latency, memory growth, users affected) and your role. Pick an incident where you did the diagnosis yourself.
  2. Show containment before root cause: a rollback, feature flag, failover or added capacity to stop the damage, then the investigation.
  3. Walk through the narrowing step by step. Line the start time up against deploys, config changes and traffic. Read the metrics, logs and traces. Reproduce with production-like data. Bisect commits or configuration. For each step, say what it ruled out.
  4. Name the technical root cause precisely: for example a memory leak found by comparing heap profiles, a connection pool exhausted by a slow query, a race condition visible in thread dumps, or an N+1 query pattern. Separate the trigger from the underlying cause.
  5. Close with the fix and the prevention: a regression test, an alert on the leading indicator, and a blameless write-up. A story where the fix was just a restart, with no explanation, does not answer the question.

Follow-up

  • How did you confirm the cause, rather than a coincidence that happened at the same time?
  • Which monitoring would have caught it earlier, and did you add it?
  • What would you have done if you could not reproduce it outside production?

Suggested practice checklist

The plan follows the four reported rounds. Days 2 to 5 cover the technical question groups, day 6 covers the statistics and puzzle questions, and day 7 is behavioural stories plus a full spoken run-through.

Day 1 — Map the loop and pick your language

  • Write the four reported rounds and what each is reported to cover.
  • Choose C++, Python or Node.js as your coding language.
  • List each resume project with the stack and one internal detail you could be asked about.

Deliverable: A one-page loop map, a language choice, and a project sheet with internals for each project.

Day 2 — Memory and concurrency

  • Explain heap versus stack aloud with an example in your language: lifetime, allocation cost, stack overflow, leaks.
  • Explain shared memory and concurrency: races, mutexes, deadlock, and how your language handles threads.
  • Implement a thread-safe queue and review a snippet for memory leaks.

Deliverable: Spoken answers to both fundamentals questions and a working, tested thread-safe queue.

Day 3 — OOP and design

  • For each SOLID principle, write a violating snippet and a fixed one.
  • Implement two patterns (for example strategy and observer) and say what problem each solves.
  • Explain cohesion and coupling with a refactor you have done or could do.

Deliverable: Five before-and-after snippets and short spoken answers for SOLID, design patterns and cohesion versus coupling.

Day 4 — SQL queries

  • Write the second-highest salary query three ways and test each on duplicates and a one-row table.
  • Write inner, left and anti joins, and one aggregation across a one-to-many join where sums could inflate.

Deliverable: A tested query set with sample data covering the joins and the second-highest salary variants.

Day 5 — SQL tuning and data modelling

  • Take a slow query, read its plan, and fix it with an index or a rewrite.
  • Model a movie theater (shows, seats, bookings with a uniqueness rule) and a retweet system.
  • Write a short MongoDB versus PostgreSQL comparison for one of those models.

Deliverable: A tuning write-up with before and after plans, two schemas, and a one-page database comparison.

Day 6 — Probability and puzzles

  • Solve the shooting-star question two ways and compare.
  • Explain the Central Limit Theorem and Bayes Theorem each with a worked example.
  • Work the 8-Ball puzzle on paper, writing every step.

Deliverable: Worked solutions to the four statistics and puzzle questions.

Day 7 — Behavioural stories and full run-through

  • Outline stories for the pivot, deadline, production-debugging and explain-to-stakeholders prompts.
  • Say each story aloud, stating the decision, the measurable result and the cost.
  • Run through one design question aloud, such as a multi-tenant system with a queue.

Deliverable: Four story outlines and one recorded or timed spoken run-through.

Behavioural stories for Afiniti

Reported behavioural questions cover adapting to change, working under deadlines, debugging in production and explaining technical decisions to people outside engineering. For this role, build stories around data-heavy or performance-sensitive work where you can state a result and what it cost.

  1. Describe a situation where you had to debug a complex issue in a production environment.
  2. Tell me about a technical decision you had to defend to a senior engineer.

Tell me about a time you had to pivot due to a significant business change.

Medium · Behavioral & Scenario-Based

Approach

  1. Choose a change that really altered your work, such as a cancelled project, a re-ordered roadmap or a changed customer requirement, and state in two sentences what changed and when you learned of it. The decision should be yours, not one you merely followed.
  2. Spend most of the answer on the decision: what you stopped, what you salvaged (schemas, interfaces, tests), how you re-estimated, and who you told. For engineering work, a good example is switching an approach under a fixed constraint and naming the switching cost.
  3. Show how you reduced risk: a thin slice first, a feature flag, a compatibility layer or a rollback plan, and a measurable result you can state (latency, error rate, delivery date).
  4. Close with an honest cost and a lesson, such as scope dropped or debt taken on. A pivot story with no cost sounds invented; one that blames management sinks it.

Follow-up

  • What did you decide not to salvage, and why?
  • How did you tell the people who depended on your work that the plan had changed?
  • Was there an earlier signal of the change that you missed or ignored?

How do you handle tight deadlines or the need to work extra hours?

Medium · Behavioral & Scenario-Based

Approach

  1. Answer both halves: how you work to a tight deadline, and your honest position on extra hours. Give a real example where you worked extra hours for a fixed date and describe it as bounded, with a clear end and a reason, not as routine.
  2. Show the mechanics: list what must ship, what can slip, and the critical path; cut scope instead of quality; and raise the risk early with options (reduce scope, move the date, add help) rather than announcing a miss at the end.
  3. Protect correctness under pressure, especially for code that handles a lot of data: keep tests and review, use a guarded rollout, and write down any shortcut you took on purpose with a follow-up to fix it.
  4. Report the result and the aftermath: what shipped, what was deferred, how you recovered afterwards. Heroics presented as the normal way of working, or a flat refusal with no example, both sink the answer.

Follow-up

  • What did you cut, and who agreed to the cut?
  • What would you do if the deadline was set before you had seen the requirements?
  • How do you tell when extra hours have stopped helping?

How do you explain your technical decisions to non-technical stakeholders?

Medium · Behavioral & Scenario-Based

Approach

  1. Pick a real decision with a trade-off that mattered to a non-engineer, such as delaying a feature to fix a slow query, choosing to rebuild rather than patch, or adding a queue. Restate it in their terms: cost, risk, schedule and customer impact, with no jargon.
  2. Structure the explanation: recommendation first, then two options with what each costs, then what you need from them and by when. Use a simple analogy and check it does not mislead about the risk.
  3. Show that you checked understanding: ask them to restate the decision, follow up in writing, and adjust when their questions reveal a business constraint you had missed.
  4. Include a moment of disagreement and how you resolved it with evidence, such as a measurement or a small prototype. Simplifying by leaving out the risk, or talking down to the listener, sinks the story.

Follow-up

  • Tell me about a time a stakeholder pushed back on your recommendation.
  • How do you communicate an estimate you are not sure about?
  • How would you explain a slow SQL query to a product manager?