Adyen is a financial technology platform that processes payments for merchants. The source notes say Software Engineers work on teams such as the Banking Network, Financial Products and Developer Experience. They build high-throughput distributed systems that must meet strict regulatory requirements and stay maintainable. Engineers own the full development lifecycle of their services: they write code, review code, contribute to architecture, take part in on-call rotations, and work with Product Managers to define requirements.
The role notes say Java is frequently required, alongside SQL and distributed system design, and Linux experience helps. They also say the Java requirement depends on the team. If Java is not your main language, expect to show depth in another language and a willingness to learn.
The reported questions focus heavily on payments. They include the difference between an API key, an HMAC key and a client key, how you would implement a payment service provider (HTTP APIs, request content, authentication), counting card transactions over a rolling 10-second window, and keeping data consistent during a network partition. Prepare to explain the reasons behind your technical decisions and the trade-offs you accepted, such as latency versus consistency. Knowing a pattern by name is not enough.
Initial Screening
reportedThe source notes describe this stage as a conversation with a recruiter about your background and your fit for the role. Use it to give a clear account of the production systems you have built and a specific reason for wanting this role. The reported behavioural questions include 'Why Adyen?' and 'What are you looking for in your next role, and how does that fit the team?'. Both go better prepared than improvised, so have them ready before any conversation about fit. Raise hard constraints now rather than at offer stage: start date, notice period, location, and whether you will carry a pager (the role notes list on-call rotations). If Java is not your main language, say so and ask how much this team relies on it.
What to demonstrate
- Whether your background matches the role: production services you built and maintained, and the languages and databases you used
- Whether your motivation is specific to Adyen and to this team rather than generic to any payments company
- Whether constraints such as start date, location and on-call willingness are compatible with the role
How to prepare
- Write a short account of your two most relevant production systems: what they did, what your part was, and the stack. Name any Java, SQL and Linux experience explicitly
- Tie your 'Why Adyen' answer to something concrete, such as a product area named in the role notes, a payments problem you want to work on, or a principle from the Adyen Formula that matches how you already work
- List your constraints in one line each, and ask what the technical stage involves for this team, since the source notes say the depth of technical questioning varies by team
Technical Evaluation
reportedThe source notes describe this stage only as deep-dive technical assessments of your skills and knowledge, and they say teams adjust the depth to the role. The reported questions cover several categories, so prepare all of them instead of guessing which one your team uses. Coding: an LRU cache, target-sum combinations by backtracking, a modified Bellman-Ford dynamic program, and iterating on an existing HackerRank solution. Also: Java concurrency and thread safety, SQL including indexing versus partitioning, and API authentication (API key vs HMAC key vs client key). Design questions are payments-focused: a payment service provider, card transaction counts over a rolling 10-second window, and an event-driven payment gateway. In every category, name the trade-off you are making. The source notes stress trade-offs such as latency versus consistency over a single 'perfect' answer.
What to demonstrate
- Whether your code is correct, handles edge cases and invalid input, and comes with an honest complexity statement
- Whether you can reason about shared state in Java: what races, what makes it safe, and what that safety costs in throughput
- Whether you can explain API authentication precisely and design an HTTP API for a payment flow, including request content and failure handling
- Whether your designs name their trade-offs, such as in-memory versus persistent storage, latency versus consistency, and how the system scales horizontally
How to prepare
- Implement an LRU cache (hash map plus doubly linked list, O(1) get and put) and a target-sum combinations backtracker from memory. Then make the cache thread-safe and explain what your locking choice costs
- Write one paragraph each on API keys, HMAC keys and client keys: who holds it, what it proves, where it is allowed to live, and what a leak exposes
- Sketch the rolling 10-second count twice: exact, with a per-card queue of timestamps, and bucketed, with a ring of per-second counters. State the memory and accuracy trade-off between them
- Work the SQL, coding and design exercises in this guide, and state the invariant each must preserve before you write anything
Leadership Assessment
reportedThe source notes describe this stage as interviews with team leaders that assess cultural fit and alignment with company values. They point candidates to the Adyen Formula. Both the notes and the reported questions push for depth on past work: discuss your projects precisely and explain the reason behind each technical decision. Prepare stories for the reported behavioural questions: a technical conflict with a teammate and how you resolved it, working through ambiguity ('build the rocket while flying it'), and a project you owned end to end. The source notes also advise being direct, admitting when you do not know something rather than guessing, and bringing thoughtful questions for the leaders you meet.
What to demonstrate
- Whether you made the decisions in your project stories yourself, and can explain why each technical choice was made, not only what was built
- Whether you handle disagreement directly and resolve it with evidence rather than escalation or deference
- Whether you show the company values the source notes point to (the Adyen Formula) through specific examples rather than by reciting them
How to prepare
- Pick one project for a deep dive and write down its architecture, the alternatives you rejected, one decision you would reverse, and the measurable result
- Prepare three stories: a technical conflict, an ambiguous project, and a production problem you owned. Each should name the decision you made
- Read the Adyen Formula and link each principle you plan to mention to a specific moment in your own work
- Write three questions for a team leader about the team's systems, its current trade-offs and how decisions get made
4 candidate reports. Individual accounts describe a particular role and hiring cycle.
Adyen Software Engineer interview: unclear final communication
My interviews lasted a while, and the technical work was not the part that stayed with me most. I began with recruiter screening, then went through several rounds with technical discussion followed by leadership or manager-style conversations. At one point I was told I would be brought to the Amsterdam office for a leadership interview. That final round felt inconsistent with what I had shown ear…
Read full experienceAdyen Account Executive interview: missing customer context
Recruiting contacted me several times over about two months. Once I finally replied, the process followed a normal path: recruiter first, then a hiring-manager round that felt as though it went well. The third round surprised me because nobody had told me it would be a set of hypothetical, multilayered customer questions. I could speak at length about my own customers and had strong stories, but…
Read full experienceAdyen Machine Learning Engineer Interview Experience — Two Technical Rounds, a Broken HackerRank Link, and a Rejection
View report detailsAdyen Software Engineer Interview Experience — 100% on the OA, Rejected After the Technical Round
There isn't much Adyen interview experience on the forum, so let me contribute mine. I'd never heard of Adyen and didn't even know they hire in the US — they have an office in Chicago and require onsite for a few days. Interview process: HR -> HackerRank OA -> Technical -> System Design -> Leadership -> Board The OA had two questions: the first was LRU, the second was similar to combination sum,…
Read full experiencePracHub editorial advice for the preparation topics above.
Treating API keys, HMAC keys and client keys as interchangeable 'auth tokens' in the reported API authentication question
Separate them by what each proves and where it may live. An API key identifies and authenticates a server-side caller and must never reach a browser. An HMAC key is a shared secret used to sign a payload, such as a webhook, so the receiver can check its integrity and origin. Compute the signature over the raw bytes and compare it in constant time. A client key is designed to be embedded in client-side code, so it has to be narrowly scoped. For each one, say what an attacker gains if it leaks.
Designing the payment service provider or third-party network integration with retries but no idempotency
Any retry on a money-moving call can charge twice unless the request carries an idempotency key. The server records that key atomically before calling the downstream network. State where the key is generated, where it is stored, what a duplicate request receives back, and what happens when the process crashes between the network call and recording the result. The duplicate-captures debugging drill in this guide walks through exactly that failure.
Answering the rolling 10-second transaction-count question with a single counter that resets every 10 seconds
A fixed window that resets lets a burst straddle the boundary and slip through at up to twice the intended rate. Offer an exact sliding window, with a per-card queue of timestamps trimmed on each event, and a bucketed version, with a ring of per-second counters summed on read. State the trade-off between memory and accuracy. Then cover partitioning by card and what the counts look like after a node fails.
Reducing Java concurrency answers to 'add synchronized' without saying what it protects or costs
Name the shared mutable state and the exact interleaving that corrupts it. Then choose the tool by the guarantee you need: synchronized or a lock for compound actions, volatile for visibility only, atomic classes or ConcurrentHashMap.compute for single-key updates. Explain the throughput cost under contention. When you make an LRU cache thread-safe, note that every get reorders the list, so a read is also a write.
Describing a past project by what was built rather than why it was built that way
The source notes stress discussing past projects precisely, with the reason behind each decision. For the project you bring, prepare the alternatives you rejected, the constraint that decided the choice, the result you measured, and what you would change. If you are asked about something you did not decide or do not know, say so directly rather than guessing.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Compute peak held exposure from overlapping authorisation holds
An account has up to 2 million authorisations on one business date: (auth_id, amount_minor, created_at, expires_at) with the hold live over the half-open interval [created_at, expires_at), plus capture events (auth_id, captured_minor, captured_at) that reduce the hold at captured_at, and explicit reversals that drop the remainder to zero. Timestamps are microsecond-precision timestamptz. Return the maximum total held amount across the day and the earliest instant it is reached, with complexity. Then say what changes if the deliverable is the peak per minute instead.
Approach
- Expand each authorisation into signed delta events rather than reasoning about intervals:
+amountatcreated_at,-remainingatexpires_at,-captured_minorat eachcaptured_at,-remainingatreversed_at. The problem collapses to a running sum over a sorted event list. - Sort the 2n to 4n events by
(timestamp, sign)with negative deltas ordered first on a tie. The half-open convention forces that: at exactlyexpires_atthe hold is already gone, so a-must land before a+at the same instant or you report a one-microsecond peak that never existed. O(n log n) time, O(n) space. - Sweep once, tracking
running,bestandbest_at, taking the first instant that attains the maximum. Say out loud which tie rule you are using — 'the peak' is ambiguous when the same level is reached twice, and the caller needs to know which instant they are being handed. - If timestamps are bucketed (1,440 minute buckets for the per-minute variant), drop the sort for a difference array: add the delta at the start bucket, subtract at the end bucket, prefix-sum once. O(n + B) time and O(B) space, strictly better, at the cost of answering only at bucket resolution.
- Assert the invariant during the sweep:
runningmust never go negative. A negative total means a capture exceeded its authorisation, which is an invariant violation upstream rather than a sweep bug — fail loudly instead of clamping at zero and reporting a plausible number. - Handle carry-in: a hold created before the window contributes its remaining amount as the sweep's initial value, not as a
+event inside the window. Omitting that is the off-by-a-day that makes the first minute of every day look artificially quiet.
Follow-up
- An incremental authorisation raises an existing hold after the fact. Where does that event go, and does it disturb the tie rule?
- You now need the peak for 10 million accounts inside a nightly window. What changes, and what must the partitioning key be?
- The peak sizes a funding transfer. Does the business date or the timestamp decide which day that transfer lands on?
Rank merchants by unmatched break value in one streamed pass
A reconciliation run streams up to 50 million break rows of (merchant_id, currency, delta_minor signed, break_type). Return the 50 merchants with the largest total absolute delta_minor in a single currency, from one pass, with memory that does not grow with merchant count — there are up to 8 million distinct merchants and room for roughly 200,000 counters. Give both an exact and an approximate design, state the error bound the approximate one actually guarantees, and say which you would put in the nightly job.
Approach
- Start by testing whether the constraint is real. Exact needs one hash map
merchant_id -> int64plus a size-50 min-heap: O(n) to aggregate, O(m log 50) to rank, O(m) space. At 8 million merchants and 16 bytes of payload that is a few hundred megabytes — quote the number before reaching for a sketch. - If it genuinely does not fit, exact still costs only two passes or an external group-by: partition by
hash(merchant_id) % Pto disk, aggregate each partition independently, then merge with a K-way heap. This is the answer whenever exactness is non-negotiable, which for money it usually is. - One-pass approximate: weighted Space-Saving with C counters. An item either hits an existing counter or evicts the minimum one and inherits its value as an over-estimate. With total weight W and C counters, every reported total over-estimates by at most W/C, and any merchant whose true total exceeds W/C is guaranteed to be in the table. Quote W/200,000 as a fraction of the day's break value.
- State what the bound does not give you: the ordering within the top 50 is not guaranteed, and a merchant just below the threshold can be missed entirely. The honest claim to an operations reader is 'contains everyone above 0.0005% of today's break value', not 'the top 50'.
- Decide the metric before either design, because
sum(abs(delta))andabs(sum(delta))are different questions: a merchant with offsetting +1,000,000 and -1,000,000 breaks is top-ranked under the first and invisible under the second. Break work wants the first. - Recommend exact (two-pass or external group-by) for the nightly job — it runs once, has hours of budget, and an analyst acts on its output. Keep the sketch for a live dashboard, where a cheap bounded approximation beats an exact number that is four hours stale.
Worked solution 25 min
- Fix the metric in writing as
sum(abs(delta_minor))per(merchant_id, currency), because that is what an analyst works from. - Implement exact: hash-map aggregate, then a size-50 min-heap — push while the heap is under 50, then push-pop only when an item beats the root.
- Implement weighted Space-Saving with 200,000 counters over the same stream.
- Generate 50 million rows with a Zipfian merchant distribution near s = 1.1 so a few merchants dominate, then generate a uniform variant for contrast.
- Compare: how many of the exact top 50 the sketch recovers, and the largest observed over-estimate, against the W/C bound.
Follow-up
- Totals are now needed per currency as well as overall. What does that do to the key and to the counter budget?
- Prove the Space-Saving over-estimate bound to me in two sentences.
- The nightly job restarts halfway. Is the aggregate idempotent, and what does the heap do with a partially consumed stream?
Derive per-account balances and catch unbalanced transactions
You are given ledger_entry rows streamed in entry_id order: transaction_id, account_id, direction (debit or credit), amount_minor (a positive int64), currency, business_date. Up to 500 million rows, at most 20 million distinct (account_id, currency) pairs, and the entries of one transaction are contiguous in the stream. In a single pass with no re-reads, return the closing balance per (account_id, currency) and the transaction_id of every transaction whose entries do not sum to zero within each currency. State your time and space bounds.
Approach
- Normalise the sign at read time from
direction, not from the amount:signed = +amount_minorfor debit,-amount_minorfor credit (state which convention you picked). The schema constrainsamount_minor > 0precisely so the sign lives in exactly one place. - Hold one hash map keyed
(account_id, currency)to an int64 running total. Twenty million keys at 16 bytes of payload plus map overhead is order 1 GB in most runtimes — quote the number, and offer the fallback: partition the stream byhash(account_id) % Pand run P passes for 1/P of the memory. - Ride the zero-sum check on the same pass. Because a transaction's entries are contiguous, keep a tiny
currency -> int64map for the currenttransaction_idonly, test it against zero on the boundary and at EOF, then clear it. That is O(currencies in one transaction), typically one or two. - Bound the arithmetic explicitly. Int64 holds about 9.22e18, so overflowing one account across 500 million entries needs an average of 1.8e10 minor units per entry — safe here, but use a checked add so an adversarial file fails loudly rather than wrapping.
- Complexity: O(n) time, O(distinct account-currency pairs) space, one sequential pass, no sort. The zero-sum check adds no asymptotic cost, which is the argument for doing it here rather than in a second job.
Follow-up
- Entries of a transaction are no longer contiguous. What does the zero-sum check cost now, and which is cheaper: buffering open transactions or an external sort on
transaction_id? - How would you produce the same balances as of an arbitrary
business_datewithout a second full scan? - The job is restarted after a crash halfway through the file. What makes the second run produce identical output?
Store multi-currency amounts and prove each transaction balances
ledger_entry holds entry_id, transaction_id, account_id, direction (debit or credit), amount_minor (bigint, CHECK > 0), currency char(3), source_type, source_id, business_date and posted_at. The service posts in JPY, USD and KWD. Specify the column types for money and the reference table that carries each currency's ISO 4217 minor-unit exponent, and say why that exponent must not be a constant in application code. Then write the query that returns every transaction_id whose entries fail to sum to zero within each currency, ordered by the largest absolute imbalance.
Approach
- State the representation: amount_minor is an integer count of minor units and the scale lives with the currency, not in the column. int4 tops out at 2,147,483,647 minor units, which is about 21.5 million units of a 2-exponent currency, so bigint, and the currency code travels on every row that carries an amount.
- Normalise the scale into currency(code char(3) primary key, exponent smallint, name) and join it only at display and parse boundaries. The exponent is 0 for JPY and KRW, 2 for USD and EUR, 3 for KWD, BHD, JOD, OMR and TND, so a hard-coded divide by 100 is wrong by 100x for JPY and by 10x for KWD, in opposite directions.
- Rule out binary floating point outright: IEEE 754 binary64 cannot represent 0.1, so repeated accrual drifts by a few minor units that later appear as reconciliation breaks. If a fixed-scale decimal is used instead, pin the scale per currency and name the single place rounding happens.
- Write the balance check as a grouped aggregate: sign the amount with a CASE on direction, GROUP BY transaction_id, currency, and HAVING the signed sum <> 0. The sign belongs in direction, and the CASE is the one place it becomes arithmetic.
- Then order in a second layer, because the ordering the question asks for cannot be written on the grouped query itself. PostgreSQL lets an output alias stand alone in ORDER BY but resolves anything inside an expression against the input columns, so ORDER BY abs(imbalance_minor) DESC raises SQLSTATE 42703, column "imbalance_minor" does not exist, while the bare ORDER BY imbalance_minor DESC is accepted and sorts signed rather than absolute. Wrap the grouped query and order by abs() outside it, or repeat the aggregate as ORDER BY abs(SUM(CASE ...)) DESC, which is legal because aggregates are allowed there.
- Group per (transaction_id, currency) rather than per transaction, because a cross-currency movement balances only within each currency, bridged by an explicit FX position account whose rate, source and timestamp are stored on the transaction.
- Note that no CHECK constraint can express this, since it spans rows: enforce it at write time with a DEFERRABLE INITIALLY DEFERRED constraint trigger that fires at commit, or by routing every posting through one function, and keep this query as the independent audit.
Worked solution 15 min
- Define currency(code, exponent, name) and seed JPY 0, USD 2, KWD 3; declare amount_minor bigint NOT NULL CHECK (amount_minor > 0) and currency char(3) REFERENCES currency(code).
- Write it in two layers so it runs: SELECT transaction_id, currency, imbalance_minor FROM (SELECT transaction_id, currency, SUM(CASE WHEN direction = 'credit' THEN amount_minor ELSE -amount_minor END) AS imbalance_minor FROM ledger_entry GROUP BY transaction_id, currency HAVING SUM(CASE WHEN direction = 'credit' THEN amount_minor ELSE -amount_minor END) <> 0) b ORDER BY abs(imbalance_minor) DESC.
- Post one clean capture, one deliberately unbalanced transaction, and one cross-currency transfer with an FX position account, then run the query.
- Convert 1000 JPY and 1000 KWD through the exponent table in both directions and confirm the round trip is exact.
Follow-up
- A transaction has a USD leg and a JPY leg. Which accounts appear, and where does the rounding residual land?
- How would you enforce the zero-sum rule at write time without serialising all postings behind one lock?
- Someone proposes storing a display amount alongside amount_minor. What goes wrong at the first rate change or rounding rule change?
Explain why the outbox relay stopped using its partial index
outbox_event holds event_id, aggregate_type, aggregate_id, aggregate_version, event_type, payload jsonb, published_at, attempts, last_error, created_at, with index ix_unpub ON outbox_event (created_at) WHERE published_at IS NULL. The relay runs SELECT ... WHERE published_at IS NULL ORDER BY created_at LIMIT 500 FOR UPDATE SKIP LOCKED, then marks each row by setting published_at. Unpublished rows hold steady near 400, but the query has gone from 3 ms to 900 ms. Explain what EXPLAIN (ANALYZE, BUFFERS) will show, why it happens, and the fix.
Approach
- Read the plan for the gap between rows returned and work done: an index scan on ix_unpub returning 500 rows while touching tens of thousands of buffers is the signature. Rows Removed by Filter and the buffer counts name it; wall-clock alone does not, because a warm cache hides it.
- Explain the mechanism: marking a row published is an UPDATE, which writes a new tuple version. The new version fails the index predicate and leaves ix_unpub, but the dead old version's index entry stays until vacuum removes it, so the scan walks dead entries and discards them. PostgreSQL can hint an entry LP_DEAD once a scan has proved it dead, which cheapens repeat visits, but the index pages themselves still have to be read and are not reclaimed.
- Ask why vacuum is not reclaiming. Anything holding the xmin horizon back prevents removal: a long-running query, an idle-in-transaction session, an abandoned prepared transaction, or an inactive replication slot. Check the oldest xact_start in pg_stat_activity, pg_replication_slots, pg_prepared_xacts, and n_dead_tup with last_autovacuum in pg_stat_all_tables.
- Fix in order of leverage: delete or archive published rows instead of leaving them in place, so a queue table stays a queue; keep the transaction horizon short and alert on it; then tune autovacuum on this one table with an aggressive scale factor rather than changing the global setting.
- Rule out the other failure with the same symptom: a partial index is usable only when the planner can prove the query predicate implies the index predicate, so rewriting the filter as coalesce(published_at, 'epoch') = 'epoch' or wrapping the column in a function disqualifies the index entirely and produces a sequential scan instead of a bloated index scan.
- Verify by re-running EXPLAIN (ANALYZE, BUFFERS) after the horizon is released and a VACUUM completes, comparing shared buffer reads rather than elapsed time, and confirm the relay keeps per-destination ordering after the change.
Follow-up
- SKIP LOCKED means two relay workers never block each other. What else does it change about ordering guarantees for a single destination?
- You archive published rows to a second table. What does that do to the relay's crash recovery and to duplicate delivery?
- The relay batches 500 rows and publishes them, then marks them. Where exactly can it crash, and what does the consumer see?
How would you architect a system to track card transaction counts over…
How would you architect a system to track card transaction counts over a rolling 10-second window?
Approach
- Choose a partition key and say what query it makes expensive.
- Name the read and write paths separately; they rarely have the same bottleneck.
- State the consistency you need, and where you are willing to be stale.
Follow-up
- How does this behave when that dependency is down for an hour?
- What would you drop to keep the system up under load?
Describe how you would integrate with 3rd party payment networks while…
Describe how you would integrate with 3rd party payment networks while maintaining high availability.
Approach
- Name the failure you are designing for, then the recovery path.
- Fix the scope first: who calls this, how often, and what they do when it fails.
- State the consistency you need, and where you are willing to be stale.
Follow-up
- How does this behave when that dependency is down for an hour?
- What would you drop to keep the system up under load?
What are the bottlenecks in a legacy system, and how would you moderni…
What are the bottlenecks in a legacy system, and how would you modernize them without downtime?
Approach
- Name the failure you are designing for, then the recovery path.
- State the consistency you need, and where you are willing to be stale.
- Fix the scope first: who calls this, how often, and what they do when it fails.
Follow-up
- What would you drop to keep the system up under load?
- How does this behave when that dependency is down for an hour?
How would you implement a payment service provider? Explain your appro…
How would you implement a payment service provider? Explain your approach to HTTP APIs, request content, and authentication.
Approach
- Work from the requirement backwards to the design.
- Clarify what is being asked and what a complete answer contains.
- Say what you would check first and why it is the highest-information step.
Follow-up
- How would you know your answer was wrong?
- What assumption would you test first?
How do you ensure data consistency in a distributed system during a ne…
How do you ensure data consistency in a distributed system during a network partition?
Approach
- Work from the requirement backwards to the design.
- Clarify what is being asked and what a complete answer contains.
- Say what you would check first and why it is the highest-information step.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
Webhook intake under duplicate, unordered delivery and ledger outage
A processor POSTs 5,000 signed events/s to one endpoint, at least once, unordered, retrying for 24 hours until it gets a 2xx, and treating a response slower than 10 seconds as a failure. Each event carries a provider event id, an object id and an object version. Your ledger is occasionally unavailable for minutes at a time. Design intake: signature verification, deduplication, how a succeeded event arriving before the processing event it follows is applied, and what status you return while the ledger is down. Justify that status and bound the backlog.
Approach
- Verify before parsing. Compute HMAC-SHA256 over the timestamp concatenated with the raw request bytes, compare in constant time, and reject a timestamp outside a tolerance of a few minutes so a captured request cannot be replayed indefinitely. Parsing to an object and re-serialising before verification is how a signature check silently stops working the day a JSON library reorders keys or renormalises a number.
- Make 'accept' mean 'durably recorded', not 'applied'. Insert (provider_id, provider_event_id, raw body, received_at) under a UNIQUE constraint on the provider's event id and return 2xx in single-digit milliseconds; apply asynchronously. The endpoint doing no business logic is what keeps it inside the provider's timeout at 5,000/s, and the constraint handles the at-least-once repeats without application logic.
- Order by version, never by arrival: UPDATE payment_intent SET status = $s, version = $v WHERE intent_id = $1 AND version < $v. A stale succeeded-then-processing pair updates zero rows on the second event and is recorded as discarded. Arrival time and the provider's own created timestamp are both unusable for ordering, because retries reorder both.
- Split the failure case by which store is down, because the answer differs. If acceptance storage is healthy and only the ledger is down, keep returning 2xx: you hold the durable record and version-ordered application makes the replay safe, so availability costs you nothing but lag. If acceptance storage itself is down, return 5xx and let the provider's 24-hour retry schedule be your queue; a 2xx you cannot honour is an event the provider will never send again.
- Bound the backlog explicitly in rows or in lag-minutes, alarm on it, and shed to 503 past the bound rather than accepting work you cannot drain. The health metric that matters is per-object version gaps, not queue depth: a queue can be empty while an object is stuck three versions behind.
Worked solution 25 min
- Build the receiver: constant-time HMAC over timestamp plus raw body, a tolerance window, then an INSERT under UNIQUE (provider_id, provider_event_id) and an immediate 200 with no application.
- Replay a captured request outside the tolerance window and assert rejection; replay it inside the window and assert one stored row rather than two.
- Deliver succeeded at version 7 before processing at version 5 and apply both through the version-conditional UPDATE.
- Stop the ledger, drive 5,000 events/s for five minutes, and watch endpoint p99 and backlog growth.
Follow-up
- After a four-hour outage the provider replays everything. What does your consumer do with 70 million duplicates?
- One hot merchant object receives 50 events/s. Does the version-conditional update starve or livelock, and what do you change?
- You detect a permanent gap at version 12 for one object. How do you close it?
Duplicate captures appear only in production, roughly weekly
About once a week one payment is captured twice. The idempotency path is: SELECT id, response_body FROM idempotency_key WHERE scope = $1 AND key = $2; if no row, call the processor; then INSERT. The table has UNIQUE (scope, key). Logs for each duplicate show one successful capture pair and one HTTP 500 carrying SQLSTATE 23505. A 200-iteration sequential test passes, and a 50-thread version passes on a laptop but fails on the production-sized cluster. Explain why, and give the fix.
Approach
- Read the 23505 as evidence, not as noise. A unique violation on the INSERT proves two requests both passed the SELECT and both reached the INSERT, which means both had already called the processor. The duplicate charge happened before the constraint fired. The constraint is reporting the race; it is not causing it, and anyone who treats the 500 as the bug fixes the wrong thing.
- Name the interleaving precisely. Under READ COMMITTED each statement takes a fresh snapshot, so two concurrent requests with the same key can both run the SELECT before either INSERTs and both see zero rows. Raising the isolation level does not fix check-then-act by itself, because at SELECT time the first transaction has written nothing to conflict with; SERIALIZABLE only converts the race into a 40001 abort that the code must then retry.
- Explain the reproduction gap rather than calling the bug rare. The window is the duration of the processor call: milliseconds against a stub on a laptop, hundreds of milliseconds against a real processor. Production retries are also correlated, since a client timeout produces a second request at a predictable delay, while a thread-pool test fires all 50 within microseconds and lands them on the same side of the window. The local test is not exercising the window at all.
- Restructure so the database picks the winner before any side effect. INSERT the key first with ON CONFLICT (scope, key) DO NOTHING RETURNING id. A returned id means this request owns the effect and may call the processor. No returned row means another request owns it, and note that RETURNING yields nothing on conflict, so the loser must then SELECT the existing row explicitly. Mutual exclusion now lives in one atomic statement and the window is gone.
- Give the loser something to read. If the winner is still in_progress, the loser must neither error nor perform the effect: it polls the row inside the caller's timeout budget and replays response_status and response_body once status is completed, or returns 409 when request_fingerprint differs. Without this, deduplication turns a successful payment into a visible failure.
- Close the crash window separately, because the atomic insert does not cover it. If the winner dies after calling the processor and before writing completed, the row stays in_progress with a stale locked_at. Recovery must query the processor for that key or client reference rather than assume either outcome, which is why the processor's own idempotency key has to be the same value, generated once by the caller and reused on every attempt.
Follow-up
- The same key arrives with a different request_fingerprint. What do you return, and why is returning the cached response wrong?
- What is your locked_at staleness threshold, and what does the sweeper do when it finds an expired one?
- Write the test that fails on the laptop. What do you have to inject to make the window observable there?
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.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Coding: LRU cache and backtracking
- Implement the reported LRU cache with a hash map and a doubly linked list so get and put are O(1). Test capacity 1, repeated puts to the same key, and eviction order after a get
- Solve target-sum combinations by backtracking: sort the candidates, prune once the running sum exceeds the target, and settle whether an element may be reused before you start, because that changes the recursion
- Iterate on each first working version, as the bank item on iterating on a HackerRank solution suggests: state its complexity, improve one dimension, and write down what changed
Deliverable: Working LRU cache and combinations code with edge-case tests, plus a one-line complexity statement for each version.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02Coding: graphs, dynamic programming and streamed aggregation
- Implement Dijkstra with a binary heap and Bellman-Ford. State when Bellman-Ford is required (negative edge weights) and its O(VE) cost
- Solve a modified Bellman-Ford variant, such as the cheapest path using at most k edges: run k rounds of relaxation, each reading from a copy of the previous round's distances
- Work the 'Rank merchants by unmatched break value' worked exercise in this guide and confirm your results against its checks
- Do the 'Derive per-account balances' drill and state its time and space bounds before writing code
Deliverable: Two graph implementations with a note on when each applies, and the ranking exercise's checks passing.
Practice prompt ↗Practice prompt ↗03Java concurrency and SQL
- Make the day 1 LRU cache thread-safe two ways, first with one lock around get and put and then with a finer-grained design, and explain what each costs under contention
- Write answers to the reported Java concurrency questions: which shared state in a production service can race, and what synchronized, volatile, atomic classes and ConcurrentHashMap each guarantee
- Work the multi-currency SQL worked exercise, then the outbox partial-index drill
- Write a short answer to the bank question on indexing versus partitioning: what each speeds up and what each costs on writes
Deliverable: Two thread-safe cache variants with a contention note, and the SQL exercise query executed with its checks passing.
Practice prompt ↗Practice prompt ↗04API design and authentication for payments
- Answer the reported question on API keys, HMAC keys and client keys in writing: what each proves, where it lives, and what a leak exposes
- Design the HTTP API for a payment service provider: resources, request content, authentication, an idempotency key on every money-moving call, and the error responses a merchant must handle
- Work the webhook intake worked exercise in this guide (HMAC verification over raw bytes, deduplication, version ordering), then write a paragraph on the bank question comparing webhooks with event handlers
Deliverable: A one-page API specification for a payment endpoint, and the webhook exercise's checks verified.
Practice prompt ↗Practice prompt ↗Worked solution ↗05System design: rolling counts and an event-driven gateway
- Design card transaction counts over a rolling 10-second window. Compare exact per-card timestamp queues with a ring of per-second buckets, choose how state is partitioned by card, and walk through a node failure
- Design the reported event-driven payment gateway: how events flow, how load is balanced, how consumers scale horizontally, and how ordering is kept per payment
- Write trade-off answers on an in-memory cache versus a persistent database in a high-concurrency payment path, and on consistency during a network partition: which operations must refuse to proceed and which may serve stale data
Deliverable: Two design sketches, each with a stated consistency choice and one failure scenario walked through end to end.
Practice prompt ↗Practice prompt ↗06Integrations, legacy systems and production debugging
- Design an integration with third-party payment networks that stays highly available: timeouts, retries with idempotency, circuit breaking, and what a merchant sees while a network is down
- Outline how to modernise a legacy system without downtime: find the bottleneck, migrate behind a stable interface with dual writes or read comparison, and keep a rollback path
- Work the duplicate-captures debugging drill and the bank item on diagnosing service slowness, and practise the Linux commands you would use to inspect a slow process, its logs and its network connections
Deliverable: A written failure table for the network integration, and a debugging write-up for the duplicate-capture drill.
Practice prompt ↗Practice prompt ↗07Screening and leadership preparation, then a full mock
- Write your answers to 'Why Adyen?' and 'What are you looking for in your next role?', each tied to a specific product area or technical problem, and read the Adyen Formula
- Prepare the project deep dive plus stories for a technical conflict and for ambiguity, each naming the decision you made and the result
- Run a mock with one coding problem from day 1 or 2, one design prompt from day 5 or 6, and the project deep dive, then note where your explanations lost precision
- Write three questions for a team leader
Deliverable: Motivation answers, three behavioural stories, a project deep-dive outline, and mock notes marking where precision slipped.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
The reported behavioural questions ask for specifics: a technical disagreement and how it was resolved, a period of ambiguity, and your reasons for choosing Adyen and your next role. The source notes advise directness and precision about the reasons behind past decisions. Prepare stories where you made the call, open each with the decision, and admit when you do not know something rather than guessing.
Design an event-driven architecture for a payment gateway. How do you …
Design an event-driven architecture for a payment gateway. How do you handle load balancing and horizontal scaling?
Approach
- Pick a story where you made the decision, not one where you watched it.
- Close with what you would do differently, concretely.
- State the situation in two sentences and spend the rest on the reasoning.
Follow-up
- What did you decide not to do, and why?
- What would you do differently if you ran that again?
Describe a situation where you had to deal with ambiguity and "build t…
Describe a situation where you had to deal with ambiguity and "build the rocket while flying it."
Approach
- Pick a story where you made the decision, not one where you watched it.
- Give the blast radius: what could have broken, and what you measured.
- Name the disagreement and how you resolved it with evidence.
Follow-up
- What would you do differently if you ran that again?
- What did you decide not to do, and why?
Tell me about a time you had a conflict with a teammate regarding a te…
Tell me about a time you had a conflict with a teammate regarding a technical decision. How did you resolve it?
Approach
- Close with what you would do differently, concretely.
- Name the disagreement and how you resolved it with evidence.
- State the situation in two sentences and spend the rest on the reasoning.
Follow-up
- How did you know your change caused the improvement?
- What would you do differently if you ran that again?
- 01
Why Adyen? Which aspects of its product and engineering culture appeal to you?
- 02
Tell me about a time you had a conflict with a teammate regarding a technical decision. How did you resolve it?
- 03
Describe a situation where you had to deal with ambiguity and "build the rocket while flying it."
- 04
What are you looking for in your next role, and how does that align with the team you are interviewing with?
- 05
Walk through a project you owned end to end: the decisions you made, the alternatives you rejected, and the result.
- 06
What is the project you are most proud of, and what was your part in it?
Is this an official Adyen interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at Adyen. The rounds and questions reflect what candidates have reported, not a process Adyen has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗How long does the entire process usually take?
Candidates report three stages over roughly three to five weeks. One set of notes puts it at three to six weeks, depending on your availability and the team's schedule. If you have a competing deadline, tell the recruiter early and ask whether the process can fit inside it.
PracHub interview research ↗Is Java a mandatory requirement?
The role notes say Java is frequently required and that the requirement depends on the team. If Java is not your main language, be ready to show depth in another language and a willingness to learn. The reported Java concurrency and thread-safety questions are still worth preparing, because the ideas carry over between languages.
PracHub interview research ↗How much should I know about Adyen's business?
The source notes advise researching Adyen's payment platform and reading the Adyen Formula before your first interview. For preparation, focus on what shows up in the reported questions: how payment APIs authenticate callers, how a payment service provider handles requests, and how payment systems stay available and consistent. Link any value you mention to something you have actually done.
PracHub interview research ↗What if I don't have experience in FinTech?
The source notes emphasise engineering fundamentals over industry experience. Be ready to explain how your past technical problems carry over to payments. For example, retries and idempotency, consistency under failure, and API security all have equivalents outside finance.
PracHub interview research ↗Which technical topics should I prepare?
The reported topics include payment service provider design, Java programming and concurrency, authentication and authorisation for APIs (API key vs HMAC key vs client key), HTTP API design, SQL and indexing versus partitioning, and distributed-systems trade-offs. Reported coding items include an LRU cache, target-sum combinations by backtracking, and a modified Bellman-Ford dynamic program.
PracHub Software Engineer practice ↗Sources & methodology 3 sources ↗
Official role evidence, timestamped platform data and clearly labeled preparation advice.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
platform · Accessed 2026-09-24 - 02PracHub Software Engineer practice ↗
Cross-company practice questions for this role.
platform · Accessed 2026-09-24 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
platform · Accessed 2026-09-24