← All posts
Deep dive #validation#backend#money#invariants#testing

A minimum-price guard computed in three places stopped being a guard

The same floor was derived at quote time, at write time and at edit time with different inputs, so the number shown, the number enforced and the number actually needed were all different. A clamp downstream turned the resulting shortfall into a clean zero.

A booking system let a customer set a unit price. That price had to cover the cost of the resources hired plus the operators running them; whatever was left became a payout pool. To stop the pool going negative there was a floor:

costToCover  = operatorCost + resourceCost
minUnitPrice = costToCover / ((1 - platformPct) * units)

One rule. It was computed in three different places.

Three answers to one question

I probed the live API by bisection — submit a price, see if it is rejected:

WhereValue
Quote endpoint, shown in the UI638.89
Create endpoint, actually enforced569.45
What billing later chargedneeded 708.33

569 was rejected, 570 was accepted. So the number the interface promised was not the number the server insisted on, and neither covered the bill.

The cause was mundane. The quote path passed the full set of timing inputs into the shared cost helper. The create path called the same helper and left two of them out:

// create path — note what is missing
const floors = await this.cost.computeFloors({
  categories, resourceIds, operatorRate, unitDuration,
  // gapMinutes and bufferMinutes never passed
});

Those two arguments are optional and default to zero. No type error, no test failure — just a cheaper cost model, a lower floor, and a guard that let through prices the system could not honour. Meanwhile the actual billing ran off the real packed schedule, which includes the gaps and buffers a resource is held through. That was a fourth model again.

The clamp is what made it invisible

The payout pool is computed and then floored at zero:

const poolRaw = fees.minus(platformFee).minus(resourceCost).minus(operatorCost);
const pool = poolRaw.isNegative() ? Money.zero() : poolRaw;

That is a defensible product decision — nobody wants to chase a customer for a debt. But it means the guard’s own failure renders as a healthy-looking pool: 0. On one test run the operator collected 14,400 and paid out 24,990. The interface reported net: 0, pool: 0. Nothing anywhere said “short by 10,590”.

A guard that fails silently is worse than no guard, because it buys the confidence of one without the protection.

The symptom that finally gave it away

The tell was not the money. It was this: a record created at a price the create path accepted could not then be edited at all.

PATCH /items/:id  { unitPrice: 600 }
400  Price is below the minimum needed - minimum 638.89

Same value. Same record. Rejected. The edit path used the stricter formula, so re-saving an unchanged field failed. Any inconsistency between a write guard and an edit guard shows up exactly like this — as a record that is legal to create and impossible to modify.

What I would do differently

  • One computation, one call site, no optional cost inputs. Every argument that changes the answer should be required. Optional-with-a-default is how two callers silently disagree.
  • The enforced number and the displayed number must come from the same call, ideally the same request. If the UI has to show a floor, it should show the one the server just used.
  • Never clamp without recording the raw value. Clamp for display if you must, but keep the signed number somewhere an alert can see. max(0, x) is a decision to discard evidence.
  • Test the guard at its boundary, not in the middle. Everything passed at comfortable values. The divergence only appears when you bisect for the exact rejection point — which is also the only way to learn what the guard actually believes.

The broader shape: whenever the same business rule is evaluated at display time, write time and settlement time, treat those three as one thing that must be provably identical. They will drift, and the drift will point in whichever direction is least visible.