DEV Community

Anas Bahraoui
Anas Bahraoui

Posted on

This Spring Boot Endpoint Has 3 Bugs. Can You Find Them?

Most backend interview practice asks you to write code.

Let's do the opposite.

You're reviewing a pull request for a Spring Boot application.

The endpoint transfers money between two accounts.

The code compiles. It looks reasonable.

But there are 3 important bugs hiding in it.

Can you find all three before reading the answers?

@PostMapping("/transfer")
public ResponseEntity<?> transfer(@RequestBody TransferRequest request) {

    Account sender = accountRepository
        .findById(request.getFromAccountId())
        .orElseThrow();

    Account receiver = accountRepository
        .findById(request.getToAccountId())
        .orElseThrow();

    if (sender.getBalance() < request.getAmount()) {
        return ResponseEntity
            .badRequest()
            .body("Insufficient funds");
    }

    sender.setBalance(
        sender.getBalance() - request.getAmount()
    );

    receiver.setBalance(
        receiver.getBalance() + request.getAmount()
    );

    accountRepository.save(sender);
    accountRepository.save(receiver);

    return ResponseEntity.ok("Transfer completed");
}
Enter fullscreen mode Exit fullscreen mode

Assume:

  • balance and amount are double
  • both accounts are stored in a relational database
  • multiple transfer requests can happen concurrently
  • there is no additional validation or transaction logic elsewhere

Before scrolling further, write down the 3 problems you'd flag in the PR.


Bug #1: The amount isn't validated

Nothing prevents:

amount = -500
Enter fullscreen mode Exit fullscreen mode

Now look at the calculations:

sender.getBalance() - (-500)
receiver.getBalance() + (-500)
Enter fullscreen mode Exit fullscreen mode

The sender gains 500 and the receiver loses 500.

A financial operation should reject zero or negative transfer amounts before touching either account.


Bug #2: double is being used for money

Floating-point numbers cannot precisely represent many decimal values.

Something as ordinary as repeated operations involving values such as 0.1 can introduce rounding errors.

Money should normally use a decimal representation such as Java's BigDecimal, with an explicit rounding policy where necessary.


Bug #3: The balance check and update aren't concurrency-safe

This one is easier to miss.

Imagine the sender has $100.

Two requests arrive at almost the same time:

Transfer A: $80
Transfer B: $80
Enter fullscreen mode Exit fullscreen mode

Both requests can read the original $100 balance.

Both pass:

if (sender.getBalance() < request.getAmount())
Enter fullscreen mode Exit fullscreen mode

Then both continue with the transfer.

Depending on the database behavior and update strategy, you can get lost updates, inconsistent balances, or allow operations that should never have passed together.

The check and mutation need to be protected as one consistent operation, for example with appropriate transaction boundaries and locking/atomic database semantics.


Catching these bugs after someone explains them is easy.

The interesting question is:

Would you have caught them while reviewing the PR yourself?

That's the skill I've been experimenting with in ReviewIQ.

You choose your role, programming language and seniority and get an unfamiliar pull request containing 3 planted bugs.

You review it without seeing the answers first.

Then your review gets graded on what you caught, what you missed and how you explained it.

First 5 reviews are free. No card:

https://reviewiq-ruby.vercel.app/

If you try one, I'm curious what score you get.

Top comments (0)