polycratia

The Python 2 to 3 migration passed on my laptop. It passed on CI. Then it mangled part of a retailer's catalog in production.

Same container image, same dependencies, same code path. The input was the only thing that differed.

My fixtures were sample pages a previous developer had captured and committed: clean, mostly ASCII, from one retailer. The live scrapers pulled from several, and real catalog data is a mess. Mixed encodings, half-decoded entities, product names full of characters my fixtures never contained. Under Python 2, implicit bytes/str coercion had been papering over that for years. Under 3, the same records decoded into something wrong instead of raising. Downstream, the pipeline indexed garbage titles without complaint.

The fix was not the expensive part. The expensive part was that nothing failed loudly, so it surfaced as a catalog that looked subtly off rather than as an alert.

What I do differently now: fixtures come from production shape, sampled for weirdness instead of for being representative. Every record that has ever broken a parser becomes a permanent test case. Decoding happens at one explicit boundary, and anything that fails there is rejected and counted, not coerced into a best-effort string.

"Works on my machine" was not about my machine. It was about my data.

If you run parsers at scale, I'd like to hear how you get test inputs that are genuinely as ugly as production.

react

$ new-project --brief

or email hey@polycratia.com