The most common way a merchandising rule breaks is also the least visible: it keeps running, produces no error, and just stops doing what it was supposed to do. A pin that referenced a document ID silently pins nothing once that ID is gone. A boost written against a brand string that got re-cased in the next feed sync silently stops matching. Nobody notices until someone asks "why isn't the featured product showing anymore?"
Pin by filter, not just by ID
A pin locked to id:sku-4471 is exact and predictable — until sku-4471 is discontinued and re-issued under a new ID next season. If the intent was "always feature our best margin item in this category," the rule should say that:
Now the rule survives a catalogue refresh: whichever SKU your product team flags featured:true this season gets the slot, with zero changes to the search-rules config. Reserve ID-based pins for genuinely one-off placements — a specific promotional SKU you know will exist for the life of the rule — and use filter-based pins for anything tied to a business concept ("our top margin item," "the newest arrival") rather than a specific row.
Boosts on stable fields, not derived ones
A boost expression compiles to the engine's own ranking syntax (_eval(...):desc), so it's only as stable as the field it references. Fields that come straight from your source system — brand, category, in_stock — tend to be stable. Fields computed by an import script — a derived "popularity tier," a hand-maintained "trending" flag nobody remembers to update — tend to silently go stale. If a boost depends on a computed field, put a note on who owns keeping it current, or better, replace it with the platform's own trending/recommendation model, which recomputes itself from real behavioural signal rather than a spreadsheet someone forgot to touch.
Exclusions need an expiry story
stock_status:!=discontinued is a good exclusion — it's driven by the same field your inventory system already updates. promotion_id:!=summer-2025 is a worse one: it works today, and next summer someone has to remember it exists, remember what it excludes, and remember to remove it. AACSearch's scheduled campaigns exist specifically so a time-bound rule expires on its own — set starts_at/ends_at on the campaign instead of hand-writing a date into a filter expression that nobody will think to revisit.
Lint before you ship a rule
A rule that pins a document and also excludes it, two pins fighting for the same position, or a boost with contradictory signs on the same filter — these are easy mistakes to make once a search-rules configuration grows past a handful of entries. The console runs a lint pass over a rule before saving it and will flag conflicts like these, along with schedules set in the past. Reading the warning is cheaper than debugging "why is this product missing" after the fact.
The pattern
Every durable merchandising rule answers "what happens when the catalogue changes under this rule?" before it ships. If the answer is "the rule quietly stops working and nobody notices," write it against a different field, or set an expiry, before it goes live.
Похожие посты
Faceted search that scales to millions of SKUs
Why facet counts get slow as a catalogue grows, the difference between an exact and an estimated count, and how to keep faceted navigation fast without hiding filters shoppers need.
Webhook delivery: signatures, retries and safe replays
How AACSearch signs outbound webhooks, what happens when your endpoint is briefly down, and how an Idempotency-Key keeps a retried write from landing twice.
Multi-tenant search architecture: how tenant isolation works in AACSearch
Collection namespacing on the engine proxy, full versus search key scopes, short-lived browser keys, per-organization quotas in a Durable Object and organization-scoped events — what a platform operator gets when embedding search for many customers.