Interview guides · Asurion
Asurion Software Engineer Interview Guide
Asurion Software Engineer interview guide: the four reported rounds, practical coding lab, React, Express, SQL, algorithm, design and behavioral prep.
The 60-second brief
Asurion is described as a company working across consumer technology, insurance, logistics and conversational AI, with software behind device protection, repair and support. Candidates describe systems that handle customer interactions, claim filings and automated device diagnostics. For a Software Engineer, the listed work spans customer-facing web interfaces (React is named, for example for device onboarding and claim filing) and backend services: microservices, REST APIs, integrations with third-party vendors, and SQL or NoSQL persistence. The role also covers writing unit and integration tests, helping maintain CI/CD pipelines, watching service telemetry and joining blameless post-mortems after incidents.
This guide is for candidates interviewing for a Software Engineer position at Asurion, from the recruiter call through the final virtual on-site. It covers the reported rounds, the practical coding lab, the reported coding, algorithm, system design and behavioral questions, and a seven-day plan. Candidates report that some content varies by team (web platform, conversational AI, backend microservices) and by seniority, so confirm your team's language and stack with the recruiter early.
Candidates report four rounds over about 3-5 weeks: a recruiter phone screening, an online technical screening, a hiring manager discussion and a final virtual on-site. The on-site is described as a practical coding assessment, a system architecture session and a behavioral conversation.
Focus areas
- System Design
- React (React.js)
- Frontend Development
Overview
The work behind this loop is full-stack and service-oriented. Listed technologies include React on the front end, Node.js with Express or Spring Boot on the back end, PostgreSQL, MySQL or NoSQL stores, and Jest, JUnit or Mocha for tests. Domain examples that come up in reported questions are repair appointments, device serial numbers, repair status updates and device diagnostics telemetry. Practise in that shape: small web apps, REST endpoints, validation and queries, not isolated puzzles.
The reported questions fall into four groups. Practical coding and frameworks: fix component state bugs in an unfinished web app, build an Express CRUD server, explain global state and side effects in React, contrast SQL with NoSQL and write an aggregate query, and validate a JSON payload against business rules. Algorithms: unique pairs that sum to a target, balanced brackets, reversing a linked list iteratively and recursively, parsing serial numbers from raw strings, and merging overlapping intervals. System design: defend a system you built, a repair-status notifications service, a monolith-to-event-driven migration, indexing and caching for read-heavy traffic, and high availability with zero-downtime deploys. Behavioral: ambiguity, disagreement, debt versus a deadline, testing and version control, and an outage.
Candidates report that the process leans toward practical work over abstract puzzles, with an extended lab in a real development environment: a virtual machine, skeleton code and failing unit tests, where passing the tests with clean, extendable code matters more than finishing every stretch goal. Spend most of your time on the habits that lab needs: read a test suite, find why it fails, fix the business logic, guard against null and malformed input, and explain the result aloud.
Treat the algorithm list as a fixed set you can finish cleanly, the design list as material you can sketch with trade-offs, and the behavioral list as stories with specifics. The plan below allots a day to each group, and the final day rehearses the lab and the stories together.
The Asurion interview process
Candidates report these four rounds, in this order, over about 3-5 weeks; the technical screen format can differ by business unit and seniority.
Recruiter Phone Screening
Candidates report this as an initial conversation with a recruiter about your background and fit for the role. No technical questions are reported for this stage. Use it to learn which team you are being considered for, which language the later hands-on stages will use, and what the online screen looks like, because candidates report that the team shapes the stack (web platform, conversational AI and backend microservices are named).
What to demonstrate
- How well your background lines up with the role
- How clearly you describe your recent projects and stack
- Which of the listed languages (JavaScript/TypeScript, Java, Python, C#) you work in best
How to prepare
- Write a two-minute walkthrough of your career that ends on the stack you want to be interviewed in, whether React and Node.js or Java with Spring Boot
- Ask which team the role sits on, which language the practical lab is likely to use, and which platform hosts the online screen
- Have your location, notice period and salary expectations ready so the call stays on your experience
Online Technical Screening
Candidates report a coding assessment run on a third-party platform such as Karat or HackerRank. They also report that, depending on business unit and seniority, this screen may be automated or run by a senior engineer on the team. A coding stage is where the algorithm and function-writing questions in this guide are practised: pair sums, balanced brackets, linked-list reversal, serial-number parsing and interval merging, plus input validation.
What to demonstrate
- Working code in a timed, browser-style editor
- Choice of data structure and Big-O reasoning
- Handling of empty, duplicate and malformed input
How to prepare
- Solve the five algorithm questions from this guide in your strongest language in a plain editor with no autocomplete, then write three edge-case tests for each
- Practise stating complexity in one sentence per solution, for example a hash set for pair sums or a stack for brackets
- Rehearse reading the whole prompt, asking about input limits, and running your own tests before you submit
Hiring Manager Discussion
Candidates describe a technical and experience discussion with the hiring manager. This is the stage to talk through projects you led and decisions you made, and the reported behavioral prompts about ambiguity, disagreement, technical debt, testing and outages are natural material. Bring concrete systems and numbers you actually measured rather than a list of technologies.
What to demonstrate
- Fit between your experience and the team's work
- Your account of past decisions, trade-offs and mistakes
- How you describe working with product, design and QA partners
How to prepare
- Choose two projects you owned and write for each the decision you made, the alternative you rejected and what it cost
- Rehearse short answers to the ambiguity, disagreement and debt-versus-deadline prompts, each with a specific outcome
- Prepare questions about how the team tests, deploys and handles incidents, which are the practices the role lists
Final Virtual On-Site Interview
Candidates describe the final virtual on-site as a combination of a practical coding assessment, a system architecture session and a behavioral conversation. The coding part is reported as an extended lab, often 2.5 to 3 hours, in a pre-configured environment with skeleton code and failing unit tests, followed by a walkthrough of how your solution handles malformed input. The architecture session is reported to include defending a past system and solving a design scenario in the company's domain; candidates are advised to be open about trade-offs, debt and mistakes.
What to demonstrate
- Code quality and modularity in a realistic environment, with the existing tests passing
- Defensive handling of null values, wrong types and malformed objects
- Architecture reasoning: storage choice, messaging, caching, scaling and failure modes
- Clarity when explaining your solution and past decisions
How to prepare
- Build a small repo with failing Jest (or JUnit) tests, then fix it end to end while talking aloud; get the core tests green before any extras
- Practise a class or module design that another engineer could extend, with input validation at the boundary
- Sketch a past system plus the repair-status notifications design, and state what fails first at ten times the traffic
- Rehearse your behavioral stories so each fits in a few minutes with a clear result
Where candidates lose points
Chasing stretch goals in the practical lab while core unit tests still fail
Run the whole suite first and read each failing assertion. Fix the business logic one test at a time, keep the suite green after each change, and only then add extensions. Candidates report that passing tests and clean, extendable code count for more than finishing every stretch objective.
Fixing a React state bug by symptom: forcing a re-render or adding an effect without finding the cause
Trace the bug before editing. Check for mutated arrays or objects, stale closures in the submit handler, missing effect dependencies and controlled versus uncontrolled inputs. Say what you found aloud, fix the cause, and add a test that fails without the fix.
An Express endpoint or JSON validator that only handles the happy path
Validate at the boundary before anything is saved: missing fields, wrong types, null, extra properties and values outside the business rules. Return distinct status codes (400 for bad input, 404 for a missing record, 201 on create) and write tests for malformed payloads, not just valid ones.
Algorithm answers that are right on the example and wrong on edge cases
List the edge cases before coding: duplicate numbers in unique pairs, interleaved bracket types, an empty list or a single node, intervals that only touch at an endpoint, serial numbers with stray whitespace or casing. State the complexity and run those cases by hand.
Presenting a past system as a success story with no trade-offs, mistakes or failure modes
For the system you will defend, prepare why you chose the storage and messaging, what you would change, where the debt sits, and what breaks first at ten times the traffic. Use first person singular for what you decided, and be specific about what you owned.
Questions & practice
Given an existing, incomplete web application, locate the bugs in the component state and implement the reques
Medium · Practical Coding & Frameworks
Given an existing, incomplete web application, locate the bugs in the component state and implement the requested feature.
Approach
- Reproduce before editing. Run the app and the failing tests, read the failing assertion, and find the component that owns the broken state. Then trace props down and callbacks up from that component.
- Check the usual React state bugs in order. First, mutating state in place (pushing onto the existing array and passing the same reference, so React bails out because Object.is sees no change). Second, stale closures from missing effect dependencies. Third, reading state right after setState instead of using the updater form, setCount(c => c + 1). Fourth, using the array index as a list key.
- Fix one bug at a time and rerun the tests after each fix, so you always know which change made a test pass. For forms, check that inputs are controlled (value plus onChange) and that the submit handler calls preventDefault.
- Build the requested feature with the smallest change that fits the existing structure. Lift state up only if a sibling needs it. Cover the empty, loading and error states, not just the happy path.
- Add a React Testing Library test for the bug you fixed and one for the new feature. Drive them through user events, not internal state, so they survive refactors.
Follow-up
- Why did the component not re-render after you pushed the new item into the array?
- How would you test the submit handler without calling the real API?
- At what point would you move this state into a reducer or a shared context?
How do you manage global state and side effects in a modern React application?
Medium · Practical Coding & Frameworks
Approach
- Sort state by kind before picking a tool. Local UI state lives in useState. Shared client state goes in Context with useReducer, or a store such as Redux Toolkit or Zustand. Server data goes in a cache layer such as TanStack Query or RTK Query. Filters and selection go in the URL. Much of what looks like global state is really cached server data.
- Know the cost of Context: every consumer re-renders when the provider value changes. Split contexts by update frequency and memoise the value. Use a store with selectors when many components read small slices.
- Use useEffect only to sync with external systems (fetching, subscriptions, timers). Give it a correct dependency array and a cleanup function, for example an AbortController for fetch or an unsubscribe. Compute derived data during render or with useMemo, not in an effect.
- Handle races explicitly: abort or ignore a stale response in the cleanup so a slow earlier request cannot overwrite a newer one. In development StrictMode, React mounts, unmounts and remounts components, so effects must be safe to run twice.
- Keep reducers pure and put side effects in thunks, listener middleware or the query library. Unit-test reducers directly, and test fetching components with a mocked network layer.
Follow-up
- How do you stop an earlier, slower request from overwriting the result of a later one?
- When would Context plus useReducer be enough, and when would you reach for Redux Toolkit or a query library?
- How would you test a component that fetches data when it mounts?
Write a function that accepts a JSON payload, validates the input structure against business rules, and handle
Medium · Practical Coding & Frameworks
Write a function that accepts a JSON payload, validates the input structure against business rules, and handles edge cases gracefully.
Approach
- Parse defensively. Wrap JSON.parse in try/catch because it throws SyntaxError. Then confirm the top level is a plain object: typeof null is 'object' and arrays are objects too, so check both explicitly.
- Validate in two layers. The structural layer checks required fields, types and formats. The business-rule layer checks things like an allowed status value, a positive integer quantity (Number.isInteger) or a date that is not in the future. Keeping the layers separate makes each one testable.
- Collect every error instead of stopping at the first. Return a result such as { valid, errors: [{ path, code, message }] } with a path for nested fields like items[2].sku. Invalid input is expected, so return errors rather than throwing.
- List the edge cases: missing vs null vs empty string, numeric strings like "5", unknown extra fields (decide whether to reject or strip them), very large payloads, whitespace-only strings, and duplicate keys (JSON.parse keeps the last one). Return a normalised copy instead of mutating the input.
- Mention the trade-off between a schema library (JSON Schema with Ajv, Zod, Joi) and hand-written checks. Back either one with table-driven unit tests: one row per rule, each with a valid and an invalid case.
Follow-up
- How do you report an error inside the third element of a nested array?
- Would you reject unknown fields or strip them, and what risk does each choice carry?
- How would you support two versions of the payload rules at the same time?
Given an array of integers, find all unique pairs that sum up to a specific target value.
Medium · Data Structures & Algorithms
Approach
- Clarify first: are pairs unique by value (so [2, 2, 2] with target 4 gives one pair), can an element pair with itself, does output order matter, and are negatives allowed?
- Hash-set pass. Iterate once with a seen set. When target - x is in seen, add the ordered pair (min, max) to a result set, then add x to seen. This is O(n) time and O(n) space. It handles x + x = target correctly because x is only in seen if it appeared earlier.
- Sort plus two pointers is the alternative: O(n log n) time and O(1) extra space beyond the sort. Move the left pointer up when the sum is too small and the right pointer down when it is too large, and skip runs of equal values after a match so duplicates are not emitted. The output comes out sorted.
- Mention the O(n^2) brute force only as the baseline. Cover the edge cases: empty or single-element input, all-equal values, no valid pair, and integer overflow of the sum in fixed-width languages.
Follow-up
- How would you count pairs by index rather than by value, for example how many index pairs sum to the target?
- What changes if the array is too large for memory but sorted on disk?
- How would you extend this to unique triplets, and what is the complexity?
Write a function to validate whether a given string of brackets and parentheses is balanced.
Medium · Data Structures & Algorithms
Approach
- Use a stack. Push every opener. On a closer, fail if the stack is empty or its top is not the matching opener; use a map from closer to opener for the check. At the end the string is balanced only if the stack is empty. This is O(n) time and O(n) space.
- Add the cheap early exits: an odd-length string of brackets only cannot be balanced, and a closer on an empty stack fails immediately.
- Clarify the edge cases: whether the empty string counts as balanced (usually yes), whether other characters are ignored, and inputs made only of openers, which pass the loop but fail the final empty check.
- Explain why a counter is not enough. With a single bracket type a running count works in O(1) space, but with several types it accepts "([)]", which is unbalanced. That is why the stack is needed.
Follow-up
- How would you return the index of the first mismatched bracket?
- What is the minimum number of insertions needed to balance the string?
- How would you ignore brackets that appear inside quoted string literals?
Implement an algorithm to reverse a linked list both iteratively and recursively.
Medium · Data Structures & Algorithms
Approach
- Iterative version: prev = null and curr = head. Loop while curr: save next = curr.next, set curr.next = prev, then advance prev = curr and curr = next. Return prev. This is O(n) time and O(1) space.
- Recursive version: if head is null or head.next is null, return head. Otherwise newHead = reverse(head.next), then head.next.next = head, head.next = null, return newHead. This is O(n) time but O(n) call stack.
- Name the recursion risk. A long list can overflow the stack (Python's default recursion limit is 1000), which is why the iterative version is the production choice.
- Test the empty list, a single node and two nodes. Forgetting head.next = null in the recursive version leaves a two-node cycle. Write a helper that converts the list to an array so assertions stay readable.
Follow-up
- How would you reverse the list in groups of k nodes?
- How would you reverse only the nodes between positions m and n?
- What changes for a doubly linked list?
Solve a classic string parsing problem to extract and sanitize device serial numbers from raw input strings.
Medium · Data Structures & Algorithms
Approach
- Pin down the format before coding: allowed characters, length range, case, separators and whether there is a check digit. For example, IMEIs end in a Luhn check digit. Ask whether look-alike characters such as O/0 and I/1 should be corrected or rejected.
- Normalise first: trim, uppercase, remove spaces and hyphens, apply Unicode NFKC normalisation to fold full-width characters, and strip zero-width characters. Then extract candidates with a global regex with word boundaries, such as /\b[A-Z0-9]{10,12}\b/g, or with a single linear tokenising pass. Either way it is O(n) in the input length.
- Validate each candidate against the rules and return both the valid serials and the rejected tokens with a reason. Remove duplicates while keeping first-seen order; a JavaScript Set preserves insertion order. Never throw on null, undefined or garbage input.
- Avoid regexes with nested quantifiers on untrusted input, because catastrophic backtracking (ReDoS) can hang the process. Cover the rules with table-driven unit tests, one row per malformed case.
Follow-up
- How would you add check-digit validation, for example Luhn for IMEIs?
- How would you process millions of lines as a stream instead of loading them all into memory?
- What do you do when two serials appear next to each other with no separator?
Given an array of time intervals representing repair appointments, merge all overlapping intervals.
Medium · Data Structures & Algorithms
Approach
- Sort a copy by start time (O(n log n)). Walk it while keeping the last merged interval: if current.start <= last.end, set last.end = max(last.end, current.end); otherwise push current. The total is O(n log n) time and O(n) for the output.
- Ask whether back-to-back appointments such as [9, 10] and [10, 11] should merge. That choice is the difference between <= and <, and for bookings it is a business rule.
- Use max when merging because an interval can sit entirely inside another, so the new end is not always larger. Handle empty and single-element input, unsorted input, and reject or report intervals whose start is after their end.
- Normalise timestamps to a single timezone or epoch values before comparing. Do not mutate the caller's array.
Follow-up
- How would you insert one new appointment into an already merged, sorted list?
- What is the minimum number of technicians needed so that no overlapping appointments share one?
- How would you handle intervals that arrive as a stream?
Explain the key differences between SQL and NoSQL databases, and write a query to fetch aggregated records fro
Medium · Practical Coding & Frameworks
Explain the key differences between SQL and NoSQL databases, and write a query to fetch aggregated records from a relational schema.
Approach
- Contrast the models on concrete properties. Relational databases enforce a schema, support joins and give multi-row ACID transactions. NoSQL families (document, key-value, wide-column, graph) model data around access patterns, partition horizontally by design and often trade cross-record transactions or strong consistency for scale. Choose by access pattern and consistency needs.
- State the grain before writing the query. For example, claims per customer: SELECT c.customer_id, COUNT(cl.claim_id) AS claim_count, COALESCE(SUM(cl.amount), 0) AS total FROM customers c LEFT JOIN claims cl ON cl.customer_id = c.customer_id GROUP BY c.customer_id HAVING ... ORDER BY total DESC.
- Handle the LEFT JOIN traps. COUNT(*) returns 1 for a customer with no claims, while COUNT(cl.claim_id) returns 0. A WHERE filter on the right-hand table silently turns the join into an inner join, so put that filter in the ON clause.
- Prevent fan-out. Joining two one-to-many tables before aggregating multiplies rows and inflates sums, so pre-aggregate each child in a CTE or subquery and join the results. Use WHERE for row filters and HAVING for filters on aggregates.
- Name the supporting index, for example claims(customer_id, created_at) for a join plus a date filter. Every non-aggregated column in the SELECT list must appear in GROUP BY or be functionally dependent on a grouped primary key.
Follow-up
- How would you return the top three customers by claim total within each region?
- How would you model the same data in a document store, and which query becomes harder?
- What happens to SUM and AVG when amount can be NULL?
Hold a per-tenant active cap against concurrent creates
Hard · write skew · isolation levels · constraints
A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.
Approach
- Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
- Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
- Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
- State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.
Follow-up
- A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
- The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?
Build a lightweight Express server in Node.js that exposes REST API endpoints for basic CRUD operations.
Medium · Practical Coding & Frameworks
Approach
- Define the resource and its routes up front: GET /items, GET /items/:id, POST /items, PUT or PATCH /items/:id, DELETE /items/:id. Register express.json() so request bodies are parsed. Keep an in-memory Map keyed by id for O(1) lookups behind a small repository module.
- Choose status codes on purpose. Return 201 with a Location header on create, 200 or 204 on update and delete, 404 for an unknown id, and 400 with field-level errors when the body fails validation. Validate before writing anything.
- Centralise error handling in one four-argument middleware, (err, req, res, next). In Express 4 a rejected promise in an async handler is not forwarded automatically: wrap handlers or call next(err). Express 5 forwards rejected promises itself.
- Explain the semantics. PUT and DELETE are idempotent and POST is not, so a client retry after a timeout can create duplicates unless POST accepts an idempotency key. Add limit/offset or cursor pagination to the list route before it gets large.
- Test with supertest and Jest: for each route, the happy path, a missing id and a malformed body. Keep the store behind an interface so the tests and a later database swap do not touch the route code.
Follow-up
- How would you replace the in-memory store with PostgreSQL without rewriting the route handlers?
- A client times out on POST and retries. How do you avoid creating the record twice?
- When would you use PATCH instead of PUT, and how does each treat fields the client leaves out?
Walk me through the architecture of a major system you designed in your previous role, including key decisions
Medium · System Design & Architecture
Walk me through the architecture of a major system you designed in your previous role, including key decisions, mistakes, and trade-offs.
Approach
- Open with the problem and its constraints: who used the system, the load and data sizes you actually know, and the latency or availability targets. Then sketch the boxes: clients, API layer, services, data stores and asynchronous paths.
- For two or three key decisions, give the options you considered, why you chose one and what it cost. For example: "I chose PostgreSQL over a document store for transactional integrity and accepted harder horizontal scaling." Or: "I put a queue between ingestion and processing and accepted eventual consistency."
- Name one real mistake, how you found it (an incident, a metric, a code review) and what you changed. Keep your own contribution ("I") separate from the team's.
- Prepare the stress answers: which component saturates first at ten times the traffic, what you would rebuild today, and how the system was deployed, tested and monitored.
Follow-up
- Which part of that system would fail first if traffic grew tenfold, and how would you know?
- If you rebuilt it today, what would you change first?
- Why that storage engine rather than the obvious alternative?
How would you design a high-throughput notifications service for device repair status updates?
Medium · System Design & Architecture
Approach
- Scope it first: which channels (push, SMS, email, in-app), which status transitions trigger a message, user channel preferences and quiet hours, per-repair ordering, the latency target, and rough volume (status events per day and the peak rate).
- Event pipeline: the repair system publishes status-change events to a durable log such as Kafka, partitioned by repair id so events for one repair stay in order. A notification service consumes them, applies preferences and templates, and fans out to per-channel queues. Channel workers call the providers (APNs/FCM, an SMS gateway, an email service) and record each notification's state.
- Delivery is at-least-once, so make it idempotent. Use repairId plus a status version as the idempotency key with a unique constraint, and drop events whose version is older than the last one sent so a late event cannot send a stale status.
- Plan for failure: retries with exponential backoff and jitter, a dead-letter queue, provider rate limits, a circuit breaker per provider with an optional fallback channel, and per-user throttling that collapses rapid successive updates into one message.
- Treat push as an alert, not the source of truth. The customer's status page reads current state from its own store, so a lost notification never leaves the customer with the wrong status.
Follow-up
- How do you guarantee a customer never sees 'repair complete' before 'device received'?
- The SMS provider is down for an hour. What happens to the queued messages?
- How would you add a new channel without changing the services that produce events?
What strategies would you use to transition a monolithic application into an event-driven microservices archit
Medium · System Design & Architecture
What strategies would you use to transition a monolithic application into an event-driven microservices architecture?
Approach
- Use the strangler fig pattern. Put a routing layer in front of the monolith, extract one bounded context at a time, move its traffic to the new service, then delete the old code. Pick the first seam for low coupling and a high change rate.
- Give each service ownership of its data and break the shared database gradually. Publish events with a transactional outbox, writing the row and the event in one local transaction, to avoid dual-write inconsistency. Change data capture is the alternative when the monolith's code cannot be changed.
- Design the events: versioned schemas with backward-compatible changes (a schema registry helps) and idempotent consumers under at-least-once delivery. Use sagas with compensating actions instead of distributed two-phase commit for workflows that span services.
- Put the prerequisites in place before splitting: independent CI/CD per service, distributed tracing with correlation ids, contract tests between producer and consumer, and an anti-corruption layer that translates the monolith's model.
- State the costs: eventual consistency, harder debugging and new network failure modes. For a small team, a modular monolith can be the better first step.
Follow-up
- While both the monolith and the new service write the same data, how do you keep them consistent?
- How would you roll back an extraction that is causing errors in production?
- A consumer falls hours behind on the event stream. What do you do?
Explain how you approach database indexing and caching strategies to minimize latency in read-heavy applicatio
Medium · System Design & Architecture
Explain how you approach database indexing and caching strategies to minimize latency in read-heavy applications.
Approach
- Start from the slow queries, not from guesses. Use the slow query log and EXPLAIN ANALYZE. Index the columns used in WHERE, JOIN and ORDER BY. In a composite B-tree index, put equality columns before the range column (the leftmost-prefix rule). A covering index, with INCLUDE in PostgreSQL, allows index-only scans.
- Know what an index cannot do. Wrapping the column in a function (WHERE LOWER(email) = ...) skips a plain index unless you create an expression index. A leading-wildcard LIKE '%x' cannot use a B-tree. Low-selectivity columns rarely help. Every index adds write and storage cost.
- Caching with cache-aside on Redis: read the cache, and on a miss read the database and set the key with a TTL. On writes, delete the key rather than updating it to narrow race windows. Read replicas add read capacity at the cost of replication lag.
- Name the cache failure modes and their fixes. Stampedes on expiry: request coalescing or a lock, jittered TTLs, early refresh. Hot keys: local in-process caching or replicated keys. Repeated misses for ids that do not exist: cache negative results briefly. Also state the staleness window the product can accept.
- Mention the other layers: HTTP/CDN caching for public responses and materialised views for expensive aggregates. Say which layer you would add first and why.
Follow-up
- Why delete the cache key on write instead of writing the new value into it?
- After adding an index a query got slower. What could explain that?
- One key receives most of the traffic. How do you keep it from overloading a single cache node?
How do you ensure high availability and zero-downtime deployments for critical consumer-facing APIs?
Medium · System Design & Architecture
Approach
- Redundancy: several stateless instances across availability zones behind a load balancer, with separate readiness and liveness health checks, plus a database with replicas and automated failover.
- Deployment strategies: rolling updates; blue-green with an instant traffic switch back; and canaries that roll back automatically when error rate or latency crosses the SLO. Feature flags separate deploying code from releasing behaviour.
- Graceful shutdown: on SIGTERM, fail readiness so the load balancer stops sending new requests, drain in-flight requests, then exit. Give long-lived connections such as WebSockets a reconnect path.
- Database changes use expand/contract. Add the new column as nullable, deploy code that writes both columns, backfill, switch reads, and drop the old column in a later release. Never rename in one step. Keep API changes backward compatible or versioned.
- Runtime resilience: timeouts on every outbound call, retries with backoff only for idempotent operations, circuit breakers, rate limiting, and alerts on SLO burn rate.
Follow-up
- How would you rename a heavily used column without downtime?
- The canary shows a small but real rise in errors. What do you do, and what decides it?
- How do WebSocket or other long-lived connections survive a rolling deploy?
Describe a technical failure or outage you were involved in. What was the root cause, and what did you impleme
Medium · Behavioral & Past Experience
Describe a technical failure or outage you were involved in. What was the root cause, and what did you implement to prevent it from happening again?
Approach
- Lead with impact in one or two sentences: who was affected, for how long, and which metric showed it. Say how it was detected (an alert or a customer report) and what your role was.
- Walk through the investigation in order. Check what changed first: a deploy, a config change, a traffic shift or a dependency. Then describe how you narrowed it down, for example by comparing affected and unaffected hosts or rolling back one change at a time.
- Separate the trigger from the root cause. For example: "The deploy triggered it, but the cause was a downstream call with no timeout that exhausted the thread pool." Say how you confirmed the cause, for example by reproducing it in staging or by showing the fix removed the symptom.
- Put mitigation before the permanent fix (rollback or turning a flag off), then list the specific prevention steps you put in place: a regression test that reproduces the failure, an alert on a leading indicator, a CI guard or a runbook. Mention the blameless postmortem.
- What sinks this answer: blaming another team, a vague cause like "a bug in the code", or prevention steps you cannot show were adopted. Own your part of what went wrong.
Follow-up
- How did you confirm that was the root cause and not a coincidence?
- Which alert would have caught this earlier, and why did it not exist?
- What did you personally change in how you work after this incident?
Suggested practice checklist
Each day targets one group of reported questions: React and Express work, SQL, algorithms, system design, then behavioral stories and a full lab rehearsal. Day 1 builds the practice environment the later days reuse.
Day 1 — Map the loop and build a practice repo
- Write the four reported rounds in order and, for each, one line on what you will need to show: a recruiter conversation, an online coding screen on Karat or HackerRank, a hiring manager conversation, and a virtual on-site with a lab, an architecture session and a behavioral conversation.
- Create a scratch repo with an Express app and a Jest test (or Spring Boot and JUnit if Java is your language) and a small React app with one test, so Days 2 to 4 start from a working setup.
- Write a two-minute summary of your background and decide which language you will choose when given the option.
Deliverable: A one-page round map, a runnable scratch repo with a passing test in each app, and a written two-minute introduction.
Day 2 — React state, bugs and side effects
- Build a small claim-submission form component and introduce four bugs on purpose: mutating a state array, a stale closure in the submit handler, an effect missing a dependency, and a controlled input that loses its value. Fix each and write a React Testing Library test that failed before the fix.
- Outline a spoken answer on global state and side effects: local state, lifting state, context, a reducer, an external store and a server-state cache, plus effect cleanup for subscriptions and requests.
Deliverable: A buggy component with a list of fixed bugs each paired with a failing-then-passing test, and a spoken-answer outline for global state and side effects.
Day 3 — Express CRUD and payload validation
- Build an Express CRUD service for device repair requests with an in-memory store: POST, GET one, GET list, PUT/PATCH and DELETE, with correct status codes and a shared error handler.
- Write a standalone validate function for a JSON payload with business rules, such as required fields, serial-number format and allowed status values, and return all errors rather than the first.
- Test malformed input with Jest and supertest: missing body, wrong types, null values, unknown fields and oversized strings.
Deliverable: A CRUD service with passing tests, including a malformed-payload test for every endpoint, and a validator backed by a table of edge cases.
Day 4 — SQL versus NoSQL and aggregate queries
- Write a one-page comparison of SQL and NoSQL for a claims and devices domain covering schema, joins, transactions, consistency, scaling and the case where you would pick each.
- Create claims and devices tables in PostgreSQL and write three aggregate queries: totals per device model with GROUP BY and HAVING, a join across a one-to-many relation without inflating the totals, and a LEFT JOIN that keeps devices with no claims.
Deliverable: A comparison page, three queries with sample output, and a worked answer to the practice question.
Day 5 — Algorithm set under timed conditions
- Solve unique pairs that sum to a target, balanced brackets and interval merging in your strongest language, covering duplicates, an empty list, interleaved bracket types and touching intervals.
- Reverse a linked list iteratively and recursively, and write the complexity and the recursion-stack cost for each.
- Write the serial-number parser: trim, normalise case, strip separators, validate length and character set, and reject bad entries without throwing.
Deliverable: Five working solutions with at least three edge-case tests each and a one-line complexity statement per solution.
Day 6 — System design and architecture trade-offs
- Draw the architecture of a system you built and write what you would change, what debt it carries, and what fails first at ten times the traffic.
- Design the repair-status notifications service: producers, a queue, workers, idempotent delivery, retries with a dead-letter queue, channels and per-device ordering.
- Write a strangler-style plan for moving a monolith to event-driven microservices, including data ownership and how events are published reliably.
- Write your approach to indexes, caching (cache-aside, invalidation) and zero-downtime deploys (rolling or blue-green, backward-compatible schema changes, health checks).
Deliverable: A past-system diagram with a trade-offs list, a notifications design sketch, a migration plan and a one-page caching and deployment checklist.
Day 7 — Behavioral stories and a full lab rehearsal
- Write outlines for the ambiguity, disagreement, debt-versus-deadline, testing and version-control, and outage stories, each with the situation, your decision, the result and what you would change.
- Take your Day 3 repo, break three tests and fix them in one sitting while narrating aloud, then explain how your code handles malformed input.
- Write three questions for the hiring manager about testing, deployment and incident practices on the team.
Deliverable: Five story outlines, a recorded lab rehearsal with notes on where you went quiet, and a list of three questions.
Stories for the Asurion behavioral rounds
Candidates report a behavioral conversation in the final on-site and an experience-focused discussion with the hiring manager. The reported prompts cover unclear requirements, disagreement, technical debt against a deadline, testing and version-control habits, and an outage, so build stories that show a decision you owned, the trade-off and the result.
- Describe a technical failure or outage you were involved in. What was the root cause, and what did you implement to prevent it from happening again?
- Walk me through the architecture of a major system you designed in your previous role, including key decisions, mistakes, and trade-offs.
Describe a time when you received ambiguous requirements for a technical feature. How did you clarify scope an
Medium · Behavioral & Past Experience
Describe a time when you received ambiguous requirements for a technical feature. How did you clarify scope and deliver?
Approach
- Pick a feature where the gap in the requirements was real and the decision was yours, such as a claim or repair-request form whose rules were never written down. Describe the gap in one or two sentences, then spend the rest on what you did.
- Show your clarification method: list the open questions, put them to the product manager, designer or QA contact, record the answers in the ticket, and write down the assumptions that stayed unanswered with the name of the person who confirmed each.
- Show how you reduced risk: ship the smallest slice that was unambiguous first, behind a feature flag or in a demo, get a reaction, then extend instead of building the whole guess.
- Close with the outcome and a checkable detail: which assumption turned out wrong, what it cost to change, and what you now require at kickoff, such as acceptance criteria with example inputs and outputs.
- The mistake that sinks this story is either guessing and being lucky, or asking once and waiting. Show that you kept moving while confirming.
Follow-up
- What did you do when the stakeholder was not available to answer?
- Which assumption turned out to be wrong, and what did it cost?
- How do you write acceptance criteria now to avoid the same ambiguity?
Tell me about a situation where you had a strong technical disagreement with a teammate or lead. How did you r
Medium · Behavioral & Past Experience
Tell me about a situation where you had a strong technical disagreement with a teammate or lead. How did you resolve it?
Approach
- Choose a real technical disagreement where both options were defensible, such as the data model, a synchronous call versus a queue, a library choice or a rollout approach. A story where the other person was plainly wrong hides your reasoning.
- State the other person's position fairly in one sentence, then yours, and name the criterion that mattered: latency, operating cost, delivery date, testability or how easy the decision was to reverse.
- Show how you resolved it with evidence rather than seniority: a small prototype, a benchmark, a load test, a written comparison, or agreeing criteria and letting the lead or a third engineer decide.
- Say how it ended, including if you lost: you disagreed, committed, and helped the chosen approach succeed, or a hybrid won. Mention how the working relationship looked afterwards.
- The mistake that sinks this story is a tale where you were right and everyone came around, or one with no technical content to check.
Follow-up
- Would you make the same call today, knowing how it turned out?
- What would you do if the lead overruled you and you still thought the decision was wrong?
- How do you raise a disagreement in a code review without stalling the pull request?
Walk me through a project where you had to balance technical debt against a strict product launch deadline.
Medium · Behavioral & Past Experience
Approach
- Name the debt concretely: a duplicated validation module, missing tests around a risky flow, hard-coded configuration or a shortcut in the data model. State the launch deadline and what depended on it.
- Lay out the options with costs: pay the debt first and slip the date, ship around it, or ship with it and schedule the repair. Say who you consulted, such as the product manager and your lead, and what you cut. Cutting scope is easier to defend than cutting tests on the risky path.
- Describe the guard rails you put in place: a feature flag, a test on the riskiest path, a ticket with an owner and a trigger, and a comment in the code explaining the shortcut.
- Report the payback honestly: whether the debt was repaid, how much it slowed later work, and whether the launch hit its date.
- The mistake that sinks this story is a vague shortcut that was never repaid, or the claim that cutting corners had no consequences.
Follow-up
- How did you decide which debt was safe to carry and which was not?
- How do you persuade a product manager to schedule the cleanup after launch?
- What would you have cut if the deadline had moved sooner?
Outline your experience with automated unit testing and version control practices in a fast-paced continuous i
Medium · Behavioral & Past Experience
Outline your experience with automated unit testing and version control practices in a fast-paced continuous integration team.
Approach
- Answer in layers using one concrete project: unit tests for pure logic in Jest, JUnit or Mocha (whichever you really used), integration tests for API routes and database access, and a few end-to-end checks. Say what you mock, such as external network calls and clocks, and what you keep real.
- Describe your actual version-control workflow: short-lived branches, small pull requests, review before merge, a protected main branch, your merge or rebase policy, and how you revert a bad merge.
- Explain how continuous integration fits: tests and linting run on every pull request in Jenkins or GitHub Actions if you used them, and a failing build blocks the merge. Cover how you keep the suite fast and how you treat flaky tests, by fixing causes such as shared state or timers rather than rerunning.
- Add one story with a result: a regression a test caught before release, or a flaky suite you repaired. Quote only numbers you measured.
- The mistake that sinks this answer is a list of tools with no decisions behind it, or treating a coverage percentage as the goal.
Follow-up
- How do you test code that calls a third-party API?
- A test passes locally and fails in CI. What do you check first?
- How do you decide what not to unit test?