J.P. Morgan Software Engineer Interview Guide

J.P. Morgan Software Engineer interview prep: online assessment, technical and behavioral rounds, reported questions, and a 7-day plan.

Topics: J.P. Morgan, Software Engineer, Interview Guide

Author: PracHub

Published: 9/22/2026

J.P. Morgan logo
J.P. Morgan · Software EngineerUpdated Sep 24, 2026 · Reviewed by PracHub

J.P. Morgan Software Engineer Interview Guide

J.P. Morgan Software Engineer interview prep: online assessment, technical and behavioral rounds, reported questions, and a 7-day plan.

3 rounds · typical prep 2–4 weeks

  1. 1Online Assessment5 questions
  2. 2Technical Screen19 questions
  3. 3Onsite17 questions

On this page0% read
01 · Overview

Interviewing at J.P. Morgan

This page's sources say nothing about J.P. Morgan's teams, systems or products, so this brief covers only what candidates report about the Software Engineer interview. They describe three stages: an online assessment, technical interviews and behavioral interviews. The reported questions and PracHub's question bank for this role fall into four groups. The first is standard data-structure problems: longest palindromic substring, maximum subarray, linked-list cycle detection, anagram grouping, interval merging, Two Sum and an O(1) LRU cache. The second is software engineering fundamentals: processes versus threads, Java concurrent collections and Java 8 features, REST exception handling with @ControllerAdvice, and reviewing concurrent code for thread safety. The third is system design: a notification system for millions of users, high availability in microservices, a URL shortener and a reliable bank payment system. The fourth is behavioral prompts about disagreement, missed deadlines and a project you are proud of. J.P. Morgan candidates report 3 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.

Practice bank
41+ questions
Rounds
3
Typical prep
2–4 weeks
Interview reports
10
02 · Difficulty

How hard is the J.P. Morgan Software Engineer interview?

From 41 labelled questions
  • Easy24%10 questions
  • Medium76%31 questions
  • Hard0%0 questions

Most questions land in the middle: hard enough to prepare for, rarely brutal.

Read 10 J.P. Morgan interview reports from candidates who went through this loop.

03 · Topic breakdown

What J.P. Morgan actually tests for

Share of 41 Software Engineer questions
  1. Coding & Algorithms46% · 19
  2. System Design24% · 10
  3. Software Engineering Fundamentals17% · 7
  4. Behavioral & Leadership12% · 5
04 · Question bank

The questions most likely to come up

41+ in the J.P. Morgan bank · sorted by popularity
  1. Design a URL shortenerDesign a URL shortening service similar to Bitly. The service should accept a long URL and return a short alias, and visiting the short URL should…System DesignOnsiteEasy
  2. Compute max-ons and deletion indicesGiven a circular array of n computers represented by 0 (off) and 1 (on), determine the maximum possible number of on computers within any contiguous…Coding & AlgorithmsTechnical ScreenCodingMedium
  3. Review a PR for thread-safe request handlingSoftware Engineering FundamentalsTechnical ScreenPremiumMedium
  4. Answer common behavioral promptsContext: You are interviewing for a Software Engineer role. Prepare concise, impact-oriented responses using the STAR method (Situation, Task,…Behavioral & LeadershipOnsiteMedium
  5. Improve shopping cart latency and stock notificationsYou are given an existing e-commerce shopping cart system (web/mobile) that already supports adding/removing items and viewing the cart. The…System DesignOnline AssessmentEasy
  6. Unlock every J.P. Morgan questionModel solutions on all of them, plus the coding and SQL consoles.See Premium
  7. Implement an O(1) LRU CacheCoding & AlgorithmsTechnical ScreenCodingPremiumEasy
  8. Reason About Top K Frequent Values at ScaleSoftware Engineering FundamentalsTechnical ScreenPremiumMedium
  9. Describe a proud project and conflictIn a behavioral interview, be prepared to discuss your previous work experience, one project you are especially proud of, and a time you had a…Behavioral & LeadershipOnsiteEasy
  10. Design a Scalable Notification SystemSystem DesignOnsitePremiumMedium
  11. Minimize Operations to Balance ShipmentsA shop has n item types, where quantity[i] is the positive integer quantity of the i-th item type. The item types must be shipped in two non-empty…Coding & AlgorithmsOnline AssessmentCodingMedium
  12. Review concurrent code qualitySoftware Engineering FundamentalsTechnical ScreenPremiumMedium
  13. Behavioral: Making Progress When Requirements Are VagueIn the behavioral round of a software engineering loop, the interviewer asks scenario questions drawn from real work. One of them is about ambiguity:Behavioral & LeadershipOnsiteMedium
Practice 41+ J.P. Morgan questions

The 60-second brief

This page's sources say nothing about J.P. Morgan's teams, systems or products, so this brief covers only what candidates report about the Software Engineer interview. They describe three stages: an online assessment, technical interviews and behavioral interviews. The reported questions and PracHub's question bank for this role fall into four groups. The first is standard data-structure problems: longest palindromic substring, maximum subarray, linked-list cycle detection, anagram grouping, interval merging, Two Sum and an O(1) LRU cache. The second is software engineering fundamentals: processes versus threads, Java concurrent collections and Java 8 features, REST exception handling with @ControllerAdvice, and reviewing concurrent code for thread safety. The third is system design: a notification system for millions of users, high availability in microservices, a URL shortener and a reliable bank payment system. The fourth is behavioral prompts about disagreement, missed deadlines and a project you are proud of.

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

What a Software Engineer does at J.P. Morgan

Most of the reported coding questions are standard data-structure problems: the longest palindromic substring, the contiguous subarray with the largest sum, and cycle detection in a linked list. PracHub's question bank for this role adds anagram grouping, merging overlapping intervals, Two Sum, course-schedule cycle detection, moving non-zero elements left in place, the first non-repeating character, and an O(1) LRU cache. Aim to write each of these from an empty file with the edge cases handled, rather than recognising them on sight.

Several questions test software engineering fundamentals rather than algorithms. These include the difference between a process and a thread, Java concurrent collections and Java 8 features, exception handling in a RESTful service with @ControllerAdvice, and reviewing a pull request for thread-safe request handling. On the design side, candidates report a notification system that supports millions of users and a question on high availability and fault tolerance in microservices. PracHub's question bank for this role adds a URL shortener, a reliable bank payment system, an e-commerce site and an auditable internal AI chatbot. None of these questions is tied to a specific round in the sources, so prepare every category before your technical interviews.

The J.P. Morgan interview process

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

Online Assessment

Candidates report an online assessment as the first stage. The sources give no platform, problem count or time limit, so confirm those with your recruiter. The reported coding questions and those in PracHub's question bank for this role make a reasonable practice set: Two Sum, grouping anagrams, merging overlapping intervals, the first non-repeating character, shifting non-zero elements left in place, and maximum subarray sum. Most lost time goes to library calls you only half-remember, off-by-one loop bounds, and random edits made when the output is wrong. When a case fails, find the smallest input that reproduces it and trace the state by hand before you change anything.

What it evaluates

  • Whether you pick the structure the access pattern calls for without a detour, such as a hash map from value to index for Two Sum or a canonical key per word for anagram grouping
  • Whether edge cases are handled before submission: an empty input, a single element, an all-negative array for maximum subarray, and intervals that only touch, such as [1,2] and [2,3]
  • Whether the solution is written from memory rather than half-recalled, including the standard-library calls it depends on
  • Whether a failing case is isolated and explained before any edit is made

How to prepare

  • Starting from an empty file with no references open, write Two Sum, grouping anagrams, merging intervals and Kadane's maximum subarray. Check each one on an empty input and a one-element input
  • Drill the library calls these problems need in your chosen language, such as sorting with a custom comparator, building a map of counts and splitting strings, until you no longer need to look them up
  • Before touching a broken solution, write one sentence naming the input, the expected output and the actual output

Technical Interviews

Candidates report technical interviews after the assessment. The sources do not say how many there are, and they do not tie any question to these interviews, so prepare every category that appears in the reported questions and PracHub's question bank. To prepare for coding, practise problems such as the longest palindromic substring, linked-list cycle detection and an O(1) LRU cache. For fundamentals, practise processes versus threads, Java concurrent collections, REST exception handling, and reviewing concurrent code. For design, practise reported design questions such as a notification system for millions of users or high availability in microservices. In each one, let the input size justify your approach, name the bottleneck in your own answer, and say what you would change if the constraints changed.

What it evaluates

  • Whether the approach is justified by stated bounds rather than by the first pattern recognised, and whether you ask about sortedness, duplicates and memory limits when they are not given
  • Whether you can explain a fundamentals answer through its consequences. For example: threads share an address space, so shared state needs synchronisation, while processes are isolated and cost more to create and switch between
  • Whether a design answer names what fails and what happens next, including retries, duplicate delivery and a dependency that is down, instead of listing components
  • Whether a claimed complexity holds up. An LRU cache is O(1) only if eviction and recency updates avoid a linear scan

How to prepare

  • Implement an LRU cache twice: once with a hash map plus a doubly linked list, and once in Java with LinkedHashMap using access order and an overridden removeEldestEntry. Be ready to explain why a plain list removal breaks the O(1) claim
  • Write one-paragraph answers on processes versus threads, ConcurrentHashMap versus Collections.synchronizedMap, and what @ControllerAdvice with @ExceptionHandler centralises in a Spring REST service
  • Talk through the notification-system and microservices-availability prompts aloud. For each, state the delivery guarantee, the retry policy and the behaviour when a downstream provider fails

Behavioral Interviews

Candidates report behavioral interviews as a stage. The reported prompts ask about a technical disagreement with a teammate and a deadline you failed to meet, and PracHub's question bank for this role adds describing a project you are proud of along with a conflict. Build each story around a decision you made yourself. State the options you had and what you knew at the time. Then give what you did, the result, and what you would change. A story that opens with the outcome and works backwards sounds like hindsight. A story that walks through the choice as it happened is easier to defend under follow-up questions.

What it evaluates

  • Whether a disagreement story shows the evidence that settled it, such as a test, a measurement or a reproduction, rather than ending with 'we aligned'
  • Whether a missed-deadline story names the specific cause, when you first saw the risk, who you told, and what you changed afterwards
  • Whether the reasoning you give was available when you decided, rather than learned from the result

How to prepare

  • Write out the disagreement, missed-deadline and proud-project stories, each with one concrete number: the size of the slip, the users affected, or the latency or error rate that changed
  • For each story, write the decision point and the options you rejected. Rehearse from the decision forward and stop before the outcome
  • Use the quota-check review scenario in this guide as a model for a disagreement settled by demonstration rather than by rank

Questions J.P. Morgan candidates report

These are the prompts PracHub has collected for this role and company. They are a record of what candidates describe being asked, so treat them as the shape of the conversation rather than a script J.P. Morgan works from.

Data structures and algorithms

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

  • Given a string, find the longest palindromic substring.
  • Explain the difference between process and thread in the context of operating systems.
  • Given an array of integers, find the contiguous subarray that has the largest sum.
  • How would you detect a cycle in a linked list?

System design and architecture

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

  • How would you design a notification system that supports millions of users?
  • How do you ensure high availability and fault tolerance in a microservices architecture?

Behavioural and engineering judgement

Every question in this category has a number in it somewhere, and engineers routinely tell the story without one. What the outage cost, what the delay cost, what the rewrite consumed in engineer-weeks against what it saved, and who absorbed it when it was not users: the on-call rotation, another team's quarter, the support queue. You will rarely have an exact figure, and an estimate with its method stated beats both a precise-sounding invention and silence. Say how you got to it and what it leaves out. That is the sentence that makes the follow-up about whether it was worth doing answerable at all.

  • Describe a time you had a technical disagreement with a teammate. How did you resolve it?
  • Describe a situation where you failed to meet a deadline. What did you learn?

Practice exercises with worked solutions

The prompts above tell you what gets asked. These are PracHub exercises written to drill the same skills, with the reasoning spelled out so you can check your own answer instead of guessing whether it was good enough.

Find the join that inflates every invoice total

invoice_line_item holds line_id, invoice_id, tenant_id, sku, rate_tier, quantity, unit_price_micros, amount_minor (bigint), currency, kind, voided_at. invoice_payment_attempt holds attempt_id, invoice_id, tenant_id, amount_minor, status (succeeded, failed, pending), created_at, and an invoice has many attempts. A finance report runs select i.invoice_id, sum(l.amount_minor), count(p.attempt_id) from invoice i join invoice_line_item l using (invoice_id) join invoice_payment_attempt p using (invoice_id) group by 1 and the totals are wrong. Say precisely what the sum now equals, and write a version that is also correct for invoices with zero attempts.

How a strong candidate works it

  • Compute what the query actually returns before fixing it. The two joins form a Cartesian product per invoice, so each line row repeats once per attempt row: sum(l.amount_minor) is the true total multiplied by the attempt count, and count(p.attempt_id) is attempts times lines. Three lines and two attempts report double the money and six attempts.
  • Reject the reflex repair. count(distinct p.attempt_id) does fix the count, because attempt_id is unique. sum(distinct l.amount_minor) does not fix the sum, because two legitimate lines with equal amounts collapse into one. DISTINCT inside an aggregate deduplicates values, not rows, and the difference stays invisible until two lines happen to match.
  • Aggregate each branch to invoice grain before joining: one CTE summing lines by invoice_id, one counting attempts by invoice_id, then join the two results. A LATERAL subquery per invoice is equivalent and sometimes plans better when the outer set is small. Either way every aggregate stays at the grain it was defined at.
  • Keep invoices with no attempts by making the attempt branch a LEFT JOIN with coalesce(attempt_count, 0). An inner join here silently drops every unpaid invoice, which is usually the exact population finance is asking about.
  • Push each filter to its own grain: where l.voided_at is null belongs inside the line CTE, not the outer query, or it would also filter the attempt branch through the join. Put the tenant predicate on both branches, since the denormalised tenant_id is what stops a wrong join crossing tenants.
  • Leave yourself a standing check: an invoice total is a function of its non-voided lines and of nothing about payments, so if changing the payment filter moves the money figure, the fan-out is back.

Expected result (25 min): The original sum equals the true line total multiplied by the number of payment attempts and the count equals attempts times lines; the corrected query pre-aggregates each branch to invoice grain in separate CTEs and LEFT JOINs them with coalesce, with voided lines excluded inside the line CTE and the tenant predicate applied to both branches.

Check yourself

  • The money total is unchanged by adding or removing payment attempts.
  • An invoice with zero attempts appears with its total and a zero count.
  • Two lines with identical amounts both contribute to the total.
  • Voiding a line changes the money figure and leaves the attempt count alone.

Follow-ups

  • Add a third branch for credit notes applied to the invoice. Does the CTE shape still hold, and when would a single pass with filter (where ...) be better?
  • Over 500k invoices this report takes minutes. Which grain would you materialise, and how do you keep it correct when a line is voided?
  • The same report is needed per tenant per month. What index makes the line CTE cheap?

Explain why the metering dashboard scans every daily partition

usage_event is range-partitioned daily on ingested_at and holds tenant_id, workspace_id, environment, sku, quantity numeric(20,6), occurred_at and ingested_at. The only relevant index is on (occurred_at). A dashboard runs select sku, sum(quantity) from usage_event where tenant_id = $1 and date_trunc('hour', occurred_at) >= $2 and environment = 'production' group by sku, and EXPLAIN shows a sequential scan of every partition. Give each distinct reason, rewrite the predicate so an index can serve it, propose the index, and state the write cost its column order adds.

How a strong candidate works it

  • Separate the three causes rather than blaming one. First, date_trunc('hour', occurred_at) wraps the column, so the predicate is not sargable against a btree on the bare column. Second, pruning keys off ingested_at while the query constrains occurred_at, so no partition can be excluded. Third, even made sargable, (occurred_at) is not tenant-leading, so for one tenant among thousands the scan reads the whole time range and discards nearly all of it.
  • Rewrite the bound carefully, because the obvious rewrite is only conditionally equivalent. date_trunc('hour', x) >= $2 equals x >= $2 only when $2 is already hour-aligned; for an arbitrary $2 it means x >= date_trunc('hour', $2) + interval '1 hour'. Normalise the parameter in the caller and leave the column bare.
  • Restore pruning with a second, redundant predicate on the partition key: ingested_at >= $2 - interval '<late-data horizon>'. State both sides of it. It prunes to a handful of partitions, and it silently omits any event whose ingest lagged past that horizon, which is precisely what a producer replay produces. Either document the horizon as a stated bound, or partition on occurred_at and move the problem into the dedup window instead.
  • Propose (tenant_id, occurred_at) include (sku, quantity) per partition. A partial index where environment = 'production' mostly saves size rather than selectivity, since production dominates the three environments; take it if non-production is a meaningful share and skip it otherwise.
  • Price the write path honestly. At roughly 250M rows/day each extra index is another insert plus WAL per row, and a tenant-leading key scatters inserts across one hot leaf per active tenant instead of appending to a single rightmost leaf, so page dirtying and random I/O both rise. An INCLUDE payload widens every leaf entry and enlarges the index accordingly.
  • Add the index-only-scan caveat before someone reports it as a regression: on a freshly appended table the visibility map is not yet set for recent pages, so the INCLUDE columns still cost heap fetches until autovacuum has been through, and the newest hour is exactly the data the dashboard reads.

Follow-ups

  • CREATE INDEX CONCURRENTLY is not supported on a partitioned parent. Give the sequence that gets this index onto 400 existing partitions without blocking ingest.
  • One tenant holds 200 times the median row count and the dashboard still times out for them with the index in place. What changes?
  • Should this read hit usage_rollup_hourly instead? State what that costs in freshness and what the watermark lets you promise.

Parse and verify a timestamped multi-signature webhook header

An inbound webhook carries a signature header of at most 1 KiB shaped t=<unix seconds>,v1=<64 hex chars>, with up to five v1 values during secret rotation and possibly unknown scheme keys. You hold the raw request body bytes and the currently active signing secrets. Write the parser and the verifier: accept when any active secret reproduces a signature and the timestamp is within a five-minute tolerance in either direction, reject otherwise. Single left-to-right pass over the header, no regular expression. State what is inside the MAC and why.

How a strong candidate works it

  • Parse in one scan: split on ,, then on the first = only, since a value may itself contain = under a future scheme. Accept t exactly once and treat a second t as a reject rather than last-wins. Push every v1 onto a short list and ignore any other key, so a v2 can be introduced later without breaking this verifier.
  • Say what is signed: HMAC-SHA256 over the exact byte string <t>.<raw body bytes>, yielding 32 bytes or 64 hex characters. The timestamp sits inside the MAC because otherwise an attacker replays yesterday's body with its still-valid signature and only has to edit the header timestamp.
  • Hash the bytes as received. Verifying against a re-serialised JSON body is the usual defect: key order, whitespace and number formatting all change the bytes while the parsed objects compare equal, so signatures fail for honest senders and the popular 'fix' is to stop checking.
  • Compare in constant time over fixed-length digests. Decode the hex to 32 bytes, accumulate acc |= a[i] ^ b[i] across the whole length, and test acc == 0 at the end. Evaluate every candidate without an early exit; at five candidates that is five HMACs over the body, linear in body size and negligible beside the network.
  • Apply the tolerance as a two-sided bound, rejecting when |now - t| > 300 seconds. A sender whose clock runs ahead of yours is an ordinary case, and an unbounded future timestamp is a free replay window.
  • Complexity: O(L) over the header producing k candidates, plus k HMACs at O(|body|) each. Space is O(k) beyond the body itself. Do the cheap rejections, including the tolerance check, before any cryptography runs.

Expected result (15 min): The valid case accepts. Both out-of-tolerance cases reject, including the future one. The unknown v2 field is ignored and the v1 still verifies. The re-serialised body fails, which is correct and is exactly why the raw bytes must be retained.

Check yourself

  • Timing the comparison over 10,000 mismatching signatures that differ at the first character versus the last shows no separation you can measure.
  • Removing t from the MAC input makes an old captured body with its original signature pass. Write that test, watch it pass, then put t back.
  • Every rejection except the signature mismatch itself returns before an HMAC is computed.

Follow-ups

  • The body is 40 MB. What changes about where you verify, and what can you do before the whole body has arrived?
  • A customer reports that signatures fail for exactly the requests whose body contains a non-ASCII character. What is your first hypothesis?
  • How do you rotate the signing secret with no failed deliveries, and how long do both secrets stay live?

Leasing sandboxed job runs without double execution

job-runner leases work from a queue and executes customer workloads in sandboxes with CPU, memory, wall-clock and egress limits. Run durations span 200 ms to 30 minutes, thousands are concurrent, and job_run.status must reach exactly one terminal state among succeeded, failed, timed_out, cancelled and lost. A worker can pause 45 seconds for garbage collection, or be briefly partitioned, after its sandbox has exited but before it writes the outcome. Design the lease duration, the fencing, and timeout ownership, and state how billable_seconds is computed for a run whose worker never returns.

How a strong candidate works it

  • Resolve the lease-duration conflict rather than picking a compromise. A lease shorter than the longest legitimate run re-dispatches work that is still executing; a lease long enough for a 30-minute run leaves a crashed 200 ms run undetected for half an hour. Use a short lease - about 30 seconds - renewed by a heartbeat every 10 seconds while the supervisor is alive, each renewal pushing leased_until to now() + 30 s. Detection latency is then one lease, not one heartbeat: a dead worker's last successful heartbeat landed at most 10 s before it died, so leased_until expires 20 to 30 s after the death, plus whatever interval the reaper scans on. What the 3:1 ratio of lease to heartbeat buys is headroom - one or two missed heartbeats change nothing, and a stall only becomes a re-dispatch once it outlives the lease remaining when it began, between 20 and 30 s here. Both costs follow from those two numbers: heartbeat write load proportional to concurrent runs (3,000 runs at a 10-second interval is about 300 updates/second), and any pause longer than the lease - the 45-second GC - re-dispatching a run that is perfectly healthy.
  • Fence the outcome write so the store, not the worker's memory, arbitrates: UPDATE job_run SET status = $1, finished_at = $2, exit_code = $3 WHERE run_id = $4 AND status = 'running' AND lease_token = $5, and require exactly one row affected. Zero rows means this worker was fenced while it was paused, and its correct behaviour is to discard the result, not to retry - the retry is how a resumed worker overwrites a newer attempt.
  • Make the status machine monotone with a check constraint or a trigger so no terminal row can move, and model a retry as a new row with parent_run_id set and attempt incremented rather than a reset of the old one. That is what keeps how many times did this actually execute answerable afterwards and keeps each attempt's resource usage attributable to itself.
  • Put the wall-clock timeout in the supervisor, never in the workload: a customer-supplied program asked to time itself out will not. Distinguish timed_out, which the supervisor observed and caused, from lost, which nobody observed at all, and leave exit_code null unless the supervisor actually saw the process exit. Recording lost as failed asserts an outcome no one witnessed and then bills and retries on that assertion.
  • Decide billable_seconds for the unobserved case explicitly, because it cannot be derived. It is not now() - started_at, which bills queue and pause time the customer never consumed. The two defensible policies are leaving it null while status = 'lost' and billing nothing, or flooring it at the last heartbeat's observed running time as a stated lower bound; pick one and write it down. Add a per-tenant concurrency cap on sandbox slots so one tenant cannot occupy the whole fleet while these questions are being answered.

Expected result (30 min): A short lease of roughly 30 seconds renewed by a 10-second heartbeat, so detection latency is one lease - 20 to 30 seconds after a worker dies, plus the reaper's scan interval - independent of the 200 ms to 30 minute run spread, with one or two missed heartbeats tolerated rather than fatal; a terminal write fenced by AND status = 'running' AND lease_token = $x where zero rows affected means discard; retries as new rows with parent_run_id; a supervisor-owned wall-clock kill that separates timed_out from lost; billable_seconds left null while status = 'lost', or floored at the last heartbeat as a stated policy, never inferred from wall-clock.

Check yourself

  • In your timeline the fenced write must affect zero rows. If it affects one, the lease alone was doing the work and the token adds nothing.
  • At 3,000 concurrent runs and a 10-second heartbeat, check 300 updates/second against the other write load on that table before accepting the interval; if it is the dominant writer, the heartbeat belongs somewhere else. Check the ratio too: at lease = 3 x interval a lost heartbeat is survivable, whereas at lease = interval every scheduling hiccup is a double execution.
  • State whether a duplicate execution is billed, then confirm that summing billable_seconds across a parent and its retry matches that stated policy rather than matching it by accident.

Follow-ups

  • A worker returns from a 45-second GC pause and writes succeeded. Walk through what the database does statement by statement.
  • Heartbeats are now a few hundred writes a second against job_run. How do you keep that off the path that matters, and what do you lose by moving it?
  • The re-dispatched workload has an external side effect the first attempt already performed. What does your design owe the customer, and what can it not fix?

A rare job-run overwrite that logging makes disappear

About one job run in fifty thousand records billable_seconds matching no observed sandbox lifetime, and a few rows show worker_id changing after finished_at was already set. It does not reproduce: debug logging around the terminal write made it vanish for two weeks before it returned. Runs last from 200 ms to 30 minutes, the lease is 60 seconds and is renewed while a run executes. Give an ordered checklist, the mechanism, and a fix that makes the illegal write impossible rather than merely rarer.

How a strong candidate works it

  • Mine the evidence instead of chasing a repro: select rows where updated_at is later than finished_at, or where a terminal status was written twice, and join them to attempt history to recover both writer identities. The defect has already happened tens of times and the rows are the recording.
  • State the signature before measuring it. If the cause is a lease that expired while the original worker was stalled, affected runs should cluster where the gap between the last renewal and the terminal write exceeds the lease, and should correlate with worker pause metrics rather than with workload shape.
  • Read the disappearance honestly. Logging inside the window changed the timing and lowered the probability; it is evidence about how narrow the window is, not a fix. Reproduce by widening the window on purpose, shortening the lease and injecting a pause between sandbox exit and the terminal write, rather than by adding more instrumentation.
  • Name the mechanism precisely: the lease expires during a stall such as a long garbage-collection pause or a brief partition, the run is re-dispatched, and the original worker then wakes and writes its terminal state over the new attempt's row. A lease alone cannot stop this, because the check and the write are separated by the stall.
  • Fix by fencing the write itself: UPDATE job_run SET status = $2, finished_at = $3, billable_seconds = $4 WHERE run_id = $1 AND status = 'running' AND lease_token = $5, with zero rows affected interpreted as having been fenced rather than as success. The token lives on the row so the store arbitrates, not the worker's memory.
  • Keep the state machine honest: a retry inserts a new row pointing at parent_run_id rather than resetting the old one, and a run whose worker vanished terminates as lost with billable_seconds null, because recording failure asserts an outcome nobody observed and then bills and retries on that assertion.

Follow-ups

  • The supervisor also emits a usage event on completion. What does the fenced worker do about the event it already emitted, and how does metering absorb it?
  • Lease renewal is itself a network call. What happens when a renewal times out, and how does the worker decide whether it still holds the lease?
  • Why is lengthening the lease past the longest legitimate run the wrong lever, and what breaks if you do it anyway?

Resolve a review disagreement over a quota check

A colleague's pull request enforces a per-tenant quota by selecting the current count and then inserting when it is under the limit. You flag it as a race. They reply that the transaction already runs at repeatable read, so the snapshot makes it safe, and the tests pass. Walk through taking that disagreement to a resolution: what you write in the review, what you demonstrate rather than assert, which fix you propose and why, and what you do if they still disagree after all of it.

How a strong candidate works it

  • Answer the claim precisely instead of restating your objection, because they have made a specific technical argument. In PostgreSQL, repeatable read is snapshot isolation; this is write skew, which snapshot isolation permits by design. Both transactions read a count that is stable within their own snapshot, insert disjoint rows that the other cannot see, and both commit, so the limit is exceeded by exactly the concurrency.
  • Demonstrate rather than cite. Two psql sessions, both BEGIN ISOLATION LEVEL REPEATABLE READ, both select the count, both insert, both commit: it succeeds. Repeat at SERIALIZABLE and the second commit fails with serialization_failure, SQLSTATE 40001. That takes two minutes, ends the argument without anyone conceding a position, and leaves an artefact for the next reviewer.
  • Offer the options with their costs rather than a verdict. Serialisable plus a retry loop on 40001 is correct but obliges every caller to retry and degrades under contention. An increment-and-compare on a counter row — update tenant_quota set used = used + 1 where tenant_id = $1 and used < limit returning used — is safe even at read committed, because a blocked updater re-evaluates the WHERE clause against the row version it finally locks, and zero rows returned means full. A unique or exclusion constraint that makes the surplus write fail is the third.
  • Name the plausible non-fix explicitly, since it is what usually gets merged instead: folding the count into the insert as insert ... select ... where (select count(*) ...) < limit is still racy under read committed, because the subquery cannot see the other transaction's uncommitted rows. It looks atomic and is not.
  • Say what you do if they still disagree: escalate the decision rather than the disagreement. Attach the reproduction, hand it to the service owner or a third reviewer, and state that you will not block the merge if the owner accepts the risk knowingly — and that you want that acceptance written down.
  • Close with the general lesson worth leaving in the review thread: a passing suite is weak evidence for a concurrency claim because it runs one request at a time. Ask for a test that runs two.

Follow-ups

  • Write the counter-row version. Does your answer change if the quota counts child rows rather than a column?
  • Under serialisable, who performs the retry, and what does the API client see if the retry also fails?
  • This is the third disagreement with the same reviewer this month. What changes in how you review?

Where candidates lose points

Submitting a standard coding problem that fails on edge cases

Reported coding questions and those in PracHub's question bank, such as maximum subarray, Two Sum and merging intervals, are easy to get almost right. Start Kadane's running sum from the first element, not zero, or an all-negative array returns 0 instead of its largest element. In Two Sum, check for the complement before inserting the current value so one element is never paired with itself. In interval merging, sort by start and decide whether touching intervals merge. Before you submit, run an empty input, a single element and one adversarial case.

Claiming O(1) for an LRU cache that hides a linear scan

If recency is tracked in a list or array and a hit removes the key from the middle, get and put are O(n). Pair a hash map from key to node with a doubly linked list so a hit unlinks and relinks in constant time and eviction removes the tail. In Java, LinkedHashMap with accessOrder set to true and removeEldestEntry overridden does the same thing. Be ready to say which of the two you used and why.

Answering fundamentals questions with definitions and no consequences

For processes versus threads, go past 'a thread is a lightweight process'. Threads in a process share heap and address space but each has its own stack. That makes communication cheap and shared state dangerous, and one thread's crash can take down the whole process. Processes are isolated and cost more to create and switch between. Answer the Java concurrency questions the same way. ConcurrentHashMap rejects null keys and values, and its iterators are weakly consistent. A check-then-put across two calls is still a race, so use computeIfAbsent or merge.

Designing the notification system or microservice availability as a box diagram with no failure behaviour

For a reported design question such as a notification system for millions of users, state the delivery guarantee first. With at-least-once delivery, give each notification an id and deduplicate on it, and say what a user sees if a provider is down. For availability in microservices, attach each mechanism to the failure it contains. Timeouts bound waiting. Retries with backoff and jitter apply only to idempotent calls. Circuit breakers stop callers piling onto a dead dependency. Redundancy removes single instances.

Telling a disagreement or missed-deadline story with no evidence or cause

A disagreement story that ends 'we talked it through and agreed' shows nothing about your judgement. Name the specific technical claim, the evidence that settled it, and what you would have done had it gone the other way. For the missed deadline, name the actual cause, when you first saw the risk, who you told, and the process change that followed.

Breadth then depth, for a generalist facing a mixed loop

Four days sample coding, design, fundamentals and the practical rounds at deliberately shallow depth, which is enough to surface the topics you did not know were in scope. That map, rather than a guess made on day one, decides where the last three days go.

Day 1 — Online assessment patterns from an empty file

  • Solve Two Sum, grouping anagrams, merging overlapping intervals, the first non-repeating character and shifting non-zero elements left in place, each from an empty file with no references
  • For each one, write the complexity and the edge case you tested: empty input, a single element, duplicates, touching intervals
  • Mark each problem as clean, slow, or needed a hint. Redo any you did not mark clean at the end of the day

Deliverable: Five working solutions with complexity and a tested edge case noted for each, plus a list of any you needed help with.

Day 2 — Reported coding questions at interview depth

  • Solve longest palindromic substring by expanding around each of the 2n - 1 centres (O(n^2) time, O(1) space). Explain how it differs from the longest palindromic subsequence in PracHub's question bank, which needs an O(n^2) dynamic program
  • Solve maximum subarray with Kadane's algorithm and test an all-negative array. Detect a linked-list cycle with Floyd's slow and fast pointers, then find the cycle's start
  • Solve 'Can All Courses Be Completed?' with Kahn's topological sort: the courses can all be completed only if every node is processed
  • Explain each solution aloud as you would in a technical interview: bounds, approach, complexity, then the test cases

Deliverable: Four solutions with spoken explanations, and a note on the palindromic substring versus subsequence distinction.

Day 3 — Fundamentals: concurrency, Java and REST error handling

  • Write short answers on processes versus threads, ConcurrentHashMap versus Collections.synchronizedMap, and Java 8 features such as lambdas, streams and Optional
  • Explain how @ControllerAdvice with @ExceptionHandler maps exceptions to consistent HTTP responses across controllers, and which status codes you would return for validation errors versus unexpected failures
  • Implement an O(1) LRU cache with a hash map and a doubly linked list, then again with LinkedHashMap
  • Work through the webhook signature parser exercise in this guide as practice in careful parsing, constant-time comparison and rejecting bad input early

Deliverable: Written fundamentals answers, two LRU implementations, and the completed webhook parser exercise with its checks passing.

Day 4 — Code review and data correctness

  • Practise the concurrent code review questions from PracHub's question bank. Take a short request handler that updates shared state and list every race, then fix each with the narrowest change
  • Work through the SQL join fan-out exercise in this guide and confirm with its checks that the invoice total no longer changes when payment attempts are added
  • Read the partition-scanning SQL drill and write, in your own words, the three reasons the query scans every partition

Deliverable: A marked-up review of a concurrent handler, the SQL exercise passing all four checks, and a written explanation of the three scan causes.

Day 5 — System design on reported and question-bank prompts

  • Design a notification system for millions of users. Cover the API, per-channel queues, at-least-once delivery with deduplication by notification id, user preferences, and what happens when a delivery provider is down
  • Answer the high availability and fault tolerance question for microservices by attaching each mechanism (timeouts, retries with backoff, circuit breakers, redundancy) to the failure it contains
  • Sketch the reliable bank payment system from PracHub's question bank, focusing on idempotency keys for retried requests and how you would reconcile records
  • Work through the leased job-runner design exercise in this guide and check that its fenced write affects zero rows in the stalled-worker timeline

Deliverable: Three design outlines, each naming its delivery or consistency guarantee and its failure behaviour, plus the completed job-runner exercise.

Day 6 — Behavioral stories with a decision at the centre

  • Write the technical-disagreement, missed-deadline and proud-project-with-conflict stories, each opening at the decision point and carrying one concrete number
  • Use the quota-check review scenario in this guide to structure a disagreement settled by demonstration. Adapt the structure to a disagreement of your own
  • Deliver each story aloud, then answer 'what would you do differently' and 'what did you decide not to do' without notes

Deliverable: Three rehearsed stories, each with a decision point, a number and a concrete change you made afterwards.

Day 7 — Rehearse the reported sequence

  • Run the three stages in the order candidates report them: a timed set of two day-1 problems, then one coding problem from day 2 plus one design prompt from day 5, then two behavioral stories
  • Note where your answers got weaker in the later stages, such as skipped edge cases or design answers that turned into lists
  • Condense the week onto one page: the edge cases per coding pattern, the fundamentals answers, and the failure behaviour for each design

Deliverable: Notes from the full run-through and a one-page review sheet you can go over before the interview.

Frequently asked questions

Is this an official J.P. Morgan interview guide?

No. It is PracHub's own research and practice material for the Software Engineer role at J.P. Morgan. The rounds and questions reflect what candidates have reported, not a process J.P. Morgan has published, and they change over time. Confirm the current format and scope with your recruiter.

What stages do candidates report for the Software Engineer interview?

Candidates describe an online assessment, then technical interviews, then behavioral interviews. The sources for this guide do not give the number of technical interviews, the assessment's format, or the length of any stage. Ask your recruiter for those details before you plan your preparation.

What coding topics should I prepare?

The reported coding questions are the longest palindromic substring, the contiguous subarray with the largest sum, and linked-list cycle detection. PracHub's question bank for this role adds grouping anagrams, merging intervals, Two Sum, course-schedule cycle detection, the first non-repeating character, moving non-zero elements left in place, the longest palindromic subsequence, and an O(1) LRU cache. Hash maps, two pointers, sorting, graph traversal and basic dynamic programming cover most of this list.

Which round asks which question?

The sources do not tie any reported question to a specific round. Treat the questions as categories: coding, software engineering fundamentals, system design and behavioral. Be ready for any category in the technical interviews, and practise the shorter coding problems under assessment conditions too.

Do I need to prepare Java specifically?

PracHub's question bank for this role includes Java-specific fundamentals questions: concurrent collections, Java 8 features, and exception handling in a RESTful service with @ControllerAdvice. If you plan to interview in Java, prepare the difference between ConcurrentHashMap and a synchronized map, streams and Optional, and centralised exception handling. Confirm with your recruiter whether a language is expected.

Is system design in scope for this role?

Candidates report design questions, including a notification system that supports millions of users and how to keep a microservices architecture available and fault tolerant. PracHub's question bank for this role adds a URL shortener, a reliable bank payment system, an e-commerce site and an auditable internal AI chatbot. For each one, prepare the guarantees you would offer and what happens when a dependency fails, not only the components.

Keep practising

More questions candidates ask

No. It is PracHub's own research and practice material for the Software Engineer role at J.P. Morgan. The rounds and questions reflect what candidates have reported, not a process J.P. Morgan has published, and they change over time. Confirm the current format and scope with your recruiter.

Candidates describe an online assessment, then technical interviews, then behavioral interviews. The sources for this guide do not give the number of technical interviews, the assessment's format, or the length of any stage. Ask your recruiter for those details before you plan your preparation.

The reported coding questions are the longest palindromic substring, the contiguous subarray with the largest sum, and linked-list cycle detection. PracHub's question bank for this role adds grouping anagrams, merging intervals, Two Sum, course-schedule cycle detection, the first non-repeating character, moving non-zero elements left in place, the longest palindromic subsequence, and an O(1) LRU cache. Hash maps, two pointers, sorting, graph traversal and basic dynamic programming cover most of this list.

The sources do not tie any reported question to a specific round. Treat the questions as categories: coding, software engineering fundamentals, system design and behavioral. Be ready for any category in the technical interviews, and practise the shorter coding problems under assessment conditions too.

PracHub's question bank for this role includes Java-specific fundamentals questions: concurrent collections, Java 8 features, and exception handling in a RESTful service with @ControllerAdvice. If you plan to interview in Java, prepare the difference between ConcurrentHashMap and a synchronized map, streams and Optional, and centralised exception handling. Confirm with your recruiter whether a language is expected.

Candidates report design questions, including a notification system that supports millions of users and how to keep a microservices architecture available and fault tolerant. PracHub's question bank for this role adds a URL shortener, a reliable bank payment system, an e-commerce site and an auditable internal AI chatbot. For each one, prepare the guarantees you would offer and what happens when a dependency fails, not only the components.

J.P. MorganSoftware EngineerInterview Guide