Somebody emailed me in February to say Smopper had told them a 12-pack of soda cost six cents, and could I please explain myself.
They were right. It did. I want to walk through why, because it is the single best illustration I have of the thing that actually determines whether a price comparison is worth anything, and it is not the comparing.
The unglamorous part is the whole job
Everyone assumes the hard part of a price comparison site is getting the prices. It is not. Prices are a data problem with a data solution, and it is tedious and it is tractable.
The hard part is answering two questions that sound trivial and are not:
- Are these two products the same thing?
- How much of it is there?
Get either one wrong and every number downstream is confidently, precisely wrong, which is much worse than being vague. A comparison that says "we are not sure" is annoying. A comparison that says "$0.06" is actively harmful, because people believe specific numbers.
Question two, and the soda
Start with size, because that is where the six-cent soda came from.
Products arrive from different sources describing their size in wildly different ways. A store might tell us 14.5 oz, or 2 x 12 oz, or 1 lb 2 oz, or nothing at all and just a price per pound. Eggs show up as 18 ct from one place and 36 oz / 18 ct from another. None of that lines up.
The original version of our compare code did the obvious thing: divide price by size, sort ascending, show the winner. Which works fine until the list contains items measured in different dimensions.
A 12-pack of soda is 12 cans at 12 fluid ounces, so 144 fluid ounces total. Divide a $8.99 price by 144 and you get about six cents. Meanwhile a single bottle priced per item shows up as its whole price. Sort those two raw numbers against each other and the 12-pack wins forever, by an enormous margin, because we were comparing dollars per fluid ounce against dollars per bottle and pretending they were the same kind of number.
The fix is boring and it is the most important code on the site. Everything reduces to a canonical base unit within a dimension, and we only ever compare within one dimension:
- Anything by volume becomes dollars per fluid ounce
- Anything by weight becomes dollars per ounce
- Anything by count becomes dollars per item
Ounces rather than milliliters because the numbers read naturally to somebody standing in an American grocery store, and the choice of base does not affect the ranking at all. It only affects whether the printed number means anything to you.
S M O P P E R
SORTING TWO KINDS OF NUMBER
BEFORE THE FIX
$/fl oz compared against $/bottle. Two different kinds of number.
AFTER THE FIX
Both reduced to one base unit inside one dimension before anything is ranked.
The 12-pack really is cheaper. It was winning for the wrong reason.
The eggs exception
There is one deliberate override and eggs are the reason for it.
Eggs can be described by count or by net weight, and the two are technically convertible, and converting them produces garbage. Nobody has ever compared eggs per ounce. You compare them per egg. So for a small set of categories we force the count dimension regardless of what the source data says, because the correct basis is a fact about how people shop and not a fact about the package.
That override exists because being technically consistent and practically useless is a real failure mode, and it is one that a purely automated system falls into constantly.
How bad is the size problem, in numbers
Here is the one statistic in this post, and I think it is the most persuasive thing I can show you about why this layer takes as much work as it does.
I pulled every butter product we hold a price for and counted the distinct units of measurement it arrived in. Not brands, not sizes. Units.
Eleven. Ounces, pounds, fluid ounces, millilitres, litres, quarts, cups, count, packs, "each", and "unit".
Some of that is legitimate. A spreadable tub really is a volume and a stick really is a weight. A good deal of it is not. It is the same physical stick of butter described one way by one source and another way by another, and every one of those has to be reconciled before the word "cheapest" means anything at all.
Butter is not unusual. It is just the one I counted.
Question one, which is much worse
Sizes are at least arithmetic. Deciding whether two products are the same thing is a judgment call that has to be made a few hundred thousand times without a human present.
What we do is build an identity for every product out of what we can parse from its name and category, and then score how well two identities line up. The scoring cares about things like:
- The core noun. "Butter" and "peanut butter" share a word and are not related. This is the trap that catches every naive text-similarity approach.
- Qualifiers that change the product. Salted versus unsalted. 80/20 versus 93/7. Whole versus 2%. Decaf. These are not adjectives, they are different products.
- Brand, in the categories where brand is the product. Some categories are brand-strict and some are flexible. Flour is flexible. Coffee is not, and neither is anything where a person's whole reason for buying it is the specific recipe.
- Category compatibility. Chocolate chips and chocolate bars overlap heavily by name and are not substitutes.
Below a confidence threshold, we do not show the comparison. That decision is why some categories on Smopper look thinner than they should, and I get asked about it more than anything else.
Where it still gets it wrong
Four failure modes I know about, in rough order of how much they bother me.
Counter goods have no identity at all
A deli counter or a butcher counter produces a product with no UPC and no feed entry. When I ran the three Pittsburgh chains against each other, deli turkey came out looking like a landslide win for one store, and it was not a win, it was a comparison between a sliced-to-order product and a sealed package. Those are different goods. We flag it now instead of scoring it. Small grocers get penalized hardest by this, since counter service is disproportionately what they are good at.
"Family size" and other units of marketing
Mega roll. Family size. Party size. Value pack. None of these are measurements and all of them appear where a measurement should be. When a package tells us the actual square footage or net weight we are fine. When it does not, we would rather drop the item than invent a number for it.
Drained weight
Every canned good is priced on net weight, which includes the liquid. Two brands of chickpeas in the same size can genuinely contain different amounts of chickpeas. We are comparing the cans correctly and the cans are not the thing you want compared. I do not have a good answer for this one. Drained weight is on the label and is not in any feed we receive.
Promotional pricing that is conditional
"Buy 2, get 1" and threshold offers are not a price, they are a function. Our data model wants a number. We handle the simple multi-buys and we do not model account-specific digital coupons at all, which, as I said when working through the turkey promotion, systematically favors the stores with less promotional machinery.
A few bad rows can sink a whole category
This is the failure mode I have come to respect most, because it is the one that does not announce itself.
When I was pulling numbers for the store comparison, Aldi's eggs came out at roughly eleven dollars a dozen. Obviously wrong. The cause was six observations, some of which had a pack size misread: a flat of eggs read as a single carton, or something like it. Six bad rows out of a database of nearly twenty-eight thousand prices, and they were enough to make an entire category report the opposite of the truth.
Bananas did the same thing in the other direction at a different store, where something banana-adjacent priced by the bunch is being read as a price per pound.
Neither of those is a hard bug. They are ordinary parsing noise, and at the scale of the whole database they are a rounding error. The problem is that they do not distribute evenly. They concentrate in thin categories, which are exactly the categories where you have the fewest other observations to drown them out. A category with six data points and one bad row is thirty per cent wrong.
Which is why almost everything we publish is a median rather than an average, why the outer tenth gets trimmed before the median is taken, and why a category with fewer than a handful of clean observations per store gets dropped rather than reported. None of that makes the bad rows go away. It stops them from being load-bearing.
Why I am telling you this
Partly because the email deserved an answer longer than "fixed, thanks."
Mostly because I think you should be suspicious of any price comparison that will not tell you how it decides two things are comparable. That decision is where all the judgment lives. Anybody can scrape a price. The question is what they did with a package labeled 2 x 12 oz, and whether they told you when they were not sure.
There is a version of this site that is much more impressive-looking. It shows a number for every product in every category, it never says "not enough data", and every comparison resolves cleanly. Building it would take about a week, because all you have to do is stop checking.
Our rule is that a gap beats a guess. It makes us look less complete than a competitor willing to show you a number for everything, and the categories where we look thinnest are frequently the ones where the honest answer is genuinely hard. Counter meat. Anything sold loose. Anything where "family size" is doing the work a measurement should be doing.
I have made my peace with that trade. If you ever see a number on Smopper you think is wrong, tell me. The six-cent soda got fixed because somebody did, and I would much rather get an annoyed email than have you quietly stop trusting the thing.


