Nerd stuff
Genetics, encoded as a boolean.
Cilantro Safe exists because for part of humanity — the OR6A2 crowd — coriander tastes like soap. The app scans a barcode and answers one question: is it in there? The interesting bits are in how the data gets in, and in one very deliberate orange badge.
Product data comes from Open Food Facts, which normalizes ingredients into language-independent taxonomy tags. So detection is not fragile multilingual string matching — it is a query for en:coriander, en:coriander-leaf and en:coriander-seed. Two import tracks feed the database: everything worldwide that is tagged with coriander (the warning list), and popular coriander-free products — but only from nine risk categories like sauces, soups and curry pastes, because "table salt contains no cilantro" is noise, not reassurance.
The soap-gene reaction is mostly to aldehydes in the leaf; many leaf-haters tolerate the seed just fine. The data model encodes that genetics as a boolean: products tagged with seed, ground or powder coriander — but not leaf — get a distinct orange "Seeds Only" badge instead of red, plus an auto-generated note explaining why. Most allergen apps are binary; this one has a "technically yes, but you'll probably be fine" tier.
Users add products, favorite them and write reviews with a cilantro-factor; a correction-request queue and admin locks keep the data honest, and a rate limit of twenty new products per user per day keeps it human. Nice recycling touch: if a product has no photo and a reviewer attaches one, the backend promotes that review photo to product image in the same database write.
The importer recreates the barcode index as unique and then bulk-inserts with duplicate-key errors deliberately swallowed — the database schema is the dedup mechanism, so re-running the seed script is safe by construction. It also throttles itself to one request per two seconds with a named User-Agent, and splits the coriander query into three tag slices to stay under the search API's 10,000-document paging window.
The whole thing runs on free tiers, and free-tier databases auto-pause after 60 days of quiet. So a scheduled job pings the health endpoint daily — a monitoring route quietly doing double duty as cron-driven life support. It is documented in the code, because it already bit once.