Back to Feed
article 3m read

Spare case slices kept going to the same stores in the order planner

Ties in my case-splitting planner fell through to store ID, so the same stores won every leftover slice on every product. My first fix only fooled its own test; hashing the product with the store keeps splits replayable while spreading ties.

Article DepthPractice

Flow

Two old tie rules, the new hash, and the test

  1. 1Store ID order

    Same answer every time, but one ranking for all products, so the same stores win every leftover.

  2. 2Row key first

    Looks fair when test keys rotate; falls back to store ID order when keys start with the store.

  3. 3Product and store hash

    Same answer for one product, a new order for the next, no matter how rows are fed in.

  4. 4Reverse and count

    Feed rows in reversed, check totals add up, and confirm leftovers reach several stores.

Favoring one store was something the ordering system did, and nobody had told it to. The cause was a sort that broke ties by store ID.

How the split works

The stores share supplier cases. The planner adds up what each store wants for one product, rounds up to whole cases, and then hands each case back out in small legal pieces, like a quarter of a case. The first extra piece goes to one store set up to take the extra. Each piece after that goes to the store that is furthest short.

Lots of code calls this same planner, including the checks that rerun it later to confirm a saved cart is right. So it has to give the same answer every time.

Where the bias came from

Ties probably happen a lot, with many stores wanting the same small amount. When they tie, the planner needs a rule to pick one. Mine used the store ID. That gives the same answer every time, which is good. But it's the same order for every product, so the same stores won every tie and the rest never did. I never measured how much that skewed real orders.

The test that fooled me

My first change sorted by the row key before the store ID. The test passed, but only because the test built row keys that rotated. In the real exact-product path the row key starts with the store ID, so nothing changed. I missed that the test was doing the work.

Hashing the product with the store

Now each store gets a tie key made by hashing the product together with the store ID. With made-up names:

const fingerprint = (sku: string, shopId: string) => hashHex(`${sku}|${shopId}`)

For one product the order never changes, so reruns agree. For the next product it's a new order, so a store that lost one tie can win the next. The same order is used when pieces get taken back.

The product is now a required input, and a blank one throws an error. If one caller left it out, or used a different value, its rerun would come out different from the plan and a good cart would get rejected. So I updated every caller at once.

Reversing the rows and counting winners

The new test runs many fake products. It feeds each one in forward and reversed and expects the same totals. It checks that units, cases and value add up, that every share is a whole piece, that no store gets every leftover, and that more than one store gets some. It doesn't ask for perfectly even counts. A hash won't give you that.

So the test answers one question: can a single store grab every leftover? No. And because every caller passes the same product, a rerun lands on the same split as the plan.

Keep reading

Pick up a connected idea or branch into a nearby one.

5 paths forward