cswork / libraryOpen practice workspace

Interview guides · Stackadapt

Stackadapt Software Engineer Interview Guide

Stackadapt Software Engineer interview guide: recruiter, technical and managerial rounds, reported coding, JS and RTB design questions, and a 7-day plan.

Interviewing at Stackadapt

  • Practice bank: 5+ questions
  • Rounds: 2
  • Typical prep: 1–2 weeks
  • Interview reports: 5

Interview rounds

RoundQuestions
HR Screen2
Technical Screen3

How hard is the Stackadapt Software Engineer interview?

DifficultyShareQuestions
Easy80%4
Medium20%1
Hard0%0

What Stackadapt actually tests for

TopicShareQuestions
Coding & Algorithms40%2
Software Engineering Fundamentals40%2
Statistics & Math20%1

The 60-second brief

Stackadapt is an AdTech company whose platform serves marketers running programmatic campaigns. This guide's sources describe the Software Engineer role as centring on the systems behind real-time bidding: microservices and data pipelines that must make bidding decisions within strict latency limits at high throughput, alongside infrastructure and UI-heavy feature work. That context explains the reported design questions (real-time bidding with millisecond latency, user-profile enrichment at scale, throughput versus latency) and is why a latency budget and a data-volume estimate belong in your design answers.

Stackadapt candidates report 3 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.

What a Software Engineer does at Stackadapt

This guide's sources say candidates should show a solid grasp of their primary language, giving Go, Java or React/JavaScript as examples, and list proficiency in at least one high-performance language (Go, C++, Java or Scala) as a must-have. Listed nice-to-haves include AdTech or MarTech experience, real-time inference systems, GPU programming and financial trading systems. The responsibilities the sources describe also include code review and working with product teams to ship features.

Candidates report three stages over roughly three to five weeks: a recruiter screen, a series of technical interviews with live coding and system design, and managerial interviews on fit and behaviour. They also say the format varies by team. Be ready for rounds that combine coding and design, and prepare for both algorithmic coding and design in any technical round. Cover front-end fundamentals even if you are applying for backend work, because the reported questions include the CSS box model and React state management.

The Stackadapt interview process

Stackadapt candidates report 3 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.

Recruiter Screen

Candidates describe this as a recruiter conversation to review your background and check that you fit the role. They also report that later rounds vary by team and that some candidates did not know what to expect in specific rounds. Use this call to pin down the format of the next round: whether coding and design are separate or combined, which language and editor you will use, and whether the team covers front-end topics. Raise your hard constraints here as well, including start date, location, work authorisation and compensation range. It costs much less to resolve them before technical interviews are booked than at offer stage.

What it evaluates

  • How your background maps onto the areas this guide's sources describe for the role: programmatic bidding, infrastructure, or UI-heavy feature development
  • Whether your constraints and expectations fit the role before technical rounds are scheduled

How to prepare

  • Prepare a short account of your background, naming your primary language (the sources give Go, Java or React/JavaScript as examples) and the scale of the systems you have built
  • Ask for the next round's format: coding, design or both, the editor you will code in, and whether front-end questions are in scope for the team
  • Write your start date, location, authorisation and compensation range as one-line facts before the call so you can state them without negotiating live

Technical Interviews

Candidates describe a series of technical interviews covering live coding and system design. Be ready for a session that combines coding and design, and for coding in a shared document or a basic editor without IDE support. Reported questions, which the sources do not tie to a specific round, fall into a few categories. Coding: detecting duplicates in an array, printing even numbers in a range, a graph problem, predicting a snippet's output (boolean logic, console.log, JavaScript closures) and sorting 1TB of data across machines with limited memory. Design: real-time bidding with millisecond latency, user-profile enrichment at scale, and the trade-off between throughput and latency. Technical knowledge: choosing a database, handling race conditions in distributed systems, the CSS box model, React state and testing a web application. Start with a working solution, then say how you would optimise it, and ask about input sizes before you commit to an approach.

What it evaluates

  • Whether you clarify constraints and input size before coding, and state time and space complexity for what you wrote
  • Whether you can get a correct first version working in a plain editor and then improve it, instead of stalling while you look for the optimal answer
  • Whether your design answers state a latency or throughput target and make trade-offs against it, including SQL versus NoSQL and consistency choices
  • Whether you can trace code by hand to predict its output, including JavaScript scope and closure behaviour

How to prepare

  • Code the reported shapes in a plain editor with no autocomplete: hash-set duplicate detection, BFS shortest path on a grid, and a range filter. State the complexity of each out loud
  • Trace short JavaScript snippets by hand, such as var versus let in a loop that schedules setTimeout callbacks and truthy/falsy comparisons, then check your predictions in a real console
  • Sketch a real-time bidding request path with a latency budget per hop and a no-bid response on timeout. Then do the same for profile enrichment with a batch path and a streaming path
  • Rehearse the 1TB sort as an external sort: sample the keys to pick range boundaries, partition across machines, sort runs that fit in memory, and k-way merge

Managerial Interviews

Candidates describe these interviews as assessing managerial fit and behaviour. Reported behavioural questions, which the sources do not tie to a specific round, include: a recent project you led from start to finish and its challenges, a disagreement with a teammate over technical architecture, how you prioritise competing deadlines under pressure, and what you want from your next role and why Stackadapt fits. Expect follow-ups that go past your summary into the architecture, the hurdles, your own contribution as distinct from the team's, and the business effect. Keep the facts of each story fixed, including team size, timeline and your role, so your account matches what you said in the technical interviews.

What it evaluates

  • Whether you separate your own decisions from the team's in a project you led
  • Whether you settle a technical disagreement with evidence and commit to the outcome
  • How you prioritise competing deadlines and explain what you deliberately dropped
  • Whether your reasons for moving connect to the actual work described for the role

How to prepare

  • Build one project deep dive with an architecture sketch, the hardest hurdle, your specific role and a measurable result
  • Prepare an architecture disagreement story that names the failure mechanism you were worried about and what you did after the decision. The argue-and-lose behavioural drill in this guide follows that shape
  • Write a two-sentence answer to 'why this role' that refers to the bidding, infrastructure or feature work described for it
  • Write down three facts per story that must not change between tellings, and check each rehearsal against them

Questions Stackadapt candidates report

Data structures and algorithms

Every fast solution here throws work away, and the claim you have to defend is that the discarded work could not have held the answer. A two-pointer walk over sorted values advances the left index only because every remaining pair using that element is also too small. A monotonic deque drops an element because a larger one sits to its right and will stay in the window longer. Say that sentence out loud. A weak answer names the technique and shows it passing the given examples, which is indistinguishable from a wrong technique that happens to pass the given examples.

  • Given a snippet of code, what is the expected output (e.g., boolean logic or console.log behavior)?
  • Implement a function to identify duplicate entries in an array or check for specific data patterns.
  • Write a function to print even numbers within a specific range or solve a graph-based problem.
  • How would you sort 1TB of data using a fleet of PCs with limited memory?

System design and architecture

A design is a set of promises about what happens when something is slow, down, or delivered twice, and that is the part usually left unsaid. For each dependency, say what the system does when it is unavailable: serve stale data, reject the write, or accept it and settle later. For each relaxation of consistency, describe the anomaly a user would actually notice, in the product's own words, rather than naming a consistency model and moving on. The separator is volunteering the failure behaviour as part of the design instead of producing it only when pushed.

  • How would you architect a service to handle user-profile enrichment at scale?
  • Design a system to process real-time bidding requests with millisecond latency.

Behavioural and engineering judgement

A reversal is the easiest of these to tell badly, because the honest version sounds like a mistake and the flattering version sounds like nothing happened. Give the trigger: the specific observation that made continuing worse than stopping, dated to when it arrived rather than when it became undeniable. Then give what stopping cost, whether that was work discarded, a migration left half-finished, or a team told the dependency they had built against was moving. The answer that stands out names a stopping condition set before it was needed, or says plainly that none had been set.

  • Describe a recent project you led from start to finish. What were the challenges?
  • Tell me about a time you had to handle a disagreement with a team member regarding technical architecture.

Other topics

A large share of these are really questions about evidence: why something is slow, or how much of something you would need. Convert the prompt into a number or an observation. For diagnosis, name the measurement that would tell two candidate explanations apart and say what each predicts before you collect it. For sizing, carry the units through and stop at one significant figure. Listing everything that could plausibly be true is what the weak version looks like. What separates the strong one is a hypothesis, the observation that would falsify it, and what the arithmetic already rules out before any new data arrives.

  • How would you handle a race condition in a high-throughput distributed system?

Practice exercises with worked solutions

The prompts above tell you what gets asked.

Replace offset paging on the resource feed with keyset

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.

How a strong candidate works it

  • 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.
  • 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.
  • 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.
  • 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.
  • State the residual honestly. Keyset is stable against concurrent inserts and deletes, but not against a row whose updated_at changes mid-scroll — that row moves in the ordering and can be seen twice. If the feed must be a snapshot, order by an immutable key or bound the page set with updated_at <= the cursor's start value.
  • Keep a total out of the page path. A tenant-wide COUNT(*) is the scan keyset just removed; fetch LIMIT 51 and return has_more instead.

Expected result (25 min): Keyset reads about 50 index entries per page and its latency is flat from page 1 to page 10,000; under both writer profiles the collected ids contain no duplicate and omit no row that existed for the whole scroll. OFFSET at page 400 reads 20,050 rows to return 50. Under the insert-only writer it repeats rows and skips none — an insert can only push a row down past the boundary — and only the second profile, whose deletes and updated_at bumps move rows up past the cursor, also makes it skip. Without the tie-break, the seek is updated_at < $2, which drops every still-unread row sharing the boundary timestamp.

Check yourself

  • EXPLAIN shows an Index Scan with no Sort node — a Sort means the ORDER BY is not satisfied by the index.
  • Buffers read per page stays constant as page depth grows.
  • Ids collected across the scroll are unique, and complete for rows untouched during the run.

Follow-ups

  • 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?
  • What does the cursor do when the row it points at has since been deleted?

Stop tag and share joins from fanning out a page

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

How a strong candidate works it

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

Follow-ups

  • The filter changes from 'any of these tags' to 'all of these tags'. Rewrite it and state what it costs relative to the ANY form.
  • A resource can be shared with the same user twice under different permissions. Does your count change, and should it?
  • Where does the correct total come from when the tenant holds 4M resources and the header must not cost 200 ms?

Identify the heaviest tenants in a five-minute window under memory pressure

The edge service handles about 3,000 requests per second across roughly 50,000 tenants, peaking near 9,000. Expose the 50 heaviest tenants by request count over the trailing five minutes so limits can be tightened before one tenant's backfill starves the fleet. You may not retain five minutes of raw records. Give the exact solution and its memory, then the bounded-memory approximation with its error stated as a formula, and say which you would ship and at what tenant cardinality that choice changes.

How a strong candidate works it

  • Do the exact version first, because it is affordable at this cardinality: a ring of 300 one-second counters per tenant, advanced lazily, is 1,200 bytes of counters per tenant and roughly 60 to 90 MB for 50,000 tenants with overhead. Carry a running total and subtract the bucket you overwrite so a window read is O(1) rather than 300 adds.
  • Extract the top 50 with a size-k min-heap over the tenant sums: O(d log k) for d tenants, against O(d log d) to sort them all. Maintaining the heap continuously instead of on query requires a tenant-to-heap-index map, because incrementing a count already inside the heap means sifting from a known position, and without that map you rebuild the heap on every request.
  • State the approximation precisely rather than gesturing at sketches. Misra-Gries with m counters retains every item whose true count exceeds N/(m+1), and each retained count underestimates the truth by at most N/(m+1). With m = 1,000 and N = 900,000 requests in the window the error is roughly 900 requests, which is fine for spotting a tenant sending 50,000 and useless for ranking two tenants 200 apart.
  • Say what breaks when the window slides: Misra-Gries and Space-Saving are insert-only and cannot be decremented as records age out. The workable construction is one summary per sub-window, say ten seconds, with 30 summaries merged at query time, and the merged error is the sum of the per-summary errors, so the bound degrades linearly in the number of sub-windows.
  • Choose and defend it: at 50,000 tenants the exact rings cost under 100 MB in a process that already holds more, so ship exact. Keep the sketch for the case that actually motivates it, a per-principal or per-IP key where cardinality runs to millions and is not bounded by anything you control.
  • Raise the fleet problem before it is asked: each of 20 to 40 instances sees only its share, and the top 50 of one shard is not the top 50 of the fleet. Either aggregate counts centrally or accept that a per-instance threshold multiplied by instance count is the limit you are really enforcing.

Expected result (25 min): An exact per-tenant ring of 300 one-second counters at roughly 60 to 90 MB for 50,000 tenants with O(1) window reads, top-50 extraction by a size-k min-heap in O(d log k), a Misra-Gries bound of N/(m+1) with the numbers substituted, the sub-window merge needed to slide it, and a decision to ship exact at this cardinality with the sketch reserved for unbounded keys.

Check yourself

  • The exact window sum for one tenant equals a replay of its raw records over the same five minutes.
  • The heap result matches a full sort of the tenant sums on a sample.
  • The sketch's count for a known heavy tenant is at most the true count and no less than the true count minus N/(m+1).

Follow-ups

  • The heaviest tenant is heavy because of one export job rather than user traffic. Should the limiter treat those as the same tenant?
  • Two tenants sit tied at the boundary of the top 50. Does your answer flap, and does the flapping matter?
  • You switch to per-principal keys and cardinality goes to 10 million. Walk through what changes.

Choose what to break when replication lag reaches forty seconds

Reads are served from two replicas: 14k requests/second, about 85% absorbed by cache, so roughly 2.1k reads/second reach the database. Writes go to the primary at 1.2k/second. A tenant's backfill drives replication lag from under 100 ms to 40 seconds and it is still climbing. Sessions that have just written are pinned to the primary. Decide, endpoint class by endpoint class, whether to serve stale, fail, or route to the primary, and justify each choice with the load it adds to the primary. Then state what you would have built beforehand.

How a strong candidate works it

  • Establish blast radius before cause, because mitigation and diagnosis have different deadlines. The decisive arithmetic is what happens if the database reads move to the primary: 2.1k reads/second on top of 1.2k writes/second roughly triples its operation count, on the node already absorbing the backfill that caused this. Reads and writes are not equal in cost, so treat that as an argument against a blanket move rather than as a capacity model - but it is enough to rule out routing everything to the primary.
  • Classify endpoints by what staleness costs, not by how important they feel. Reads whose staleness is invisible - listings, search, counters - stay on the replica and return the watermark so the client can tell. Reads that immediately follow that same session's write keep their primary pin, which is a small bounded slice of traffic rather than the whole 2.1k/second. Reads that feed a decision with a side effect - authorisation, quota, the read half of a read-modify-write - must not be stale at all, because a 40-second-old permission row is the stale-permission failure wearing a different costume; those go to the primary or fail.
  • Shed instead of queueing. If the must-be-fresh class alone exceeds the primary's headroom, refuse its lowest-value slice with 503 and a retry-after. A request queued behind a saturated primary holds a connection for a client that has already given up, and the retry storm that follows is what turns degradation into an outage. Bound the connection pool per role so the read fallback cannot consume the write path's connections - that bulkhead is the single decision that determines whether writes survive the next ten minutes.
  • Attack the cause in parallel, since it is the one thing that can be stopped. The backfill is the load generator. A backfill that reads replication lag as its throttle signal and pauses above a threshold would have made this a non-event, with batch sizes small enough that each batch's write volume is a fraction of what a replica can apply per second. That is most of the answer to what should have existed beforehand.
  • Name the mechanism you would prefer over session pinning. Capture the write position at commit and require the read path to be at or past it: compare the primary's pg_current_wal_lsn() at commit time against the replica's pg_last_wal_replay_lsn(), and fall back to the primary only for the specific request that is ahead of the replica. Session pinning is the cheap approximation and it over-pins - every read in the window goes to the primary whether or not it needed to, which is a share of the cost being paid right now.

Expected result (35 min): Staleness-invisible reads stay on the replica and return a watermark. Reads following the session's own write keep a bounded primary pin. Reads that gate a side effect go to the primary or fail, with the lowest-value slice shed as 503 plus retry-after if headroom runs out. Connection pools are bounded per role so the read fallback cannot starve writes. The backfill is throttled on measured replication lag. The durable fix is a commit-position token compared against the replica's replay position, replacing blanket session pinning.

Check yourself

  • The traffic routed to the primary under the plan is stated as a rate, not as a category.
  • No element of the plan increases the write path's connection usage.
  • The backfill throttle references a measured lag value and has an explicit resume condition.
  • Every endpoint left on a replica returns something with which a client can detect staleness.

Follow-ups

  • Lag returns to normal in nine minutes. Which mitigation do you remove first, and which one stays permanently?
  • A user reports their change did not save, and the write committed. Trace the path that produces that report and name the signal that would have shown it before the report arrived.
  • The replica is 40 seconds behind but otherwise healthy. Do you take it out of rotation? What does that do to the other replica's lag?

Every query on one table stalls for forty seconds mid-deploy

During a release on PostgreSQL, every query touching resource times out for about 40 seconds and then recovers with no intervention. The release ran one migration, ALTER TABLE resource ADD COLUMN archived_reason TEXT, and the migration log shows it completing in 6 ms. Unrelated tables showed no change in error rate. Explain how a 6 ms statement caused a 40-second stall, give the ordered checks you would run on a live system to confirm it, and give the migration procedure that prevents a repeat.

How a strong candidate works it

  • Separate the statement's duration from the lock's duration. ADD COLUMN with no default is a catalogue-only change and genuinely runs in milliseconds, but it requires ACCESS EXCLUSIVE, and it cannot acquire that until every transaction already touching the table has finished.
  • Account for the queueing, which is the part that surprises people. A lock request that is waiting blocks later requests for conflicting modes behind it rather than letting them overtake, so one long-open transaction holds the DDL and the DDL holds all the traffic. The stall length is set by the longest open transaction, not by the size of the change.
  • Confirm on a live system in this order: pg_stat_activity for that table ordered by xact_start, looking for the oldest transaction and specifically for state = idle in transaction; then pg_locks where granted = false to find the waiter; then join them on pid to name blocker and blocked. pg_blocking_pids() does that join for you and is the fastest single call.
  • Prevent rather than merely time it better. Set lock_timeout to a second or two on the migration session so the DDL abandons the queue after a bounded wait and is retried, instead of holding it for as long as the oldest transaction lives. Be exact about what that buys: queries arriving during the wait still queue behind the pending ACCESS EXCLUSIVE request, so each attempt costs them up to one lock_timeout of added latency. The outage goes from 40 seconds to about one second per attempt, not to zero. Also run migrations away from deploy-time peaks, and put a statement timeout and an idle-in-transaction timeout on the analytics role that opens the long transactions.
  • Know the lock each change takes, since the mitigation differs by change. A column with a non-volatile default is a metadata-only change from PostgreSQL 11 and still needs the brief ACCESS EXCLUSIVE; an index needs CREATE INDEX CONCURRENTLY, which cannot run inside a transaction block and leaves an INVALID index to drop if it fails; a check or foreign key is added NOT VALID and then VALIDATE CONSTRAINT as a separate statement under a weaker lock.

Follow-ups

  • The same release also wants NOT NULL on that column. What is the sequence that gets there without a long lock?
  • Your lock_timeout retry fails ten times in a row because the analytics transaction is always open. What do you change?
  • How does this differ on MySQL with InnoDB online DDL, and what is the equivalent of the waiting-lock queue there?

Argue against a design, lose, and commit anyway

Describe a design you argued against and lost. State the failure you predicted as a named mechanism, not a feeling about complexity: two services that would need one transaction, a projection with no rebuild path, a write path with no idempotency key. Say what evidence you brought, what the decision maker weighed instead, and what you did after the decision was made: what you instrumented, what you wrote down, and whether the prediction came true. Five minutes.

How a strong candidate works it

  • State the prediction in falsifiable form up front: the mechanism, the condition that triggers it, and the observable outcome. A prediction that cannot be checked also cannot be credited to you later.
  • Show the evidence you had at the time and label each piece honestly as measured, analogous, or intuition. Keeping the intuition is fine; disguising it as data is the thing that erodes your standing in the next argument.
  • Represent the opposing case at full strength, including the constraint you did not control: a fixed date, a team boundary, or the fact that the decision was cheap to reverse and yours was not.
  • Make disagree-and-commit concrete. Name the artefact you left behind so the prediction could be settled without you: the alert and its threshold, the counter on the dashboard, the decision note that recorded the trade-off and the condition that would revisit it.
  • Report the outcome without editing it. If the design held and your predicted mechanism never fired, say so and say what you had mis-weighted, which is more persuasive than a vindication story.

Follow-ups

  • What threshold on that alert would have proved you right, and did anyone ever look at it?
  • If the same proposal arrived tomorrow with the same deadline, would you argue it the same way?
  • How did you behave toward the design once it shipped and started failing in a different way than you predicted?

Where candidates lose points

Guessing at a code-output question (console.log, boolean logic, closures) instead of tracing it

Reported questions include predicting the output of a snippet and explaining a JavaScript loop with closures. Trace the code line by line and write down each variable's value. Know the standard trap: for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)) prints 3, 3, 3, because var is function-scoped and all three callbacks share the same i. With let, each iteration gets its own binding and the output is 0, 1, 2. Also know that setTimeout callbacks run after the current synchronous code finishes, and that == coerces types while === does not.

Drawing boxes for the real-time bidding design without a latency budget or a timeout path

A design question about bidding with millisecond latency is really a question about the hot path. Start by stating the end-to-end budget as an assumption you will confirm, then split it across the hops. Keep the per-request work to in-memory or local lookups against precomputed data, such as enriched profiles and campaign state. Move logging, attribution and model updates off the request path into an asynchronous pipeline. Say what happens on a timeout: return a no-bid rather than a late bid. Then explain what you give up to hold latency, for example staleness in the precomputed data.

Answering the 1TB-across-a-fleet question as if the data fits on one machine

Ask how much memory each machine has and whether the output must be one globally sorted file or a set of sorted partitions. Then give the external-sort answer. Sample the keys to choose range boundaries, send each record to the machine that owns its range, and have each machine sort memory-sized runs and k-way merge them from disk. Because each partition holds a contiguous key range, concatenating the partitions in order gives a globally sorted result. Mention skew in the sample and how uneven partitions would be rebalanced.

Preparing only algorithms and backend design when candidates report front-end, testing and fundamentals questions

The reported technical and domain questions include the CSS box model, React state management, how you test a web application, choosing between database systems, and handling race conditions in a distributed system. The bank also covers web automation locators, CI/CD, and hash-map complexity. Prepare a short, concrete answer for each. For race conditions, name a mechanism such as an idempotency key, optimistic concurrency with a version column, or a conditional write. For database choice, tie the choice to the access pattern and consistency needs.

Giving a project story in a behavioural interview that cannot survive a follow-up

Reported behavioural questions ask about a project you led end to end and an architecture disagreement. Prepare one project so that you can sketch its architecture, name the hardest hurdle, say which decisions were yours, and give the business result. Keep team size, timeline and your role identical every time you tell it. If an earlier technical answer went badly, be ready to name what you missed instead of giving a polished second version.

Weakness triage, driven by a day-one self-assessment

Day one measures instead of guessing, under a fixed rubric, and the remaining hours are allocated in proportion to the gaps before any studying begins. The allocation is deliberately not renegotiated midweek, because the area that feels worst on day three is usually the one that is moving.

Day 1 — Recruiter screen and format questions

  • Write a short background summary that names your primary language (for example Go, Java or React/JavaScript) and the largest system you have worked on
  • List your hard constraints (start date, location, authorisation, compensation range) as one-line facts
  • Write the questions you will ask about the technical rounds: coding, design or both, which editor, and whether front-end topics are in scope

Deliverable: A one-page screen sheet with your background summary, constraints and format questions.

Day 2 — Core coding in a plain editor

  • In an editor with no autocomplete, solve the reported shapes: duplicate detection with a hash set, a range filter such as even numbers in [a, b], and BFS shortest path on an obstacle grid
  • For each one, state the time and space complexity out loud before running it, then write the edge cases: empty input, a single element, an unreachable target
  • Review hash-map behaviour for the bank questions on dictionary operation complexity and Java HashMap collisions: average O(1), and the worst case when many keys collide

Deliverable: Three working solutions, each with its complexity and edge cases written above the code.

Day 3 — Code-output, JavaScript and front-end fundamentals

  • Trace a set of short snippets by hand before running them, covering var versus let in loops with setTimeout, closures, == versus ===, and truthy/falsy values
  • Write short answers to the reported front-end questions: the CSS box model (content, padding, border, margin, and what box-sizing changes) and how React components hold and update state
  • Write out how you would test a web application and inspect a page, including how to choose stable element locators

Deliverable: A sheet of traced snippets with predicted and actual output, plus written front-end answers.

Day 4 — Large data, concurrency and counting

  • Talk through sorting 1TB across machines with limited memory: sample the keys for range boundaries, partition, sort memory-sized runs locally, k-way merge
  • Answer the race-condition question with specific mechanisms: an idempotency key, a version column with a conditional update, or a single-writer partition
  • Work through the heavy-tenants worked exercise (drill-coding-3) and check your exact ring-buffer and Misra-Gries bounds against it
  • Sketch the bank's time-windowed key-value store and state the complexity of get and put

Deliverable: Written answers to the 1TB sort and race-condition questions, plus a completed heavy-hitter exercise.

Day 5 — System design: bidding, enrichment and throughput versus latency

  • Design the reported real-time bidding system with an explicit latency budget per hop, precomputed lookups on the hot path, and a no-bid response on timeout
  • Design user-profile enrichment at scale with a streaming update path and a batch backfill, and say where the profile store may be stale
  • Work through the replication-lag worked exercise (drill-design-4) and practise stating each routing decision as a load on the primary
  • Write one paragraph on when you would give up throughput for latency and when you would do the reverse

Deliverable: Two design sketches, each with a latency or throughput target and one named trade-off.

Day 6 — Databases, SQL and debugging

  • Write a comparison of relational and NoSQL stores keyed to access pattern and consistency needs, for the reported database-choice question
  • Do the keyset pagination worked exercise (drill-sql-1) and the bank topics on INSERT and UNION
  • Work the lock-stall debugging drill (drill-debugging-5), and for each check state what result would confirm or rule out the hypothesis

Deliverable: A database-choice comparison, a verified keyset query, and an ordered debugging checklist.

Day 7 — Managerial interviews and a combined mock

  • Rehearse the reported behavioural questions aloud: a project you led end to end, an architecture disagreement, prioritising competing deadlines, and why Stackadapt
  • Build the argue-and-lose story from drill-behavioral-6, naming the predicted mechanism and what you instrumented afterwards
  • Run a mock round that combines a coding question and a design follow-up in one session, since a technical round may combine both
  • Write three facts per story that must stay fixed across tellings

Deliverable: Four rehearsed behavioural answers with fixed facts, and notes from one combined coding-and-design mock.