cswork / libraryOpen practice workspace

Interview guides · Chase

Chase Software Engineer Interview Guide

Chase Software Engineer interview guide: the five reported rounds, HackerRank to final round, coding, design and behavioral questions, and a 7-day plan.

The 60-second brief

Chase is the consumer and commercial banking brand of JPMorgan Chase. Role descriptions for this position mention trading systems, mobile banking features and AI-driven tooling, along with performance, cloud integration and data processing at volume. The listed responsibilities are practical: turn business requirements into secure code, take part in sprint planning, review other engineers' code, keep technical documentation, work with product managers, architects and operations staff, and improve automation and CI/CD pipelines.

This guide is for candidates applying to Chase as a Software Engineer, mainly in Java or Python (Scala also appears in the requirements candidates describe). It covers the five reported rounds, the reported coding, database, design and behavioral questions, the stack the role lists (Agile, CI/CD, with AWS, Spark, Kafka and AI/ML workflows as extras), and a plan to prepare in one week. Requirements reportedly vary by level, with Software Engineer II and III both mentioned and two or more years of corporate experience described as common.

Candidates report five rounds over roughly four to six weeks: Initial Screening, HackerRank Assessment, Pair Programming Session, Architectural Discussion and Final Round. Pacing reportedly varies by region and business unit.

Focus areas

  • Coding Interviews
  • System Design
  • Database Performance Optimization

Overview

The loop moves from a screen, to an online coding assessment, to live collaborative coding, to a design conversation, to a final meeting with hiring managers or team leads. Preparation therefore splits into three skill sets: writing correct code alone against a platform (graph, string and matrix problems), coding while talking to another person, and reasoning about services out loud. Candidates also describe database performance and caching as recurring topics, so be ready to take a slow data-retrieval problem apart step by step.

The reported technical questions are narrow enough to rehearse by name. Cycle detection in an undirected graph, the longest common prefix of a set of strings, and matrix multiplication test fundamentals you can solve and then explain with complexity. The ConcurrentHashMap question tests whether you know your language's concurrency primitives, not only its syntax. The design questions are a CRUD service wrapping an external background-check API, a notification API, microservices versus RESTful services, and scalability for a new feature. Three of those are service-building questions and one is a comparison of architectural styles, so practise both a whiteboard design and a short conceptual explanation.

Candidates report that the loop rewards clear thinking out loud, so rehearse by speaking: state assumptions, name the brute-force version, then improve it. For the final round and for any conversation about past work, have four stories ready: a conflict with a teammate, a project you are proud of with your own contribution isolated, a time requirements were ambiguous, and an example of mentoring or cross-functional work. The later sections of this guide give a structure for each.

The role lists Agile delivery and CI/CD as must-haves, with cloud (AWS) and big-data tools (Spark, Kafka) as nice-to-haves. You do not need to be an expert in all of those, but be able to say what you have used, how your team shipped changes, and how a design you describe would be deployed and monitored. If you name a technology, expect to explain why you chose it over the alternative.

The Chase interview process

Candidates describe five stages, listed below in the reported order. Treat the descriptions as what candidates report rather than a published process, and expect details to vary by team and location.

Initial Screening

The first stage is an initial screening that candidates describe as a check on basic qualifications. Candidates also report that early stages are often remote, through a video call or an assessment platform. Treat it as a short, structured conversation about your background: it sets the tone for the rest of the process, and it is the point where you can learn the shape of the loop for your team and region.

What to demonstrate

  • Whether your experience matches the Software Engineer requirements, including Java or Python and delivery in an Agile team
  • How clearly you summarise your background and recent work
  • Practical fit such as level (II or III), location and timing

How to prepare

  • Write a concise walk through your last two projects, naming the language, the frameworks and your own part in each
  • List which of Agile, CI/CD, AWS, Spark, Kafka and AI/ML workflows you have used directly, and one concrete sentence on each
  • Ask which rounds your team's loop includes and whether the coding stage is an online assessment, since candidates report that it varies by region and business unit

HackerRank Assessment

This stage is an online coding assessment on HackerRank, reported as a test of coding skill. Candidates describe questions from easy to medium. Practise the problem families reported for this loop, such as graph traversal, string handling and matrix operations, and rehearse working alone in a platform editor where you must read the problem, choose a structure and get working code through the provided tests.

What to demonstrate

  • Correct, working code in your chosen language
  • Command of data structures and algorithms, especially graphs, trees, strings and recursion
  • Handling of edge cases and constraints in the problem statement

How to prepare

  • Solve cycle detection in an undirected graph both with a depth-first search that tracks the parent and with union-find, and note when each is preferable
  • Write the longest common prefix solution and the matrix multiplication solution from memory in your main language, then add the dimension checks and empty-input cases
  • Practise inside a plain online editor without autocomplete, running only the visible sample tests before you submit

Pair Programming Session

Candidates describe a pair programming session as a collaborative coding exercise in which you demonstrate problem-solving with another engineer. The format means your reasoning is visible while you type. Expect to talk through your plan before coding, adjust when the other person suggests something, and fix defects as they appear. A database-performance problem or a small service is a natural fit for this format, given the topics candidates report.

What to demonstrate

  • Collaboration and communication while coding live
  • Working, readable code and a systematic approach to debugging
  • Language depth, such as Java 8 Streams or Python decorators, as candidates report these as examples of language knowledge

How to prepare

  • Pair with a friend or record yourself solving the ConcurrentHashMap, cycle detection and database retrieval problems, narrating each decision
  • Practise taking a hint without losing your plan: restate what the other person suggested, test it, then continue

Architectural Discussion

The architectural discussion is where candidates report talking through system design and architecture concepts. The reported design questions include a CRUD service integrating an external background-check API, a notification API, microservices versus RESTful services, and designing for scalability in a new feature. Candidates also mention scalability and availability, relational versus NoSQL choices, and resilience as evaluation areas, and that some interviewers push back on design choices.

What to demonstrate

  • Reasoning about scalability, availability and failure handling
  • Database choice justified by access patterns
  • Ability to explain trade-offs between API and service architectures
  • Holding a position with evidence while staying open to feedback

How to prepare

  • Design the background-check integration end to end: request identity for safe retries, a pending versus confirmed state, a timeout and retry policy, and what the user sees when the external API is slow
  • Design the notification API with separate paths for accepting requests and delivering them, a queue between them, retry with backoff, and delivery tracking
  • Prepare a short explanation of microservices versus RESTful services that makes clear they are different axes (deployment and ownership boundaries versus an interface style)
  • For each design, write one sentence on which datastore you would choose and why, relational or NoSQL

Final Round

Candidates describe the final round as interviews with hiring managers or team leads to assess overall fit. Some report that final stages can be in person or a virtual multi-interview day, depending on team and location. Expect a discussion of your past work in depth, behavioral prompts and follow-ups on earlier answers, so keep your stories and your design reasoning consistent with what you said in earlier rounds.

What to demonstrate

  • Overall fit with the team as candidates describe it
  • Ownership and the specifics of your past projects
  • Teamwork, conflict handling and working with ambiguity

How to prepare

  • Write four stories in situation, task, action, result form: a teammate conflict, a project you are proud of, an ambiguous requirement and mentoring or cross-functional work
  • For each project on your CV, list the decision you made, the alternative you rejected and what you would change now
  • Prepare questions for the hiring manager about how the team ships changes, reviews code and handles on-call or production issues

Where candidates lose points

Solving the graph question as 'visited means cycle', which wrongly flags an edge back to the node you just came from in an undirected graph

Track the parent in the depth-first search, or use union-find and report a cycle when both endpoints already share a root. Test a two-node graph, a self-loop, a disconnected graph and a plain tree before you finish.

Reciting that ConcurrentHashMap 'locks per segment' and stopping there

Know the modern mechanics: compare-and-swap into an empty bin, locking only the head node of a bin on collision, lock-free reads, tree bins for long chains, no null keys or values, and weakly consistent iterators. Know what compute and merge guarantee that get-then-put does not.

Answering the slow database retrieval question with 'add an index' before measuring anything

Start with how you would find the cause: the query plan, the row counts, whether the program issues one query per item, whether it fetches columns or rows it never uses. Then propose the fix that matches, such as an index, a batched query, pagination or a cache with a clear invalidation rule.

Presenting the background-check CRUD service as a single synchronous call to the external API

Say what happens when that API is slow or down. Save the request with a status, give each request an identity so a retry cannot duplicate it, process the external call outside the user's request path, and show pending and confirmed as different states.

Telling project and conflict stories entirely in 'we', or defending a design choice only by assertion

Use 'I' for what you personally decided and built. In design discussions, back each choice with a reason you can state, such as an access pattern or a failure mode, and change your position when the pushback has a good argument.

Questions & practice

How would you detect a cycle in an undirected graph?

Medium · Technical & Algorithmic Foundations

Approach

  1. Default answer: DFS while tracking each vertex's parent. If you reach an already-visited neighbour that is not the vertex you came from, the graph has a cycle. Start a DFS from every unvisited vertex so that disconnected components are covered. Cost is O(V + E) time and O(V) space for the visited set and the stack.
  2. Alternative: union-find over the edge list. For each edge (u, v), if find(u) == find(v) the edge closes a cycle; otherwise union them. With path compression and union by rank this runs in O(E α(V)), and it suits edges that arrive one at a time.
  3. Cheap sanity check: a forest with V vertices and C components has exactly V - C edges, so E > V - C proves a cycle. Use it to confirm your result. It does not locate the cycle.
  4. Edge cases: a self-loop is a cycle. With parallel edges, the 'neighbour is not my parent' test misses the two-edge cycle, so track the parent edge id instead of the parent vertex. In Python, use an iterative DFS with an explicit stack, because deep graphs hit the default recursion limit of about 1000.

Follow-up

  • How would you return the vertices of the cycle rather than just true or false?
  • What changes for a directed graph? (Three-colour DFS, where an edge to a grey vertex on the current path is a back edge.)
  • If edges stream in and you must report the first edge that creates a cycle, which approach would you use and why?

Explain the internal mechanics of a ConcurrentHashMap and its performance implications.

Medium · Technical & Algorithmic Foundations

Approach

  1. Java 8+ design: a table of bins. A put into an empty bin uses a CAS with no lock. A put into an occupied bin synchronizes only on that bin's first node, so writers to different bins do not block each other. Contrast this with the pre-Java-8 version, which had segments, each a ReentrantLock, sized by concurrencyLevel.
  2. Reads: get() takes no lock. It relies on volatile reads of the table and of node val/next fields, and it reflects the most recently completed updates. Iterators are weakly consistent and never throw ConcurrentModificationException.
  3. Collisions and resizing: a bin becomes a red-black tree once it holds 8 nodes (TREEIFY_THRESHOLD) and the table has at least 64 slots; below that size the table resizes instead. Worst-case lookup in a hot bin drops from O(n) to O(log n). During a resize, several threads can help move bins, and ForwardingNode markers point readers at the new table.
  4. Size and nulls: the count is spread over baseCount plus CounterCells (the LongAdder idea), so size() is only an estimate under concurrent writes; mappingCount() returns a long. Null keys and values are rejected because get() returning null must mean 'absent'.
  5. Performance trap: check-then-put is not atomic. Use putIfAbsent, computeIfAbsent, compute or merge, which are atomic per key. Keep the mapping function short, and never let it modify other keys of the same map: that can block other writers or throw IllegalStateException('Recursive update').

Follow-up

  • How does it compare with Collections.synchronizedMap or Hashtable under read-heavy and write-heavy load?
  • How would you keep a thread-safe per-key counter? (merge(key, 1L, Long::sum), or LongAdder values created with computeIfAbsent.)
  • Why can a size() call be stale, and when does that matter?

Given a string, how would you find the longest common prefix?

Medium · Technical & Algorithmic Foundations

Approach

  1. Clarify the input first. The classic version takes an array of strings and returns their longest common prefix. Confirm that, and confirm the case-sensitivity and character-set rules, before you write code.
  2. Vertical scan: compare the character at index i across every string, and stop at the first mismatch or at the end of the shortest string. This is O(S) time in the worst case, where S is the total number of characters, with O(1) extra space. It exits early when the strings diverge near the start.
  3. Shortcut: the LCP of the lexicographic min and max strings equals the LCP of the whole set. Python's min() and max() find them in O(S) with no sort, and you then compare only those two. Sorting first also works but adds O(n log n) string comparisons.
  4. Edge cases: an empty list returns an empty string; any empty string in the list returns an empty string; a single string returns itself; strings that are prefixes of one another stop at the shorter length.
  5. Mention a trie when the question becomes many prefix queries over a changing set. Building it costs O(S) time and space, and you walk down from the root while the node has exactly one child and does not end a word.

Follow-up

  • LCP is associative: LCP(a, b, c) = LCP(LCP(a, b), c). How would you use that for divide and conquer, or to split the work across machines?
  • How would binary search on the prefix length work, and what does it cost?
  • How would you find the longest common prefix between any two strings in the set rather than across all of them? (Sort, then compare adjacent pairs.)

Implement a solution for matrix multiplication or circle radius calculations.

Medium · Technical & Algorithmic Foundations

Approach

  1. Matrix multiplication: validate the shapes first. An n x m matrix A times an m x p matrix B gives an n x p result C, and a mismatched inner dimension is an error. Also reject ragged rows, because a Java int[][] can be jagged.
  2. Core algorithm: C[i][j] = sum over k of A[i][k] * B[k][j]. That is O(n·m·p) time, O(n^3) for square matrices. Use i-k-j loop order and hoist A[i][k] so the inner loop walks B and C row by row, which suits row-major memory layout.
  3. Correctness details: accumulate in long or double to avoid integer overflow. For sparse inputs, store only the non-zeros (CSR or row -> {col: value}) and skip the zero terms. You can mention Strassen at about O(n^2.81) and BLAS-backed libraries such as NumPy for production work.
  4. Circle calculations: area = πr² and circumference = 2πr, and from an area the radius is r = sqrt(A / π). Use Math.PI or math.pi, reject negative or NaN input, and compare floating-point results with a tolerance rather than ==. Round only at output, to the precision the prompt asks for.
  5. Testing: multiply by the identity, try a non-square pair such as 2x3 times 3x2, use a 1x1 case and a dimension mismatch, and check one product you computed by hand.

Follow-up

  • How would you parallelise the multiplication? (Each output row or block is independent.)
  • How would you compute A^k efficiently? (Exponentiation by squaring, O(n^3 log k).)
  • When would floating-point error matter in the circle math, and how would you test for it?

How do you optimize a program that takes too long to retrieve data from a database?

Medium · Technical & Algorithmic Foundations

Approach

  1. Measure before changing anything. Find which query is slow, and split database execution time from network time and ORM object hydration. Tools: app timings or APM traces, plus the database's slow-query log (pg_stat_statements in Postgres, the slow_query_log in MySQL).
  2. Read the plan with EXPLAIN (ANALYZE). Look for full scans on large tables, row estimates far from actual counts, and sorts spilling to disk. Fix with a composite index: equality columns first, then the range or ORDER BY column, made covering if that saves heap lookups. Rewrite non-sargable filters, for example DATE(created_at) = x becomes a half-open range on created_at.
  3. Fix application access patterns. Remove N+1 queries from lazy loading by using a join or batch fetch (JPA JOIN FETCH, or an IN list). Select only the columns you need, and replace deep OFFSET pagination with keyset pagination (WHERE id > :last ORDER BY id LIMIT n).
  4. Infrastructure levers: right-size the connection pool (HikariCP in Java), add cache-aside caching with a TTL for hot read-mostly data, or move heavy reads to replicas that tolerate replication lag. For expensive aggregates, use a precomputed table or materialized view.
  5. Re-measure p95 and p99 latency after each change, and state the costs. Every index slows writes and uses storage, a cache adds an invalidation problem, and replicas can return stale reads.

Follow-up

  • The query is fast when you run it by hand but slow in production. What do you check? (Lock contention, a different plan for different parameter values, a cold cache, a differently sized dataset.)
  • How do you keep a cache consistent with the database when rows are updated?
  • When would you denormalise or precompute instead of indexing?

Find version gaps and relay lag with window functions

Medium · window functions · event tables · frame semantics

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

Approach

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

Follow-up

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

Explain why the owner filter ignores the listing index

Medium · composite index · query plans · sort elimination

The only index on resource is (tenant_id, status, updated_at DESC, resource_id DESC). A new endpoint returns one user's resources across all statuses, newest created first: WHERE tenant_id = $1 AND owner_user_id = $2 ORDER BY created_at DESC LIMIT 20. On a tenant with 2M rows it takes 900 ms and EXPLAIN shows a sort above a large scan. Explain precisely why the existing index cannot serve it, give the index that can, and state which of these the new index still will not help: owner_user_id alone across tenants; the same query ordered by updated_at. PostgreSQL 16.

Approach

  1. Separate the two jobs an index does. For filtering, a composite btree is seekable only on a left prefix, so with no predicate on status the scan can at best range over tenant_id and test owner_user_id per row; PostgreSQL 16 has no btree skip scan to jump the unconstrained column.
  2. For ordering, the index is sorted by (status, updated_at) within a tenant and not by created_at, so the LIMIT cannot stop early: every matching row is read and then sorted. That is the 'Sort Method: top-N heapsort' line, and it is why the plan reads 2M rows to answer with 20.
  3. Derive the replacement from the access path — equality, equality, then the ordering column: CREATE INDEX CONCURRENTLY ON resource (tenant_id, owner_user_id, created_at DESC). The scan seeks to the (tenant, owner) range and walks 20 entries in order, so the Sort node disappears along with the row-read.
  4. Treat INCLUDE (title, status) as conditional, not free. An index-only scan still visits the heap for any row whose page is not marked all-visible, so on a table taking 1.2k writes/second the win depends on autovacuum keeping the visibility map current, and the wider index costs more on every insert.

Follow-up

  • 90% of rows are status='active'. Would a partial index WHERE status = 'active' change your answer, and for which of the three queries?
  • A dashboard runs this for 40 owners in one page load. What changes about the design?

Design a service to handle user inputs (CRUD operations) and integrate with an external background-check API.

Medium · System Design & Architecture

Approach

  1. Clarify scope. Which entities are involved (an applicant record and a background-check request)? What are the expected volumes, and which fields are sensitive? What does the vendor API offer: synchronous answers or async results via webhook, rate limits, typical turnaround?
  2. CRUD layer: a REST resource such as /applicants with POST, GET, PUT/PATCH and DELETE, input validation, and a relational store. Give the check its own status machine: PENDING -> SUBMITTED -> IN_PROGRESS -> COMPLETED or FAILED. Use optimistic locking (a version column, or ETag with If-Match) so concurrent updates are not lost.
  3. Decouple the vendor call. In one transaction, write the applicant row and an outbox row. A worker reads the outbox, calls the vendor with an idempotency key generated per check, and retries with exponential backoff and jitter. A circuit breaker sits on the vendor client, and checks go to a dead-letter queue after N failures. The API returns 202 Accepted with the check id.
  4. Results path: a webhook endpoint verifies the vendor's signature (HMAC), deduplicates by vendor event id, and moves the status forward only along valid transitions. A scheduled poller catches checks stuck past a deadline in case a callback is lost.
  5. Data protection: TLS everywhere, field-level encryption for identifiers such as SSNs, least-privilege access, and an audit log of reads and changes. Deletion may need to be a soft delete if retention rules apply.

Follow-up

  • The vendor is down for several hours. What do users see, and how does the backlog drain afterwards?
  • A webhook arrives twice, or before your own write has committed. How do you handle it?
  • The applicant edits their details after the check was submitted. What happens to the in-flight check?

How would you design a notification-API that is both robust and scalable?

Medium · System Design & Architecture

Approach

  1. Fix scope first. Which channels are in scope (email, SMS, mobile push, in-app)? What is the volume and the burst shape? Do transactional messages such as one-time codes have different latency needs from bulk sends? Is the delivery guarantee at-least-once or best-effort?
  2. Write path: the API validates the request, checks the user's preferences and opt-outs, stores a notification record, and returns 202 with an id. It then publishes to a broker (Kafka or SQS), split by channel and priority so bulk traffic cannot starve urgent messages. Channel workers render templates and call the providers (APNs/FCM, an email service, an SMS gateway).
  3. Reliability: delivery is at-least-once, so the API takes a client idempotency key and workers dedupe by notification id. Failures get exponential backoff with jitter, a circuit breaker per provider, a fallback provider where one exists, and a dead-letter queue. Track status (queued, sent, delivered, failed) from provider callbacks.
  4. Scale: keep the API stateless behind a load balancer and scale workers on queue depth. Rate-limit per client and per user with a token bucket, and stay under provider rate limits. Partition by user id if per-user ordering matters.
  5. Observability: queue depth and consumer lag, success and latency per provider and channel, and DLQ size, with alerts on each.

Follow-up

  • Can you guarantee exactly-once delivery? If not, how close can you get?
  • How would you add quiet hours and time-zone-aware scheduling?
  • One provider starts timing out at peak. Walk through what happens to the queue and to users.

What are the architectural differences between microservices and RESTful services?

Medium · System Design & Architecture

Approach

  1. Open by naming the category difference. Microservices is a way to decompose a system into independently deployable services, each owning its data and a business capability. REST is a style for designing a client-server API over HTTP. They are not alternatives: a monolith can expose a REST API, and microservices can talk over gRPC or messaging.
  2. REST specifics: resources identified by URIs, a uniform interface, stateless requests, cacheable responses and a layered system. Use HTTP verbs for their meaning: GET is safe and idempotent, PUT and DELETE are idempotent, POST is not. Use status codes accurately. HATEOAS is part of the definition but is rarely fully implemented.
  3. What microservices buy: independent deploys and scaling, fault isolation and team autonomy. What they cost: network latency and partial failure, no local cross-service transactions (sagas with compensating steps replace them), eventual consistency, contract versioning, and the need for distributed tracing and centralized logging.
  4. Close with a decision rule. A modular monolith is simpler while one team owns the code and the scaling needs are uniform. Split out a service when a part needs independent deploys, different scaling, or a different data store.

Follow-up

  • How should two services communicate: synchronous REST/gRPC calls or asynchronous events, and what decides it?
  • How do you handle a business operation that must update data owned by two services?
  • When would you choose gRPC or GraphQL over REST?

How do you account for system scalability when designing a new feature?

Medium · System Design & Architecture

Approach

  1. Start with numbers. Estimate expected users, average and peak requests per second, read/write ratio, data growth, retention and the latency target. For example, 10M requests a day is about 116 per second on average (10M / 86,400), so size for the peak multiple, not the average.
  2. Find the first bottleneck, which is usually the data store. Keep application servers stateless so they scale horizontally. Add indexes for the new access paths, cache-aside caching with a TTL for read-heavy data, and read replicas where stale reads are acceptable. Choose a partition key early if the data will outgrow one node, and say which queries it makes expensive.
  3. Move slow or non-critical work (emails, reports, third-party calls) to a queue with workers. Add backpressure and rate limits so a spike degrades the feature instead of the whole system.
  4. Ship safely: put the feature behind a flag, roll it out to a percentage of traffic, and load-test the expected peak first. Set an SLO with dashboards and alerts on latency, error rate and saturation, and configure autoscaling on a metric that leads the load.
  5. Name the trade-off. Avoid building sharding or multi-region for traffic that does not exist yet, and say which consistency the feature actually needs.

Follow-up

  • Traffic grows tenfold. Which component fails first, and how would you know before users do?
  • How would you load-test this feature before launch?
  • Latency regresses after rollout. What is your rollback path?

Read latency spikes on a sixty-second sawtooth

Medium · cache stampede · single-flight · ttl

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

Approach

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

Follow-up

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

Suggested practice checklist

Seven days built around the reported rounds: coding fundamentals for the assessment, live coding and database performance for the pair session, the four reported design questions, and behavioral stories for the final round.

Day 1 — Map the loop and set up your toolkit

  • Write the five reported rounds in order and next to each note what the format tests: a screen, an online assessment, live coding, design discussion and a hiring-manager conversation.
  • Choose Java or Python as your single interview language and write a one-page cheat sheet of its collections, string handling and, for Java, Streams or, for Python, decorators.
  • Write a two-minute summary of your last two projects, listing which of Agile, CI/CD, AWS, Spark and Kafka you used.

Deliverable: A round map, a one-page language cheat sheet and a written background summary.

Day 2 — Coding assessment fundamentals

  • Solve cycle detection in an undirected graph twice, with depth-first search carrying the parent and with union-find, and test a self-loop and a disconnected graph.
  • Write the longest common prefix solution with a vertical scan across a list of strings, then add a sorted-first-and-last variant and compare them.
  • Write matrix multiplication with dimension validation, then discuss loop order and the cost; for the circle task, list the ways the input could be defined and ask which applies.

Deliverable: Working code in your interview language for cycle detection, longest common prefix and matrix multiplication, with complexity written beside each.

Day 3 — Concurrency and database performance

  • Write a one-page explanation of ConcurrentHashMap: bins, compare-and-swap on empty bins, head-node locking, tree bins, atomic compute and merge, and weakly consistent iteration.
  • Write a diagnostic checklist for slow data retrieval: measure, read the query plan, look for one-query-per-row access, check indexes and fetched columns, then consider batching, pagination and caching.

Deliverable: A ConcurrentHashMap explanation, a slow-retrieval checklist and two solved SQL practice problems.

Day 4 — Rehearse the pair programming format

  • With a partner, solve one of the day 2 problems live while narrating assumptions, and ask your partner to interrupt with a suggestion you must incorporate.
  • Re-solve the cycle detection problem iteratively to remove the recursion limit, and explain the change aloud.

Deliverable: One recorded or partnered live-coding session, an iterative cycle detector and a spoken debugging walkthrough.

Day 5 — Design the two service questions

  • Design the CRUD service with the external background-check integration: data model, request identity, status states, retry and timeout policy, and failure behaviour for the user.
  • Design the notification API: accept path, queue, workers, retry with backoff, delivery status and what you do when a downstream channel is down.
  • For both designs, choose relational or NoSQL for the main data and write a sentence explaining the access pattern behind the choice.

Deliverable: Two one-page design sketches with a stated datastore choice and a failure plan for each.

Day 6 — Architecture trade-offs and scalability

  • Write a half-page comparison of microservices and RESTful services that treats them as different dimensions, and say when a monolith with a REST interface is the better choice.
  • Write a repeatable scalability checklist for a new feature: expected traffic, read and write paths, the bottleneck at ten times the load, caching, statelessness and degraded behaviour.
  • Practise defending one of your day 5 designs against a challenge, holding the choice with a stated reason or changing it when the challenge is right.

Deliverable: A comparison page, a scalability checklist and a recorded defence of one design.

Day 7 — Behavioral stories and full run-through

  • Write four stories in situation, task, action, result form: teammate conflict, a project you are proud of, ambiguous requirements and mentoring or cross-functional work. Mark which parts are your own decisions.
  • Say each story aloud and trim it until the reasoning, not the setup, takes most of the time.
  • Do a final timed run: one coding problem aloud, one design question aloud and one behavioral story, then note what to fix.

Deliverable: Four written stories, spoken versions of each and a note of the weakest point from the final run.

Behavioral stories for the Chase final round

Candidates report that the final conversation with hiring managers or team leads covers overall fit, and the reported behavioral questions cover conflict, ownership, ambiguity and mentoring. For a Software Engineer, the strongest stories name a decision you made, the alternative you rejected and a result you can support, with the team's contribution separated from yours.

  1. Tell me about a time you had to explain a technical trade-off to someone outside engineering.

Describe a time you had a conflict with a teammate and how you resolved it.

Medium · Behavioral & Leadership

Approach

  1. Choose a real disagreement where both positions had merit, such as a design choice, a review comment or a delivery trade-off, and where you personally acted. A conflict that was only a misunderstanding gives you little to show.
  2. Describe the other person's position fairly in one or two sentences, then state yours. If you cannot explain their view in terms they would accept, the story will sound one-sided.
  3. Spend most of the answer on what you did: a direct conversation first, then a shared fact (a benchmark, a prototype, a log, a requirement) that moved the discussion off opinion. Involve a manager or tech lead only after the direct attempt, and say why.
  4. Close with the outcome and the relationship afterwards. State what shipped, whether the decision turned out right, and what you changed in how you handle disagreements.
  5. The answer sinks when it paints the teammate as the problem, or when 'we reached a compromise' hides what was actually decided.

Follow-up

  • What would you have done if the teammate had still disagreed after you showed the data?
  • Did you involve your manager, and when did you decide to?
  • How do you work with that person today?

Can you discuss a project you are particularly proud of and your specific contributions?

Medium · Behavioral & Leadership

Approach

  1. Choose a project where you made decisions, not one you only attended. Name the problem and the constraints (data volume, deadline, team size, existing system) in two or three sentences.
  2. Separate your contribution from the team's. Use 'I' for the pieces you designed, built, reviewed or unblocked, and say plainly which parts others owned. A story told entirely in 'we' leaves your specific contribution unclear.
  3. Show one real technical decision with the alternative you rejected and the reason, for example a queue versus synchronous calls, a schema change, or a caching layer, so the story has depth to follow up on.
  4. Give the result with evidence you actually have, such as latency, error rate, deployment frequency, tickets closed or adoption. Do not invent numbers; a qualitative result stated honestly is better than a precise one you cannot defend.
  5. End with what you would change now. It shows judgment and prepares you for the follow-up.

Follow-up

  • What was the hardest technical decision on that project, and what did you rule out?
  • What would you do differently if you rebuilt it today?
  • How did you know the project worked after release?

How do you handle situations where you are given ambiguous requirements?

Medium · Behavioral & Leadership

Approach

  1. Start from one real situation where the requirement was vague, such as a ticket with no acceptance criteria or a feature request described in business terms. Say what exactly was unknown.
  2. Describe how you reduced the ambiguity in order: list the open questions, ask the product manager or stakeholder specific questions (who uses it, what happens on failure, what is out of scope), and write down the assumptions you could not confirm.
  3. Show that you kept moving: build the smallest slice or a prototype that exposes the gaps, choose reversible decisions first, and put assumptions in the ticket or design note so reviewers can challenge them.
  4. Confirm before you invest heavily, for example by demoing the slice or sharing the assumptions in sprint planning or refinement, and say what changed as a result.
  5. The answer sinks in two ways: waiting passively for a perfect spec, or silently guessing and building the wrong thing.

Follow-up

  • What if the stakeholder was not available to answer your questions?
  • How do you decide when you have clarified enough to start building?
  • Tell me about a time an assumption turned out wrong and what it cost.

What is your approach to mentoring junior engineers or collaborating across functions?

Medium · Behavioral & Leadership

Approach

  1. The prompt has two halves, mentoring junior engineers and collaborating across functions. Pick one story as your main answer and add a brief example of the other, instead of giving two thin ones.
  2. For mentoring, show a structure: agree on a goal with the junior engineer, pair on a first task, give code review comments that explain the reason behind each change, hand over progressively harder work, and describe how they performed afterwards. Mentoring that consists of giving answers or rewriting their code shows little.
  3. For cross-functional work, give a case with a product manager, architect or operations team where you turned a technical constraint into a business consequence (a date, a risk, a cost) and reached a decision together. Mention how you kept them informed, for example through demos, short written updates or review of the design note.
  4. Include how you adapt: you explain a database trade-off to a non-technical partner differently from how you explain it to an engineer, and you ask what they need to decide rather than listing every detail.
  5. State your result: what the junior engineer can now do, or what the cross-functional decision allowed the team to ship.

Follow-up

  • What did you do when the person you were mentoring was not improving as expected?
  • How do you give code review feedback that is direct without discouraging a newer engineer?
  • How would you handle a product manager pushing for a deadline you thought was unrealistic?