At a glance

Most software is written as though only one thing happens at a time. In production, several things happen at once, constantly.

When two of them touch the same data in the same instant, you get results that should be impossible. And because it requires precise timing, it is invisible while you are quiet and unavoidable once you are busy.

The last ticket

Here is the whole problem in one example.

One ticket left. Two customers click Buy at the same moment.

The software does what it was told, twice, in parallel. It checks whether a ticket is available for the first customer: yes. It checks for the second customer: also yes, because the first has not finished yet. Both checks passed, so both purchases go through.

You have now sold two tickets and you have one. Neither customer did anything wrong. The code is not incorrect in any way you could point at by reading it. It simply assumed nobody else would be doing anything in the half-second it took to make up its mind.

What it looks like from where you sit

You will not see a message saying "concurrency problem". You will see this:

  • Two invoices with the same number, which everyone assumes is a one-off glitch
  • Stock counts that drift away from reality and have to be corrected manually every week
  • The same order processed twice, or a customer charged twice
  • A booking sold to two people
  • Records that are half-updated, in a state the software should not be able to produce
  • Anything anyone describes as "intermittent" or "we could not recreate it"

That last one is the tell. If a bug cannot be reproduced on demand but keeps happening, timing is very often the reason.

Why they are so hard to pin down

Because reproducing one requires recreating a coincidence.

You need two actions to overlap within a window that might be a few thousandths of a second wide. Sitting at your desk clicking through the application, you will never manage it. Under real load, with hundreds of people using the system, that coincidence happens several times a day without anyone trying.

So the developer cannot recreate it, the logs show two entirely normal-looking requests, and the whole thing gets written off as a glitch. Meanwhile someone in accounts is quietly fixing the numbers by hand every Friday.

Why AI writes this by default

Because the straightforward version of the code is the version with the problem in it.

Check whether it is available, then take it. Read the current number, add one, save it back. That is the obvious way to express the instruction, it is what the examples show, and it reads perfectly sensibly. The safe version requires knowing that two of these can run at once and deliberately writing code that accounts for it.

That is not a coding skill. It is knowing that the situation exists, which comes from having been bitten by it before. AI has read a great deal of code that has this problem, because most code does.

The cruel part

Concurrency problems scale with traffic. Not linearly, either. Doubling the number of simultaneous users more than doubles the chance of two actions colliding.

Which means the trigger for these bugs is you doing well. Quiet Tuesday, everything is fine. The morning after a piece of press coverage, or the first hour of a sale, and suddenly the numbers stop making sense.

Software that fails when it succeeds is a particularly demoralising thing to own, and it is why we treat this as an urgent category rather than a theoretical one.

The fix

Reassuringly boring, and long since solved. Databases have had good answers to this for decades.

Transactions group several changes so they all happen or none do, which removes the half-updated states.

Locking makes the second request wait its turn rather than charging ahead on stale information.

Constraints let the database itself refuse to store something impossible, such as two invoices with the same number, so correctness does not depend on every piece of code remembering to be careful.

None of this is exotic or expensive. It just has to be applied deliberately by someone who knows the problem exists, and it is far easier to add before your data has drifted than after.

It is one of the standard things we look at when rescuing an AI-built app, usually alongside the missing indexes and the missing ownership checks. They tend to travel together.

And the reason they travel together is the one this series keeps coming back to: AI is a brilliant typist and a terrible architect. Every line was written correctly. Nobody decided what should happen when two people arrive at once.

Got a bug everyone calls intermittent? It is probably this. Our AI Code Audit is a fixed-price £495 review that tells you plainly what you are dealing with, or get in touch for a no-pressure conversation.