Discovery Platform Architecture and Delivery Design
1. Purpose
This document turnssearch-prd.txt into an implementation design for an owned discovery platform. It covers the recommended technology stack, system boundaries, high-level and low-level designs, data contracts, feature delivery sequence, safety controls, operations, and decision criteria for evolving the search engine.
It extends the existing platform instead of creating a parallel discovery stack: Compass remains the customer-facing discovery service; Atlas remains the catalogue edge; Sesh provides anonymous/session context; Tetris composes CMS-backed content and widgets; Medusa, Storehouse, and Dermadesk remain sources for commerce, availability, and prescription/consultation context.
The complete architecture remains in this document. Discovery platform low-level design is an additive companion focused on executable query parsing, TypeSense request construction, ranking execution, event routing, and use-case examples.
The first production goal is deliberately narrow:
A user can search for a product, brand, concern, or ingredient; receive safe and relevant results; have the interaction measured; and have a merchandiser correct a bad outcome without an engineering deployment.The platform is not a search engine replacement alone. It is a product layer around candidate retrieval that owns catalogue quality, safety eligibility, ranking policy, merchandising, attribution, experimentation, and operational visibility.
2. Architecture Decisions
3. Why This Stack
3.1 Typesense for the first production version
Typesense matches the Phase 1 requirements with low implementation and operational burden:- Fielded full-text search with adjustable text-match weights.
- Typo tolerance, prefix matching, infix matching where appropriate, and search-as-you-type.
- Facets, numeric filters, Boolean filters, sorting, and filter counts.
- Grouping by canonical product ID to prevent variant duplicates.
- Synonym support and curated result overrides where they are useful.
- Fast response times without designing shard topology, analyzers, or a complex query DSL from day one.
- Native analytics rules for popular/no-hit queries, document counters, and pseudonymous behavioral logs.
- One self-contained server binary with a clear Kubernetes deployment model.
3.2 Why not Meilisearch by default
Meilisearch is a strong choice for simple product search. It can be the right option for a lightweight MVP if there is no near-term need for detailed ranking governance or complex discovery controls. This PRD, however, requires a path toward:- Query-, page-, and context-specific merchandising rules.
- Explicit rank-layer precedence and per-result explanations.
- Product/variant family behavior.
- Rich per-page filter and sorting configuration.
- Controlled behavioral ranking and experiments.
- Hybrid lexical and semantic candidate retrieval.
3.3 Why not OpenSearch initially
OpenSearch is mature and highly capable. It becomes attractive when the business has proven needs for complex custom scoring, advanced analyzers, nested variant documents, very high throughput, or a mature search operations team. It should not be the default starting point because it brings forward substantial work:- Index mappings and analyzer strategy are expensive to change and require safe reindexing.
- Production reliability requires shard, replica, snapshot, storage, scaling, and upgrade decisions.
- Query DSL, scoring, relevance tuning, and performance diagnostics require specialist expertise.
- A resilient managed deployment has a materially higher baseline cost than a small managed Typesense deployment.
3.4 When to move to OpenSearch
Run an OpenSearch evaluation after Phase 2 or Phase 3 only when one or more evidence-based triggers is met:- Typesense cannot express an important retrieval or ranking requirement without fragile workarounds.
- The catalogue or query volume makes Typesense materially less cost-effective.
- Strong hybrid retrieval or vector-search quality is a validated requirement.
- Explainability requires score details unavailable from the current engine.
- The team has committed operational ownership and has people experienced with OpenSearch.
4. Guiding Principles
- Safety and eligibility are server-side invariants. A client, rule, experiment, pin, model, or future sponsored placement cannot reintroduce an ineligible item.
- The catalogue is the quality bottleneck. Attribute coverage and freshness are features, not data-cleanup work outside the platform.
- Rules are data, not deployments. Routine merchandising changes are versioned configuration with previews and audit records.
- Every result is attributable. The platform records the request, configuration version, returned products, positions, and downstream actions.
- Search is lexical first. Exact names, brands, and product families must be dependable before semantic matching is introduced.
- Do not create a service per PRD module. Begin with a cohesive Discovery API and a small set of worker processes. Split only at proven scaling or ownership boundaries.
- Configuration needs a rollback story. All rule and profile changes must be previewable, schedulable, audited, and reversible.
- Defaults must work without personalization. Personalization only reorders already eligible, relevant products and can be disabled per surface.
- Production launches are measured and staged. Shadow mode, canaries, regression tests, and health signals are mandatory for material changes.
5. High-Level Design
5.1 Runtime request path
6. Service Boundaries
Begin with four deployable applications. Their code may live in one monorepo.
Do not make ranking, synonym management, personalization, recommendations, or experiments independent services until they have independent load, release cadence, data storage, or ownership requirements.
7. Infrastructure Design
7.1 AWS resources
7.2 Environments
Maintain at leastdevelopment, staging, and production.
- Development: synthetic catalogue subset, seed configuration, no production personal data.
- Staging: production-like schemas and anonymized/sampled catalogue; validates migrations, index builds, and golden-query tests.
- Production: separate database, cache, queues, engine collections, credentials, and dashboards.
7.3 Availability and degraded operation
Thediscovery-api must return a usable response when a downstream dependency is degraded:
No degraded response may violate Rx, listing, or price/availability safety requirements. Cached responses should be limited to contexts where eligibility remains valid.
8. Data Model
7.4 Self-hosted Typesense operations
Production runs a three-node Typesense cluster. Typesense uses Raft consensus and replicates the full dataset to every node; three nodes tolerate one node failure. The Raft peering network remains private, while Compass connects through the internal load-balanced Typesense service and configures individual nodes as fallbacks. Each node requires persistent SSD/NVMe-backed volumes for both the Typesense data directory and analytics directory. The cluster configuration enables native analytics explicitly:- Use a predefined schema and leave display-only fields unindexed to reduce memory use.
- Plan keyword-search RAM at roughly 2-3 times the combined size of indexed/searchable/filterable/sortable values, then validate with the real catalogue.
- Use collection aliases and versioned collections for incompatible schema changes.
- Snapshot through Typesense’s snapshot API and copy the resulting snapshot to S3. Never copy a live data directory directly.
- Monitor
/health,/metrics.json, and/stats.json; alert before RAM reaches 85% or sustained CPU reaches 90%. - Use bulk imports for indexer batches. Treat
503 Not Ready/Laggingas backpressure, retry with jitter, and keep concurrent bulk imports at or belowvCPU count - 2. - Keep analytics collections, analytics directories, API keys, and snapshots isolated per environment.
- Set
enable_analytics=falseon tests and vendor-shadow queries so they do not contaminate production query counts/suggestions.
8.1 Discovery product document
Index at the canonical product-family level, not one document per SKU. This satisfies the PRD requirement to avoid near-duplicate variants while preserving variant choices for the PDP/cart.listable,purchasable,inStock, andrxClassificationare distinct values.- Do not infer search facets from ungoverned descriptions at query time.
- Attribute IDs are stable canonical IDs; display names can change without breaking rules or URLs.
- Variants are included to select a sensible display option, but the top-level product remains the search/browse unit.
- Product family, pack, and flavour policies must be explicit. The current open decision recommends grouping size variants but not distinct flavours.
8.2 PostgreSQL control-plane entities
JSONB for flexible condition/action definitions, but validate every entity through typed application schemas. Do not rely on arbitrary JSON interpreted at runtime without versioning and validation.
8.3 Bootstrap state and seeded configuration
Database migrations create empty tables, but the platform must not launch with an empty control plane. Bootstrap data is a reviewed, versioned configuration bundle imported after schema migration and before Typesense traffic is enabled.
Seed imports must be idempotent, include a source/version and an owner, produce an audit record, and support dry-run validation. Treat seed data as configuration under change control, not application code and not manually edited production SQL.
8.4 Merchandising rule format
- Redirect query to page/destination.
- Pin a product at a deterministic position.
- Hide a product from a scoped context.
- Boost or bury an eligible product.
- Add or disable a synonym/protected term.
8.5 Phase 1 event model and destinations
The shared event contract is versioned and common across web/mobile/server producers, but Phase 1 does not persist every event into Compass PostgreSQL. Each event is routed to the system that needs it:
The portable envelope below is the internal contract from which PostHog and Typesense-specific payloads are constructed:
Destination routing rules:
- Search: Compass emits
search_executedto PostHog and keeps its result/position/rule explain evidence. TypeSense auto-captures the actual engine search for popular/no-hit analytics; shadow/test requests useenable_analytics=false. - Client interaction: web/mobile sends click, suggestion, filter, sort, and visible-impression events to the first-party endpoint. It validates the signed discovery context, forwards the normalized event to PostHog, and forwards only verified click/visit events needed by TypeSense rules.
- Cart/order: Vector/Medusa persists discovery attribution on the line item and emits server-side PostHog events. A worker may forward verified TypeSense
conversionevents for bounded counters only after cart/order validation. - Operational evidence: Compass stores short-lived explain/provider-comparison records, not raw clickstream, in PostgreSQL.
9. Discovery API
9.1 External endpoints
9.2 Search request
9.3 Search response
searchId, with access controlled for operations and support.
10A. End-to-End Discovery Flows
Every discovery request uses the same boundary: Compass interprets and governs the request; TypeSense performs primary candidate retrieval, filters, grouping, facets, and ranking; Compass performs final defensive safety/external eligibility checks, response shaping, and attribution.A.1 Exact product and product-family search
Examples:Acne UV Gel, Cetaphil Gentle Cleanser, Minimalist 10% Niacinamide.
A.2 Brand search
Examples:Cetaphil, Minimalist, Sebamed.
A.3 Category, ingredient, and concern search
Examples:face wash, sunscreen, niacinamide, retinol serum, pigmentation, hair fall, dark spots.
retinol serum resolves to the canonical retinol ingredient plus serum product type before TypeSense retrieves candidates. This is more reliable than text matching raw descriptions alone.
A.4 Hinglish and mixed-language search
Examples:muhase ki cream, balo ka jhadna, jhaiyan, oily skin ke liye sunscreen.
Hinglish starts as a governed Compass alias dictionary, not as a vector-only feature:
A.5 Typo-tolerant and attribute-rich search
Examples:sunscreem spf 50, niacinemad, spf 50 sunscreen for oily skin under 700, 10% niacinamide serum.
A.6 Autosuggest
Examples: typed prefixes such assun, acne u, and naci.
popular_queries supplies candidates only. Compass validates quality and safety before a suggestion reaches users.
A.7 Browse and PLP flow
Examples: category, brand, concern, ingredient, and campaign pages.A.8 Semantic and hybrid search
Examples:cream for dark spots, something for oily acne-prone skin, moisturizer that does not feel sticky.
Semantic retrieval is introduced only after lexical search, controlled Hinglish aliases, relevance fixtures, and baseline metrics are dependable.
A.9 Redirect, medical, and fallback flow
A.10 Recommendation retrieval flow
Recommendations are not text search, but use the same ownership boundary:A.11 Unified decision tree
A.12 Responsibility summary
10. Search Low-Level Design
10.1 Query understanding pipeline
vit cmaps to the approvedvitamin calias.SPF 50extracts anspf >= 50candidate filter/preference.under 500extractsprice.maximum = 500.for oily skinextracts an approved skin-type signal.- Protected terms are identified before typo correction and never auto-rewritten into generic words.
Log confidence and evidence. Ambiguous queries may retrieve multiple intent routes; do not force false certainty.
10.2 Candidate retrieval
Typesense collection fields should include separate searchable fields and facet fields. A representative collection configuration is:- Full-text fields: title, brand, categories, concerns, ingredients, product type, description.
- Facet/filter fields: stock, listability, pricing, formats, SPF, safety, brands, categories.
- Sort fields: price, rating, newness, approved popularity scores.
- Stable identity fields: product family ID, source version, canonical taxonomy IDs.
10.3 Eligibility policy
Eligibility runs before final ranking and must be used by search, browse, recommendations, and future sponsorship.10.4 Ranking pipeline
The exact layer order is a platform invariant:RankingProfile, evaluate them against golden queries, and validate material changes through interleaving or A/B testing.
Pins are applied after organic scoring. A pin must be validated before publishing:
- Product exists and is currently eligible for the target scope.
- Product does not conflict with an existing pin at the same position.
- Safety restrictions are never overridden.
- Expiry date and audit reason are present where required.
10.5 Fallback ladder
For zero or materially poor results:- Run the protected-term-safe typo correction query.
- Run a relaxed query plan by dropping low-confidence modifiers.
- Run a hybrid/semantic candidate source only when introduced.
- Route to the nearest relevant category/concern page with an honest message.
- Return an explicit zero-result response plus supported alternative actions.
10.6 Hinglish and multilingual support
Start with a governed alias dictionary sourced from real query traffic. Examples include mapped concern terms such asmuhase, balo ka jhadna, and jhaiyan. Store language/locale and confidence on aliases. Do not add broad transliteration or LLM rewriting until query-health data shows the coverage need and quality review capacity exists.
11. Autosuggest Design
Autosuggest reuses query normalization, protected terms, candidate filtering, and safety policy from search. It must not be less tolerant than the full search endpoint.11.1 Suggest endpoint behavior
For each prefix, return grouped suggestions:suggestionId, type, rank, source, and destination so impressions and taps are attributable.
11.2 Suggestion quality controls
- Reject candidates that lead to no/poor results based on an offline quality job.
- Deduplicate near-identical strings.
- Preserve product family and brand intent when confidence is high.
- Exclude Rx items from promotion-like suggestion positions; allow direct matching where policy permits.
- Apply merchandising pins/blocks/curated suggestions only after safety and quality checks.
- Before typing, surface privacy-safe recent searches, approved trending queries, and configured seasonal shortcuts.
12. Browse and PLP Design
Browse pages are query templates over the same product index, not separate product databases.12.1 Page definition
12.2 Filters and sort
FilterConfiguration is data associated with page type/page/category. It controls:
- Visible facets and their display order.
- Pinned facet values and quick filters.
- Search-inside-filter support for high-cardinality fields such as brands.
- Sort options and default ranking profile.
- Maximum allowed selections and zero-result recovery behavior.
12.3 SEO requirements
- Use a stable page ID and stable canonical URL independent of display-name changes.
- Store redirects when products, brands, or pages retire.
- Define canonical/index/noindex policy for filter combinations before releasing filtered pages to crawlers.
- Capture organic traffic and indexability metrics before and after migration.
- Do not change URL structures as an incidental side effect of changing the search provider.
13. Recommendations Design
Recommendations start in Phase 3 because they depend on reliable catalogue relationships and attribution. They do not require a separate model-serving platform initially.13.1 Shared widget pipeline
13.2 Candidate sources in delivery order
- Curated lists and catalogue relationships.
- Product similarity based on controlled attributes.
- Co-purchase signals after event/order attribution is validated.
- Bounded model output where data coverage justifies it.
13.3 Routine intelligence
Create versioned relationship/rule tables:- Anchor product is excluded.
- Out-of-stock and Rx products are excluded where required.
- Same-step/duplicate active-ingredient suggestions are excluded for routine widgets.
- Known ingredient conflicts and declared cautions are respected.
- Category mismatch constraints are enforced per widget.
- Short, high-quality lists are preferable to padded weak lists.
13.4 Recommendation quality gate
Maintain reviewed fixture sets with approved and prohibited recommendations per anchor product. Every widget strategy change runs against them before rollout. Online click/revenue metrics complement, but do not replace, this review.14. Merchandising Control Plane
14.1 Initial operator workflows
The initial console should focus on the highest-value, lowest-risk capabilities:- Search query inspection.
- Protected-term and synonym management.
- Query redirects.
- Search pins, hides, boosts, and buries.
- Ranking-profile assignment.
- Preview and publish/revert.
- Rule audit history.
14.2 Publishing workflow
14.3 Configuration caching
- Store authoritative configurations in PostgreSQL.
- Publish an immutable configuration version to Redis/cache on activation.
- Discovery API reads the active version once per request or with a short TTL.
- Attach
config_versionto every response/event. - Cache invalidation must be explicit and observable. A rule that saves but does not take effect is a production incident.
14.4 Compass source of truth and Typesense compilation
The merchandising panel writes only to Compass admin APIs and Compass PostgreSQL. It does not treat Typesense as the source of truth. Compass owns rule definition, scheduling, audit history, approval, preview, rollback, and conflict validation; Typesense is an optimized execution target for rules it can apply natively.synced state is necessary for a native override to execute, but Compass remains the emergency authority: disabling a rule stops Compass from applying request-time behavior immediately and queues removal of the Typesense resource.
Do not persist a campaign boost as a permanent product popularity/document value. It becomes stale, is hard to reverse, and can leak beyond its intended scope. Pins/hides map cleanly to native TypeSense controls; boosts/buries are query-context policy and must remain explainable in Compass.
14.5 Rule publication, synchronization, and runtime behavior
Publishing a rule follows this sequence:15. Ranking Governance and Explainability
15.1 Ranking profile example
15.2 Explain record
For everysearchId, persist a short-lived structured record:
16. Catalogue Ingestion and Quality
16.1 Delta ingestion
16.2 Freshness objectives
16.3 Quality gates
Define coverage requirements by category. Example:16.4 Assisted enrichment
Enrichment proposals can be generated from names, descriptions, and ingredient lists, but they remainproposed until a catalogue operator approves them. Record source, confidence, reviewer, and timestamp. Never silently alter controlled attributes.
17. Analytics, Reporting, and Experiments
17.1 Phase 1 analytics architecture
Phase 1 does not require a new data warehouse. Use Typesense’s native analytics for search-engine feedback, PostHog for product and revenue analytics, Compass PostgreSQL for operational/search explainability, and Vector/Medusa/Orderbox for authoritative commerce facts.17.2 Typesense native analytics
Enable Typesense analytics only on the self-hosted production/search cluster and configure four rule families:
Typesense analytics supports
search, click, conversion, and visit events. Each Compass request uses analytics_tag to distinguish the surface/platform, for example search_results:web, search_results:android, or concern_plp:web. Send a pseudonymous X-TYPESENSE-USER-ID derived from approved Sesh/Hodor identity policy rather than a raw customer identifier.
Use filter_by and analytics_tag metadata only for lightweight search segmentation. Keep ranking profile/version, rule IDs, experiment assignment, fallback outcome, exact positions, and safety decisions in Compass because Typesense does not model the full discovery policy.
Counter events are forwarded only after first-party token validation or authoritative cart/order handling. A suggested initial weighting is click +1, add-to-cart +3, purchase +10, but the resulting value is only a bounded tie-breaker within eligible/relevant results. It must not override relevance, safety, pins, or merchandise rules. Position bias, fraud, stale stock, and popularity feedback loops require evidence thresholds, rate caps, decay, holdout evaluation, and a kill switch before behavioral ranking is enabled.
17.3 PostHog analytics and attribution
PostHog is the Phase 1 cross-surface analytics layer. It receives validated client interaction events and server-side commerce events, and answers:- Search-to-PDP, search-to-cart, and search-to-order funnels.
- Revenue by query, intent, platform, suggestion type, ranking profile, rule/campaign, fallback path, and experiment variant.
- Cross-session conversion after Sesh/Hodor identity linking, under the approved privacy policy.
- Comparison of search, PLP, widget, and direct PDP journeys.
- Filter/sort/suggestion behavior, cohorts, feature flags, and experiments.
order_completed is emitted server-side by Vector, Medusa, or Orderbox and includes line-item revenue plus the discovery attribution persisted at cart-add time. This makes PostHog suitable for Phase 1 search-to-order revenue dashboards; browser-reported purchases are never authoritative.
17.4 Future warehouse plan
Introduce a data warehouse only after a concrete need exceeds PostHog, Typesense, Compass, and commerce reporting. Triggers include:- Finance-grade reconciliation of revenue, refunds, taxes, and order lines directly against commerce data.
- Large/custom SQL joins across discovery, catalogue, inventory, CRM, marketing, and orders.
- Long-term immutable raw-event retention or lower-cost high-volume analytical storage.
- Offline training for position-debiased behavioral ranking/learning-to-rank.
- Statistical analysis or experiment methods beyond PostHog.
- PostHog query performance, retention, or cost becoming a product constraint.
fact_discovery_requestsfact_search_resultsfact_discovery_interactionsfact_discovery_attributionfact_merchandising_rule_exposurefact_widget_exposuredim_query_interpretationdim_ranking_profile_versiondim_configuration_version
17.5 Future owned analytics stack
An owned analytics stack is a future option, not a replacement for TypeSense analytics or PostHog at launch. Build it only when the triggers above are accepted and an owner is assigned. The target responsibilities are:
The owned stack remains downstream and additive during migration. Continue sending PostHog events until its dashboards and experiments have validated against the owned models. Do not make TypeSense analytics the raw event source: it is optimized for search-native aggregation and limited log retrieval, not immutable organization-wide event retention.
Required future raw-event fields include the portable envelope, destination delivery status, producer/service version, schema version, deduplication key, event and receipt timestamps, discovery IDs/positions, configuration/profile/rule versions, and persisted cart/order line attribution. Raw sensitive query/health data requires separate access controls and retention/deletion policy.
17.6 Event ownership and source of truth
Use a hybrid event model. The browser/mobile app reports interaction only visible in the interface; backend services report the discovery response and business facts they authoritatively know. This avoids trusting clients with result positions, ranking versions, cart state, or completed orders.
Analytics delivery is always asynchronous. No search, click navigation, cart, or checkout response waits for PostHog, S3, SNS, or a warehouse job.
17.7 Identity and discovery context
Each event contains the identities needed for reliable joining:
When a visitor signs in, preserve the anonymous activity and record an identity-link event. Do not rewrite historical records destructively.
Compass returns an opaque signed
discovery_context_token for every result card. The token binds the source, product, variant, position, session, issue time, and expiry. Clients pass it unchanged to click and add-to-cart flows; they never construct attribution fields themselves.
17.8 Shared web and mobile SDKs
Create a shared, versioned contract package and platform wrappers rather than scattering direct analytics calls through clients:trackSearchResultClick and trackSuggestionClick, not arbitrary event strings. Both clients use the same event names and required properties.
Web behavior:
- Send navigation-adjacent events with
navigator.sendBeacon, falling back tofetchwithkeepalive: true. - Queue and batch lower-priority interaction events.
- Flush on
visibilitychangeand page lifecycle transitions. - Debounce autosuggest requests by 150-250 ms and cancel stale requests.
- Emit viewport impressions only after a card/widget meets the agreed visibility threshold, for example 50% visible for 500 ms.
- Persist an encrypted local event queue.
- Batch on normal connectivity and flush on foreground/interval events.
- Use idempotent
event_idvalues so retries are safe. - Never delay navigation or rendering for analytics delivery.
17.9 First-party client event ingestion
Web and mobile clients submit interaction events to a first-party endpoint, initially owned by Compass or the existing analytics edge:17.10 End-to-end search-to-purchase lifecycle
17.11 Durable server event delivery
Server events use the existing SNS/SQS transport. Search events can be published asynchronously with metrics/alerts for enqueue failure. Cart and order events require stronger delivery semantics through a transactional outbox:event_id; event processing is at-least-once and idempotent.
17.12 Impression and event rules
Avoid one network request per visible card. Batch impression events and deduplicate within a bounded time window using
session_id + source_id + product_id + position.
17.13 Event envelope and schema evolution
Every event uses a versioned envelope:- Event names are immutable after production release.
- Add optional properties without changing the schema version.
- Breaking changes require a new event name or schema version.
- Validate client events at ingestion and server events before publication.
- Store both event occurrence and server-receipt timestamps; accept bounded client clock skew.
- Deduplicate by
event_id. - Redact or access-control personal health and prescription query data.
17.14 Metrics and data-quality controls
Track at least:- Zero-result rate and fallback rescue rate.
- Search to PDP click rate, add-to-cart rate, and purchase conversion.
- Reformulation and dead-end exit rates.
- Suggestion acceptance and downstream quality by suggestion type.
- PLP click-through, conversion, filter use, and empty-grid rate.
- Widget impressions, clicks, add-to-cart, revenue, fallback rate, and session-level effect.
- Rule/campaign impact compared with baseline/control where possible.
- Attribute coverage, index freshness, latency, errors, and event completeness.
- Result-click events without a valid source ID and position.
- Cart lines missing valid attribution when a discovery token was supplied.
- Purchase-event reconciliation differences against commerce orders.
- Event duplicates, invalid/expired tokens, client/server delivery lag, and queue/DLQ backlog.
- Compass search exposure counts that diverge from request logs.
- PostHog delivery failures and abnormal event-volume changes by client platform/version.
17.15 Experiment assignment
Experiment assignment should be deterministic using a stable hash of anonymous/user ID, experiment ID, and assignment version. Persist exposure with the assigned variant and configuration version. Avoid assignment changes mid-session. Every experiment has:- Hypothesis and owner.
- Surface and inclusion criteria.
- Primary metric and guardrails.
- Minimum duration/sample guidance.
- Variant configuration references.
- Stop/rollback conditions.
- Final decision and explanation.
18. Safety, Privacy, and Security
18.1 Rx handling
Rx policy belongs in the shared eligibility layer:- Rx products carry visible badges on applicable surfaces.
- They are excluded from recommendation widgets, promotional boosts, bestseller logic, discount badges, and sponsored placements.
- Exact search/category/concern discovery remains available according to regulatory policy.
- Prescription molecule searches route toward consult/prescription flows rather than promotional shelves.
- Rule validation, tests, and runtime policy all enforce this independently.
18.2 Claim-safe content
Content slot definitions include aclaimSensitive flag and review status. Concern pages must have approved copy and a designated consultation module where a product grid could overstep clinical/compliance boundaries.
18.3 Personal data
- Use pseudonymous session/device IDs for anonymous discovery behavior.
- Do not place raw personal health data in the search index or client logs.
- Protect inspector access with role-based authorization and audit queries of sensitive information.
- Sensitive searches and Rx products do not appear in shared/recent-search surfaces without an explicit privacy policy.
- Profiles are consent-governed, deletable, and isolated from third-party engine data where required.
18.4 Security controls
- Service-to-service AWS IAM roles; no static production credentials.
- Secrets in AWS Secrets Manager.
- RDS encryption, S3 encryption, TLS in transit, private subnets for internal services.
- WAF/rate limiting on public endpoints.
- Admin role model: viewer, merchandiser, approver, product admin, engineer admin.
- Audit all privileged config changes, previews, publishes, reverts, and inspector access.
19. Observability and Operational Readiness
19.1 Service level objectives
Initial targets should be validated against real UX expectations:19.2 Alerts
Alert on:- Search/suggest latency, error-rate, and timeout spikes.
- Zero-result and fallback-rate anomalies by query/category.
- Index queue age, DLQ size, index failures, and stale stock/price.
- Event gaps and duplicate-event spikes.
- Empty widget rate and serving-context integrity failures.
- Rule publication failures/cache propagation gaps.
- Safety-rule test failures and prohibited result exposure.
- Capacity headroom before planned sales events.
19.3 Test strategy
Golden cases should include direct product/brand matches, common typos, protected terms, concern/ingredient queries, zero-result recoveries, filters, variant grouping, pins/hides, and Rx exclusions.
20. Netcore-to-Typesense Migration and Rollout
Compass remains the stable client-facing facade throughout migration. Web and mobile clients continue calling the same Compass APIs; Compass normalizes provider responses, applies shared safety/attribution policy, and selects the provider internally.20.1 Provider interface and modes
Compass defines a provider-neutral internal contract for search, autosuggest, and browse. Netcore and Typesense implement that contract; provider-specific response shapes never reach clients.
Compass owns request validation, identity/session context, query/search IDs, Rx/listing/availability policy, response normalization, discovery context tokens, exposure events, and provider-comparison records. This guarantees behavior that must be consistent across engines.
20.2 Dual indexing and compatibility inventory
Keep the existing Netcore feed unchanged while a Compass index worker builds and maintains the Typesense index:
Reconcile dual indexes continuously: active/listable count, in-stock count, Rx count, category/brand/ingredient coverage, canonical product-family coverage, price/stock mismatch rate, and source-to-index lag.
20.3 Shadow comparison
Inshadow mode, Netcore serves the user while Compass invokes Typesense asynchronously with a bounded timeout. The Typesense request never delay the visible response and uses enable_analytics=false so it cannot double-count popular/no-hit query data.
Store a comparison record with:
- Raw and normalized query, filters, sort, platform, surface, and query interpretation.
- Per-provider latency, errors, result count, product-family IDs/positions, display variants, facets, redirects, and fallback state.
- Top-3/top-10/top-24 canonical product overlap.
- Exact product and brand rank for golden/known queries.
- Safety, listing, stock, price, filter/facet, and variant-duplication mismatches.
- Active configuration/ranking profile versions.
20.4 Sticky percentage rollout
Provider assignment is deterministic, server-side, and sticky. Useuser_id when authenticated, otherwise anonymous_id, with session_id as the final fallback:
0-24 receive typesense_with_netcore_fallback; remaining buckets receive Netcore. Preserve the assignment after sign-in and never switch a user between providers within a session. This protects pagination consistency, user experience, attribution, and experiment validity.
At roughly 250,000 daily visitors, 1% is around 2,500 visits per day: enough for technical detection, not enough to claim small business-metric lift. Advance by decision gates, not elapsed time alone.
20.5 Feature flags and control precedence
Use separate controls for engine rollout and individual Typesense capabilities. Do not use oneuse_typesense flag for every behavior.
Provider controls:
20.6 Backward compatibility
Maintain four compatibility guarantees during migration:- Client API: Compass endpoint paths, product-card fields, facets, sorting, pagination, and error semantics remain stable. New metadata is additive and optional.
- Identity: Both providers map to the same canonical product/family/variant/brand/category identifiers.
- Behavior: Existing Netcore rules are inventoried and reproduced or explicitly retired only after testing. Rx/safety behavior is centralized before public Typesense traffic.
- Data: Netcore and Typesense index feeds run in parallel until decommission; all Typesense documents carry source product IDs, source versions, index timestamps, listability, purchasability, stock, and price.
20.7 Decision gates, fallback, and decommission
Hard-stop events immediately route the affected cohort to Netcore:- Verified Rx or safety-rule violation.
- Invalid provider response, material price/availability breach, or index freshness breach.
- TypeSense error/timeout rate above threshold or cluster health/quorum incident.
- Attribution failure that makes the cohort unmeasurable.
- p95/p99 latency, zero-result, reformulation, search-exit, click, cart, revenue/session, and facet-empty-state regressions versus Netcore control.
- Event completeness, price/stock mismatch, index lag, or unresolved high-volume relevance differences.
21. Delivery Plan
Phase 0: Discovery foundation and engine proof of concept
Deliverables:- Catalogue completeness audit and controlled attribute dictionary.
- Canonical
DiscoveryProductcontract. - 100+ golden search/recommendation safety cases.
- Baseline of current query, conversion, zero-result, latency, and reformulation metrics.
- Typesense proof of concept against real products and queries.
- Event schema and attribution design.
- Architecture decision record and production SLOs.
- Engine meets representative relevance and latency needs.
- The team can ingest/update a representative product set.
- The catalogue gaps blocking Phase 1 are assigned with owners.
Phase 1: Search, autosuggest foundation, and safety
Build:- Discovery API, Typesense collection/indexer, and catalogue delta ingestion.
- Search result integration on one chosen surface.
- Normalization, synonyms, protected terms, typo tolerance, and deterministic intent detection.
- Eligibility, stock/listability, product-family grouping, basic facets, and ranking profiles.
- Fallback ladder, redirects, pins/hides/boosts, and a narrow admin console.
- Search/suggestion event backbone, query dashboard, inspector, alerts, regression suite, staged rollout, and shadow mode.
- Rx and claim-safety rules.
- Golden cases pass.
- Safety violations are zero.
- Event completeness and attribution targets are met.
- Search quality matches or beats the current system under shadow/interleaving evaluation.
- A merchandiser can correct a known query problem without deploying code.
Phase 2: Browse and SEO-safe PLPs
Build:- Category, brand, concern, ingredient, and campaign page definitions.
- Configurable facets, quick filters, sorting, content slots, badges, and page-level rules.
- Search-inside-filter for high-cardinality facets.
- Stable URLs, redirects, canonical/index policy, and organic-traffic monitoring.
- Catalogue quality gates and assisted enrichment workflow.
- Page/filter health dashboards and page inspector.
Phase 3: Recommendations
Build:- Widget framework, strategies, placements, and measurement.
- Catalogue relationship data, hard exclusion filters, routine conflict/complement rules, and fallback chains.
- Curated and relationship-based widgets first, co-purchase candidates second.
- Offline review fixtures and widget experiments.
- Recently viewed/continue shopping and session intent adaptation.
- Hinglish coverage expanded based on Phase 1 query data.
Phase 4: Full control plane and learning
Build:- Campaign bundles, bulk imports, approval workflows, and rule-health monitoring.
- Ranking review workflow, bounded behavioral signals, profile-aware recommendations, and discovery profiles.
- Experiment registry, automatic proposal queues, holdouts, and weekly discovery review.
- Sponsorship only after explicit commercial, legal, relevance, and safety approval.
22. Cost and Scaling Strategy
22.1 Traffic planning assumptions
At roughly 250,000 daily website visitors, discovery is a meaningful production workload but not, by itself, a reason to introduce Kafka or OpenSearch. Use measured funnel data for final capacity planning. Until it is available, size the initial benchmark and load tests using these conservative assumptions:
Peak traffic must be determined from hourly production data, not daily averages. A sale, campaign, push notification, or SEO landing-page burst can reasonably produce 10-20 times average traffic. The Phase 1 load test should use the higher of the measured peak or this planning multiplier, with separate test mixes for search, autosuggest, filters, and product-click event ingestion.
22.2 Why SNS/SQS remains sufficient at this scale
SNS/SQS is the selected event backbone for Phases 0-3. It is appropriate because catalogue indexing is an asynchronous, idempotent work queue:- Each product, price, inventory, or listing update is processed independently using
product_id + source_versionas its idempotency key. - Ordering is required only per product, not globally. The indexer rejects stale versions, so normal SQS at-least-once delivery is safe.
- Worker concurrency can scale with queue depth through the existing Kubernetes/KEDA deployment model.
- SQS provides retries, visibility timeouts, buffering, and DLQ isolation already used by the platform.
- The primary high-volume path is synchronous query serving to Typesense, not catalogue event consumption. Kafka would not improve search-request latency.
22.3 Cost priorities
At the start, engineering time and operational risk cost more than a small difference in infrastructure pricing. Self-hosted Typesense is appropriate while:- Product schemas and relevance rules are evolving frequently.
- Traffic is moderate.
- The team is focused on building discovery capabilities rather than search-cluster operations.
22.4 Scaling paths
Do not prematurely introduce Kafka, a separate Kubernetes platform, a data lakehouse, feature stores, or dedicated vector databases. Introduce them only when the existing queues, PostHog/warehouse reporting, or engine capabilities genuinely prove insufficient.
23. Open Decisions and Owners
24. Immediate Next Steps
- Confirm the Phase 1 surface: web search results is the recommended first integration.
- Run the catalogue completeness audit and define the controlled attribute dictionary.
- Collect 100-200 real, anonymized queries and build the first golden-query suite.
- Create a Typesense proof of concept using the proposed
DiscoveryProductcontract. - Define the event schema, Typesense analytics rules, and PostHog server/client ownership.
- Build the eligibility/Rx policy module and test it independently before integrating search UI.
- Establish the PostgreSQL configuration schema and a minimal query-rule preview workflow.
- Shadow the existing vendor/system before any user traffic is shifted.
25. Definition of a Successful First Release
The first release is successful when it demonstrates all of the following in production or controlled rollout:- Users can find exact products, brands, categories, concerns, and ingredients reliably.
- Common typos and approved aliases recover without damaging protected brands/molecules.
- Non-listable, non-purchasable, and prohibited Rx contexts never leak into results.
- Product variants are not duplicated in search.
- A zero-result query follows a measured, honest fallback path.
- Search clicks, add-to-carts, and purchases are attributable to the returned result list and position.
- Product/merchandising can publish, preview, inspect, and revert a query-level correction without engineering.
- The team can explain why a product appeared at its position.
- The platform can be rolled back or degraded safely if the engine or index pipeline fails.