Case study · Product data engineering
Two thirds of the catalogue carried nothing but a brand and a barcode. Faceted filters could not work. Here is what was done, what was deliberately left alone, and how correctness was established before anything reached production.
Average attributes per product: 3.9. Products with zero attributes: 1,727. Half the catalogue carried three attributes or fewer.
The consequence was not aesthetic. A shopper could not narrow by material, volume, power or size across most of the catalogue, and category pages had almost nothing structured to index. Done by hand at a conservative two minutes per product, closing that gap across 36,000 SKUs is on the order of 1,200 content-team hours — my own estimate, and the arithmetic is right there.
| Measure | Before | After |
|---|---|---|
| Attributes per SKU | 3.9 | 11.24 |
| SKUs with ≥10 attributes | 7.8% | 61.9% |
| SKUs with zero attributes | 1,727 | 0 |
| SKUs with only 1–3 attributes | 31,813 | 2,963 |
59,473 SKUs went through the API in 119 batch submissions. 7,456 bilingual attribute definitions were created, grouped into 312 base types and scoped to categories so that faceted filters actually resolve. Total project spend, including translation passes, came to under $215; tooling and infrastructure were already in place.
Production import: 36,814 of 36,817 SKUs landed cleanly. Zero errors on attribute definitions. The three failures were SKU codes that no longer existed in the catalogue.
This is the part that matters, and it is where most AI content projects quietly fail. Model output that looks plausible is not the same as output that is correct, and the difference usually surfaces only after it is in production.
22 data tests covering structure, types, uniqueness, bilingual completeness, duplicate attributes within a card, category binding and translation coverage. Staged import — 100 SKUs, then 1,000, then the full set — with verification at each step and a rollback procedure written before the first import ran.
Then an adversarial audit of my own work, before production: a read-only critical review against 25 explicit hypotheses, with a standing instruction that an audit returning “everything is fine” would itself be treated as a failed audit.
It returned 3 blockers, 8 warnings, 14 passes. The blockers were real:
Both were fixed before import. That audit is why the number above is 99.99% and not 97%.
Bilingual coverage was handled without API cost: translation of roughly 131,000 variant values ran through an offline dictionary — 150 phrases, 500 words, transliteration fallback — reaching 100% coverage at zero marginal cost, rather than paying per token for work a lookup table does.
27,412 SKUs — 42% of the catalogue — were deliberately left untouched. They already carried good human-written content. Overwriting them with model output would have been measurable activity and a net loss in quality. Enrichment was scoped to the 57% that needed it.
1,341 SKUs were skipped because they had no category assigned. Attribute extraction without a category has no target schema to map into; those products need classification first. Documented as open work, not quietly omitted.
4,884 SKUs already carried ten or more attributes and were preserved as-is, even where the model might have added more. Above a certain density, marginal attributes add noise rather than value.
The precision gate should not have been bypassed during the pivot, even under schedule pressure. The audit recovered it, but that was luck plus process, not design. On a repeat I would make the gate a hard blocker in the pipeline rather than a checkpoint a phase change can step around.
Not that a model can fill in product attributes. It can, and that is not the hard part.
The hard part is knowing which 42% of a catalogue to leave alone, catching the 16,399 cells that would have broken the import format, and being willing to say that a spot-check found a 30–50% error rate on low-confidence output before shipping it rather than after.