Revolut Java Live Coding Interview: Tests, Changing Requirements, and Concurrency

Prepare for Revolut Java live coding with tested reservation code, JUnit examples, changing requirements, retry semantics, and concurrency trade-offs.

Author: PracHub

Published: 9/9/2026

Revolut Java Live Coding Interview: Tests, Changing Requirements, and Concurrency

September 9, 2026

Quick Overview

Prepare for Revolut Java live coding with official guidance, carefully scoped candidate reports and an original inventory-reservation exercise. Follow input contracts, request-ID retry semantics, executable JUnit tests, a deterministic race-condition example and synchronization trade-offs without assuming a fixed interview loop.

Backend EngineerFree

In a Revolut Java live coding interview, a working method is only part of the answer. You need to explain the contract, demonstrate it with tests, and recognize when a new requirement changes the unit of work that must be atomic.

Our preparation recommendation: rehearse one small Java program as its requirements grow. The inventory-reservation exercise below is original PracHub practice, not a reported Revolut prompt. It moves from input validation to retry behavior and concurrency so you can see which earlier assumptions stop being sufficient.

Use PracHub's Backend Engineer questions to follow up on the specific weakness your rehearsal exposes, rather than collecting another broad list of backend definitions.

A Java reservation exercise progresses from contract to tests to concurrent correctness

What Revolut officially says, and where reports differ

Official facts: Revolut's Java interview article, dated March 31, 2025, describes live Java coding with clear explanations and readable solutions. Its preparation advice includes data structures, testing best practices, and multithreading theory and implementation. It discusses deeper technical and system-design interviews separately. Use that guidance to prepare the skills, while checking your invitation for the current sequence and setup. Revolut's Java interview guide.

Official role context: the Mid/Senior Java opening reviewed for this article lists Java 17/21 and describes development with TDD, DDD, and continuous integration and delivery. A team's engineering practices do not, by themselves, establish a mandatory test-first procedure or exact JDK for every interview. Revolut's Software Engineer Java role.

Candidate-reported briefing: in a February 2026 discussion, an applicant said their recruiter described three feature tasks, unit tests, and concurrency concerns. The applicant also reported that TDD was encouraged but could be skipped if it slowed them down. This describes a briefing before the interview, not a completed-round account. The February discussion.

Candidate-reported experience: the author of a June 2026 thread later described four feature stories followed by a concurrency discussion. That author also characterized TDD as required for senior candidates. The two accounts do not establish one universal task count or testing rule. We do not have two matched, independent completed-round accounts that justify a fixed current loop. The June thread and follow-up.

Our inference: prepare to test each increment and discuss concurrency, then follow the instructions for your actual round. Confirm IDE, starter project, JDK, test framework, and assistance rules beforehand; forum reports cannot grant permission to use AI during an interview.

Start with a contract small enough to test

Imagine one warehouse item with five available units. Implement a reservation operation taking a request ID and positive integer quantity. It returns whether the reservation succeeded.

Agree on the first rules: initial stock cannot be negative; request IDs cannot be null or blank; quantities must be positive; insufficient stock returns false without changing inventory. No restocking, cancellation, persistence, or multiple items exist yet.

Write the invariant in plain English: available stock never becomes negative, and accepted new requests consume exactly their requested units. A request for all five units should succeed and leave zero. A request for six should fail and leave five.

Do not begin with a web controller, repository interface, dependency-injection container, and database migration unless the exercise asks for them. A small object with a clearly owned integer is enough to expose the important behavior. Add a boundary when it represents a real dependency or responsibility.

Before implementing, write one successful reservation test and one insufficient-stock test. Their names should explain behavior, such as insufficientStockDoesNotChangeAvailability. A test that only constructs the class proves very little about its contract.

Add retries without consuming inventory twice

Now change the requirement: a caller may retry because it did not receive the first response. Repeating the same request ID and quantity must return the original outcome without another deduction. Reusing the ID with a different quantity is an error.

For this exercise, store both successful and unsuccessful outcomes. That means an initially rejected request stays rejected on retry. There is no restock operation here, but stating the policy now prevents an accidental semantic change if restocking is added later.

The following complete class includes synchronization for the later concurrency requirement. In a rehearsal, first implement the sequential contract, then identify which operations need to share a lock.

import java.util.HashMap;
import java.util.Map;

public final class Reservations {
    private record Outcome(int quantity, boolean accepted) {}
    private final Map<String, Outcome> outcomes = new HashMap<>();
    private int available;

    public Reservations(int initialStock) {
        if (initialStock < 0) throw new IllegalArgumentException();
        available = initialStock;
    }

    public synchronized boolean reserve(String requestId, int quantity) {
        if (requestId == null || requestId.isBlank() || quantity <= 0) {
            throw new IllegalArgumentException();
        }
        Outcome previous = outcomes.get(requestId);
        if (previous != null) {
            if (previous.quantity() != quantity) {
                throw new IllegalArgumentException("Conflicting retry");
            }
            return previous.accepted();
        }
        boolean accepted = quantity <= available;
        if (accepted) available -= quantity;
        outcomes.put(requestId, new Outcome(quantity, accepted));
        return accepted;
    }

    public synchronized int available() {
        return available;
    }
}

The map is part of the business rule: it remembers the original result for a request identity. A set of successful IDs would not capture rejected outcomes or detect a conflicting quantity.

This implementation has an explicit limit: the history grows with distinct requests and disappears when the object is lost. It demonstrates in-memory retry semantics within one process. It does not provide durable idempotency across restarts or multiple service instances.

If an interviewer asks about expiration, do not simply delete old IDs and call the problem solved. A delayed retry after deletion could consume stock again. Clarify the retry window, retention policy, and what the caller should do after that window before changing the storage behavior.

Write tests that distinguish the new requirement

Here is a focused JUnit Jupiter test for the successful-retry path and its conflicting-input boundary:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class ReservationsTest {
    @Test
    void retryReturnsOriginalResultWithoutAnotherDeduction() {
        Reservations stock = new Reservations(5);
        assertTrue(stock.reserve("r1", 3));
        assertTrue(stock.reserve("r1", 3));
        assertEquals(2, stock.available());
        assertThrows(IllegalArgumentException.class,
            () -> stock.reserve("r1", 2));
        assertEquals(2, stock.available());
    }
}

Use assertions for observable results and exceptions, rather than relying on printed output that someone must interpret. JUnit's documentation covers these assertion facilities; your interview environment may use another supported test framework. JUnit's user guide.

Add separate tests for zero and negative quantities, null and blank IDs, exact depletion, insufficient stock, and retrying a rejected request. The last case matters because an implementation that only caches successes can pass the successful-retry example while violating the agreed contract.

Keep a regression test when the requirement changes. If you modify the map representation, the earlier insufficient-stock test should still pass. If a test fails, explain the expected state, the observed state, and the smallest relevant correction before restructuring unrelated code.

Our reference checks compile the article's class and run its JUnit test alongside additional boundary and concurrency cases. We used Java 21 and JUnit Jupiter 5.11.4 for verification. These are our test versions; confirm the tooling for your own interview.

Make a race reproducible before proposing the fix

Consider an unsafe implementation that reads available into a local variable, checks it, and later writes back the decremented value. With one unit left, two different requests can both return success.

StepThread AThread BShared stock
ReadSaves local value 1Saves local value 11
DecideOne unit is sufficientOne unit is sufficient1
WriteWrites 0 and succeedsNot yet written0
WriteAlready returnedWrites 0 and succeeds0

The final stock is zero, so testing only that it is nonnegative misses the defect. Two successful one-unit reservations against one initial unit are already an error. Check both the state and the number of accepted requests.

In the deliberately unsafe test fixture, put a barrier after each thread has read the original stock and before either writes. Both threads must reach the barrier before proceeding. That forces the problematic interleaving without hoping an arbitrary sleep happens to expose it.

The barrier is a test instrument for the broken fixture, not code to add inside the corrected critical section. Putting a two-party barrier inside a lock held by the first thread would prevent the second thread from reaching it.

For the corrected object, release two callers from a start latch, wait for their results with bounded timeouts, and assert that exactly one succeeds. Also launch two retries with the same ID: both should return the same successful outcome, but inventory should be deducted only once.

A passing concurrent run verifies those executions; it does not prove correctness under every schedule. Combine it with reasoning about the protected invariant and a deterministic witness showing why the original implementation was wrong.

A whole reservation transaction shares one monitor, including retry lookup and outcome storage

Explain why the lock covers the whole operation

The critical section includes retry lookup, stock checking, stock mutation, and outcome storage. Protecting only available -= quantity would leave the earlier check and retry decision exposed to races.

Java specification fact: a synchronized instance method acquires the instance's monitor. An unlock on a monitor happens-before a subsequent lock on that same monitor, providing the relevant ordering and visibility relationship. The synchronized getter uses the same monitor as the mutation. Java Language Specification: Threads and Locks.

Making the integer volatile would not turn this multi-step operation into one atomic decision. Replacing the history map with ConcurrentHashMap would protect its individual supported operations, but would not automatically make an unrelated stock update and result recording one transaction. Oracle's ConcurrentHashMap API.

For one small in-memory object, the coarse lock is straightforward to reason about. Its cost is serializing callers. Per-item locks might improve concurrency after introducing multiple inventory items, but a request spanning two items would need an agreed lock order or another atomicity strategy.

Keep network calls and slow external work outside this monitor unless the contract requires a carefully designed coordination mechanism. A Java lock also cannot coordinate independent service processes. Moving the state into a database requires revisiting transaction boundaries, uniqueness, and failure recovery rather than copying the same synchronized method into each instance.

Respond to changes with a short explanation and a test

A useful response to a new requirement has a concrete shape: “Retries mean request identity is now part of correctness. I'll store the original quantity and outcome, reject conflicting reuse, and add a test showing that two calls deduct once.”

That is more informative than announcing that you will apply SOLID or DDD. Explain the responsibility you are protecting, implement the smallest change, and show the result. If you choose a simple lock, name its limitation before proposing a more complicated alternative.

Finish a rehearsal by reviewing the tests that actually failed during development. Distinguish a missing input rule from a state-transition error and a concurrency defect. Then repeat the weakest part with different numbers so you are practising the reasoning, not remembering one output.

Five targeted questions to practise next

These PracHub questions train adjacent skills. They are not predictions of Revolut's question set; the concurrent-deposit prompt uses a different language, so focus on the read-modify-write reasoning.

PracHub questionPreparation focus
Fix race condition in concurrent depositIdentify the lost update and protect the complete invariant.
Assess HashMap vs ConcurrentHashMapSeparate collection guarantees from business transactions.
Explain Java ConcurrentHashMap and queuesChoose a primitive based on the required operation.
Design a thread-safe bounded queueReason about waiting, capacity, timeouts, and progress.
Identify and handle race conditionsCompare lock granularity and verification strategies.

Continue in PracHub's Backend Engineer collection, and make your next solution reviewable through its contract, tests, and explanation of what is atomic.

Sources and Further Reading


Comments (0)