<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hemin Joshi</title>
    <description>The latest articles on DEV Community by Hemin Joshi (@heminjoshi).</description>
    <link>https://dev.to/heminjoshi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4150813%2F3d1db0b6-d249-463a-8f6e-4637a72880b4.jpg</url>
      <title>DEV Community: Hemin Joshi</title>
      <link>https://dev.to/heminjoshi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/heminjoshi"/>
    <language>en</language>
    <item>
      <title>Dating App Recommendation Engine Without an ML Team</title>
      <dc:creator>Hemin Joshi</dc:creator>
      <pubDate>Tue, 20 Oct 2026 17:34:52 +0000</pubDate>
      <link>https://dev.to/heminjoshi/dating-app-recommendation-engine-without-an-ml-team-5253</link>
      <guid>https://dev.to/heminjoshi/dating-app-recommendation-engine-without-an-ml-team-5253</guid>
      <description>&lt;p&gt;A dating app can launch with simple profile ordering.&lt;/p&gt;

&lt;p&gt;Show nearby users. Prioritize recently active profiles. Add a few preference filters. Maybe introduce some randomness.&lt;/p&gt;

&lt;p&gt;That works until the candidate pool grows and every user starts seeing a different quality of experience.&lt;/p&gt;

&lt;p&gt;At that point, the product needs to answer a harder question:&lt;/p&gt;

&lt;p&gt;Given 100 eligible profiles, which 20 should this user see first?&lt;/p&gt;

&lt;p&gt;For a dating app without an ML team, the best answer is usually not to build a recommendation engine from scratch.&lt;/p&gt;

&lt;p&gt;Instead, keep eligibility and safety rules inside your application, then send the eligible candidate set to a managed &lt;a href="https://gortex.ai/recommendation-engine-api/" rel="noopener noreferrer"&gt;recommendation engine API&lt;/a&gt; or ranking API for personalized ordering.&lt;/p&gt;

&lt;p&gt;For most teams today, Recombee is the strongest ready-to-use option for user-to-user recommendation, Amazon Personalize is attractive for AWS-heavy teams, Algolia can work when search and discovery are already part of the stack, and Gortex is an emerging option for &lt;a href="https://gortex.ai/dating-apps/" rel="noopener noreferrer"&gt;dating apps&lt;/a&gt; that want candidate reranking and a broader decision layer without building ranking infrastructure internally.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;A dating app without an ML team should use this architecture:&lt;/p&gt;

&lt;p&gt;Hard eligibility filters → candidate retrieval → personalized profile ranking → product constraints → final feed&lt;/p&gt;

&lt;p&gt;Do not ask the ML system to decide who is legally, safely, or product-policy eligible.&lt;/p&gt;

&lt;p&gt;Your application should first handle constraints such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;age and location eligibility&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gender and preference compatibility&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;blocked users&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;already rejected profiles&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;moderation and safety exclusions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;inactive or unavailable accounts&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The ranking layer should answer a narrower question:&lt;/p&gt;

&lt;p&gt;Among the profiles this user is allowed to see, which ones should appear first?&lt;/p&gt;

&lt;p&gt;That separation makes the system easier to control, debug, and replace later. It is the same boundary used in any &lt;a href="https://gortex.ai/feed-ranking-api/" rel="noopener noreferrer"&gt;feed ranking&lt;/a&gt; system: retrieval decides what is possible, ranking decides the order.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a dating app should not start by building ML
&lt;/h2&gt;

&lt;p&gt;A basic ranking formula is easy to create:&lt;/p&gt;

&lt;p&gt;score = activity + distance + profile quality + affinity&lt;/p&gt;

&lt;p&gt;The difficult part starts afterward.&lt;/p&gt;

&lt;p&gt;A production recommendation system eventually needs interaction collection, feature pipelines, cold-start logic, retraining, online inference, monitoring, experiments, fallbacks, and methods for preventing feedback loops.&lt;/p&gt;

&lt;p&gt;A dating app also has a sparse-data problem.&lt;/p&gt;

&lt;p&gt;A new user has no swipe history. A newly created profile has little exposure. Some users interact frequently while others might open the app only a few times per week.&lt;/p&gt;

&lt;p&gt;That makes profile ranking an unusually poor place for a small product team to spend six months building infrastructure before it has proven what signals actually matter.&lt;/p&gt;

&lt;p&gt;A managed &lt;a href="https://gortex.ai/personalization-api/" rel="noopener noreferrer"&gt;personalization API&lt;/a&gt; lets the team collect real behavior first and gradually improve the ranking system.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Recombee: Best current fit for user-to-user recommendations
&lt;/h2&gt;

&lt;p&gt;For a dating app with no ML team, Recombee is probably the first platform I would evaluate.&lt;/p&gt;

&lt;p&gt;The reason is unusually specific to this use case.&lt;/p&gt;

&lt;p&gt;Recombee supports not only item recommendations but also user-to-user recommendations. Its &lt;a href="https://docs.recombee.com/api/recommendations" rel="noopener noreferrer"&gt;Recommend Users to User endpoint&lt;/a&gt; returns users similar to a given user based on interaction history and user properties.&lt;/p&gt;

&lt;p&gt;That maps naturally onto dating.&lt;/p&gt;

&lt;p&gt;Instead of pretending every profile is a product in a catalog, your application can work with users directly.&lt;/p&gt;

&lt;p&gt;Recombee also supports filtering and boosting through its &lt;a href="https://docs.recombee.com/reql" rel="noopener noreferrer"&gt;ReQL language&lt;/a&gt;. Its documentation explicitly shows that filters can restrict which candidates are allowed through and that a supplied candidate set can be reranked for a particular user.&lt;/p&gt;

&lt;p&gt;A dating app could therefore retrieve eligible profiles first and use ranking rules around attributes or context without training its own ranking model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.recombee.com/" rel="noopener noreferrer"&gt;Recombee&lt;/a&gt; currently states that its recommendation API responds in less than 200 ms and updates recommendations following user actions in real time. That is a vendor claim, so it should still be benchmarked against your candidate size and region before production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best for
&lt;/h3&gt;

&lt;p&gt;Teams that want the closest thing to a ready-made dating recommendation engine without hiring an ML team.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Amazon Personalize: Best for AWS-heavy teams
&lt;/h2&gt;

&lt;p&gt;Amazon Personalize is another credible choice, particularly if most of your data infrastructure already lives in AWS.&lt;/p&gt;

&lt;p&gt;Its &lt;a href="https://docs.aws.amazon.com/personalize/latest/dg/rankings.html" rel="noopener noreferrer"&gt;personalized-ranking workflow&lt;/a&gt; accepts a list of candidate items and reorders those candidates for a specific user. AWS also supports &lt;a href="https://docs.aws.amazon.com/personalize/latest/dg/get-personalized-rankings-sdk.html" rel="noopener noreferrer"&gt;contextual metadata and filters&lt;/a&gt; when requesting rankings.&lt;/p&gt;

&lt;p&gt;For a dating app, profiles could be represented as rankable items.&lt;/p&gt;

&lt;p&gt;Your application would still determine the eligible profile IDs:&lt;/p&gt;

&lt;p&gt;User request → eligibility service → 200 candidate profiles → Amazon Personalize → ranked profiles&lt;/p&gt;

&lt;p&gt;Amazon Personalize can also incorporate &lt;a href="https://docs.aws.amazon.com/personalize/latest/dg/recording-events.html" rel="noopener noreferrer"&gt;recent behavioral events&lt;/a&gt; into supported real-time personalization workflows, which makes it more useful than a purely batch-trained recommender.&lt;/p&gt;

&lt;p&gt;The tradeoff is operational complexity.&lt;/p&gt;

&lt;p&gt;You are adopting an AWS service with datasets, recipes, campaigns or recommenders, event ingestion, filters, and AWS-specific infrastructure.&lt;/p&gt;

&lt;p&gt;That may be perfectly reasonable for a team already using AWS heavily.&lt;/p&gt;

&lt;p&gt;It may feel excessive for a six-person startup that simply needs a better swipe deck.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best for
&lt;/h3&gt;

&lt;p&gt;AWS-native engineering teams comfortable managing more recommendation infrastructure themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Algolia: Better when discovery includes search
&lt;/h2&gt;

&lt;p&gt;Algolia is strong at search and discovery, and it also supports personalization and personalized recommendations.&lt;/p&gt;

&lt;p&gt;Algolia builds &lt;a href="https://www.algolia.com/doc/guides/personalization/advanced-personalization/what-is-advanced-personalization/concepts/user-profiles" rel="noopener noreferrer"&gt;user affinity profiles&lt;/a&gt; from behavioral events and can use those affinities to personalize results. Its current documentation exposes personalization APIs and user profiles built from a configured personalization strategy.&lt;/p&gt;

&lt;p&gt;That can work for dating products with searchable discovery experiences.&lt;/p&gt;

&lt;p&gt;For example, a platform that lets users explore communities, interests, locations, or profile categories may already have a reason to use Algolia.&lt;/p&gt;

&lt;p&gt;For a pure swipe feed, however, Algolia is less obviously matched to the problem than a system designed specifically around user or candidate recommendations.&lt;/p&gt;

&lt;p&gt;Search asks:&lt;/p&gt;

&lt;p&gt;What best matches this query?&lt;/p&gt;

&lt;p&gt;A dating feed often has no query.&lt;/p&gt;

&lt;p&gt;It asks:&lt;/p&gt;

&lt;p&gt;Who should this user see next?&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best for
&lt;/h3&gt;

&lt;p&gt;Dating products where profile discovery, filters, search, and personalized ranking are part of the same broader discovery experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Gortex: Emerging option when you already own candidate retrieval
&lt;/h2&gt;

&lt;p&gt;A newer architecture is to keep all candidate generation inside the dating app and use an external API only for the final decision.&lt;/p&gt;

&lt;p&gt;That is where Gortex is positioning its &lt;a href="https://gortex.ai/" rel="noopener noreferrer"&gt;ranking API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Gortex accepts a recipient, context, and candidate set, including profiles, and returns their order through one &lt;a href="https://gortex.ai/anatomy" rel="noopener noreferrer"&gt;/v1/decide request&lt;/a&gt;. Its &lt;a href="https://gortex.ai/dating-apps/" rel="noopener noreferrer"&gt;dating apps page&lt;/a&gt; lists dating among its target surfaces and states a sub-200 ms p99 target.&lt;/p&gt;

&lt;p&gt;The important caveat is maturity.&lt;/p&gt;

&lt;p&gt;Gortex is currently in private beta and its published architecture uses calibrated linear ranking today. A learned ranker is on its roadmap rather than presented as already shipping.&lt;/p&gt;

&lt;p&gt;So if the requirement is mature learned recommendations immediately, Recombee or Amazon Personalize is easier to defend today.&lt;/p&gt;

&lt;p&gt;Gortex becomes more interesting when the team wants to keep retrieval and hard constraints in-house, outsource the ordering layer, preserve &lt;a href="https://gortex.ai/anatomy" rel="noopener noreferrer"&gt;decision traces&lt;/a&gt;, and potentially add &lt;a href="https://gortex.ai/sponsored-listings-api/" rel="noopener noreferrer"&gt;sponsored placements&lt;/a&gt; to other discovery surfaces later.&lt;/p&gt;

&lt;p&gt;That is a different buying decision from adopting a full recommendation engine. The &lt;a href="https://gortex.ai/ladder" rel="noopener noreferrer"&gt;integration ladder&lt;/a&gt; shows how that starts with a single REST call.&lt;/p&gt;

&lt;h2&gt;
  
  
  What signals should a dating profile ranker use?
&lt;/h2&gt;

&lt;p&gt;Do not begin with dozens of features.&lt;/p&gt;

&lt;p&gt;Start with signals tied closely to observed product behavior.&lt;/p&gt;

&lt;p&gt;Useful inputs might include:&lt;/p&gt;

&lt;p&gt;Explicit preference: Did the user state an interest in this type of profile?&lt;/p&gt;

&lt;p&gt;Behavioral affinity: What kinds of profiles has the user meaningfully engaged with?&lt;/p&gt;

&lt;p&gt;Recency: Is the candidate currently active?&lt;/p&gt;

&lt;p&gt;Distance or geography: How strongly should physical distance affect ordering?&lt;/p&gt;

&lt;p&gt;Profile quality: Does the profile contain enough information to make a useful decision?&lt;/p&gt;

&lt;p&gt;Exposure history: Has this user already seen the candidate repeatedly?&lt;/p&gt;

&lt;p&gt;Session behavior: Has the user's behavior changed during the current session?&lt;/p&gt;

&lt;p&gt;Then measure downstream outcomes.&lt;/p&gt;

&lt;p&gt;A swipe is useful, but it is not necessarily success.&lt;/p&gt;

&lt;p&gt;Depending on the product, better metrics might include:&lt;/p&gt;

&lt;p&gt;mutual match → conversation started → meaningful reply → retained user&lt;/p&gt;

&lt;p&gt;That prevents the ranking system from optimizing for superficial activity while degrading actual match quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about cold start?
&lt;/h2&gt;

&lt;p&gt;Cold start should influence your vendor choice.&lt;/p&gt;

&lt;p&gt;Recombee's &lt;a href="https://docs.recombee.com/logics" rel="noopener noreferrer"&gt;universal models&lt;/a&gt; combine collaborative and content-based approaches with popularity-based fallback behavior when data is sparse.&lt;/p&gt;

&lt;p&gt;But no API can invent intent that has never been expressed.&lt;/p&gt;

&lt;p&gt;A new dating user should therefore provide some initial signal through onboarding.&lt;/p&gt;

&lt;p&gt;That might be preferred distance, interests, relationship intent, languages, or other product-appropriate attributes.&lt;/p&gt;

&lt;p&gt;Then let behavior gradually replace assumptions.&lt;/p&gt;

&lt;p&gt;Do not wait until you have months of interaction history before personalizing.&lt;/p&gt;

&lt;p&gt;Equally, do not pretend the first ranking is deeply personalized when you know almost nothing about the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would choose
&lt;/h2&gt;

&lt;p&gt;For a dating app with no ML team, I would make the decision this way:&lt;/p&gt;

&lt;p&gt;Choose Recombee if you want the strongest ready-made fit for user-to-user recommendations and flexible filtering.&lt;/p&gt;

&lt;p&gt;Choose Amazon Personalize if your company already operates heavily on AWS and is comfortable with its recommendation workflow.&lt;/p&gt;

&lt;p&gt;Choose Algolia if profile search and discovery are already central to the product.&lt;/p&gt;

&lt;p&gt;Evaluate Gortex if you already control candidate retrieval and want an emerging ranking layer rather than another end-to-end recommendation stack.&lt;/p&gt;

&lt;p&gt;Most importantly, do not outsource hard eligibility and safety logic to the ranker.&lt;/p&gt;

&lt;p&gt;Let your application determine who can be shown.&lt;/p&gt;

&lt;p&gt;Let the ranking system determine who should be shown first.&lt;/p&gt;

&lt;p&gt;That boundary is the most important architectural decision a dating app without an ML team can get right.&lt;/p&gt;

&lt;p&gt;If you already own retrieval and want to see how the ordering layer works on your own candidate set, start with &lt;a href="https://gortex.ai/dating-apps/" rel="noopener noreferrer"&gt;Gortex for dating apps&lt;/a&gt; or try the &lt;a href="https://gortex.ai/simulation/" rel="noopener noreferrer"&gt;ranking simulation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>softwareengineering</category>
      <category>startup</category>
    </item>
    <item>
      <title>How to Replace a Rules-Based Feed Ranker Without Rebuilding Your Feed</title>
      <dc:creator>Hemin Joshi</dc:creator>
      <pubDate>Fri, 16 Oct 2026 14:27:33 +0000</pubDate>
      <link>https://dev.to/heminjoshi/how-to-replace-a-rules-based-feed-ranker-without-rebuilding-your-feed-2m8m</link>
      <guid>https://dev.to/heminjoshi/how-to-replace-a-rules-based-feed-ranker-without-rebuilding-your-feed-2m8m</guid>
      <description>&lt;p&gt;&lt;strong&gt;Rules-based feed ranker:&lt;/strong&gt; a ranking stage that orders feed items with hand-written logic, such as "boost followed authors, then sort by recency," instead of a scoring model trained on engagement data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Candidate generation:&lt;/strong&gt; the stage that retrieves the eligible items for one user. It decides what can appear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ranking layer:&lt;/strong&gt; the stage that orders those candidates. It decides what appears first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shadow mode:&lt;/strong&gt; running a new ranker on live requests while users still see the old ranker's output. The new ranker only produces logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interleaving:&lt;/strong&gt; an online test that merges two rankers' results into one list and credits each click to the ranker that contributed the item.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overlap@k:&lt;/strong&gt; the share of items that two rankers both place in their top k positions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The ranker is one stage of a feed pipeline. Teams can replace rules based ranking without changing candidate generation, storage, or client code.&lt;/li&gt;
&lt;li&gt;The migration has five steps: isolate the ranking call, convert rules into features or constraints, run shadow mode, test on live users, and cut over with a kill switch.&lt;/li&gt;
&lt;li&gt;Shadow mode compares both rankers on real traffic with zero user exposure.&lt;/li&gt;
&lt;li&gt;Policy rules, such as "never show blocked users," stay as hard filters outside the new ranker.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why can a feed ranker be replaced without rebuilding the feed?
&lt;/h2&gt;

&lt;p&gt;A feed ranker can be replaced in isolation because ranking is a stage boundary. The ranker receives a user, a surface, and a candidate list, and it returns the same candidates in a new order. If the new ranker accepts the same input and returns the same output shape, nothing upstream or downstream has to change.&lt;/p&gt;

&lt;p&gt;Most feed pipelines have four stages: candidate generation, filtering, ranking, and rendering. The migration below changes one function call and its configuration.&lt;/p&gt;

&lt;p&gt;One exception applies. If the current ranker is also the retrieval step, for example a SQL query with &lt;code&gt;ORDER BY&lt;/code&gt; and &lt;code&gt;LIMIT 20&lt;/code&gt;, the candidate set must be widened first. The section on edge cases covers this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: How do you isolate the ranking call?
&lt;/h2&gt;

&lt;p&gt;Isolation means every ranking decision passes through one function with a fixed signature: &lt;code&gt;rank(user, surface, candidates) -&amp;gt; ordered candidates&lt;/code&gt;. Nothing else in the codebase reorders items after this call.&lt;/p&gt;

&lt;p&gt;Find every place that sorts candidates after retrieval: SQL &lt;code&gt;ORDER BY&lt;/code&gt; clauses, sort calls in API handlers, and client-side re-sorting. Move that logic into the single function. Then add one configuration flag with three values: &lt;code&gt;rules&lt;/code&gt;, &lt;code&gt;shadow&lt;/code&gt;, and &lt;code&gt;new&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Step 1 is complete when the feed output is byte-identical to production and all existing tests pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: What happens to the existing rules?
&lt;/h2&gt;

&lt;p&gt;Each existing rule becomes either a feature that the new ranker can weigh or a hard constraint applied outside the ranker. Rule #7 in Google's &lt;a href="https://developers.google.com/machine-learning/guides/rules-of-ml" rel="noopener noreferrer"&gt;Rules of Machine Learning&lt;/a&gt; by Martin Zinkevich gives the same instruction: turn heuristics into features, or handle them externally.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Existing rule&lt;/th&gt;
&lt;th&gt;What it becomes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Boost posts from followed accounts&lt;/td&gt;
&lt;td&gt;Feature: &lt;code&gt;is_followed&lt;/code&gt; (0 or 1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show newer posts first&lt;/td&gt;
&lt;td&gt;Feature: item age in hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Never show blocked or muted users&lt;/td&gt;
&lt;td&gt;Hard filter before ranking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max 2 posts per author in the top 10&lt;/td&gt;
&lt;td&gt;Diversity constraint after ranking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pin announcements to slot 1&lt;/td&gt;
&lt;td&gt;Fixed placement after ranking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A preference becomes a feature, because a model may learn to weigh it differently. A policy becomes a constraint, because no ranker should be able to violate legal, safety, or contractual rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: How do you run a new ranker in shadow mode?
&lt;/h2&gt;

&lt;p&gt;Shadow mode ranking calls the new ranker asynchronously on each request, logs its output next to the served output, and never shows it to users. The served response keeps the rules-based order, so a failure in the new ranker cannot affect the feed.&lt;/p&gt;

&lt;p&gt;The harness below runs with the Python standard library. Replace &lt;code&gt;new_ranker&lt;/code&gt; with the call to your model or vendor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;concurrent.futures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rules_ranker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Current production logic: followed authors first, then newest.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;author&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;follows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;created_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
                  &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_ranker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Stand-in. Replace with your model or HTTP client.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;overlap_at_k&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mean_rank_shift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;shadow_compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;served&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;new_ranker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shadow_error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;)}))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shadow_compare&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latency_ms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overlap_at_k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;overlap_at_k&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;served&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mean_rank_shift&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mean_rank_shift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;served&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rank_feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;served&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rules_ranker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shadow_compare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;served&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;served&lt;/span&gt;  &lt;span class="c1"&gt;# users always see the rules-based order
&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;u1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;follows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="n"&gt;candidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;p&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;author&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;created_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="nf"&gt;rank_feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log four signals per request: overlap@10, mean rank shift, latency, and error rate. An overlap@10 near 1.0 means the new ranker barely changes the feed. An overlap@10 near 0 means a large change, so sample those requests and review the lists by hand. Run shadow mode for at least one full week. Ron Kohavi, Diane Tang, and Ya Xu recommend full-week durations in &lt;a href="https://www.cambridge.org/core/books/trustworthy-online-controlled-experiments/D97B26382EB0EB2DC2019A7A7B518F59" rel="noopener noreferrer"&gt;Trustworthy Online Controlled Experiments&lt;/a&gt; (Cambridge University Press, 2020) because behavior varies by day of week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: How do you test the new ranker on live users?
&lt;/h2&gt;

&lt;p&gt;A live test exposes a small share of users to the new ranker and measures engagement against the rules-based baseline. Two methods work: an A/B test, which splits users between rankers, and interleaving, which mixes both rankers into one list for each user.&lt;/p&gt;

&lt;p&gt;Interleaving needs less traffic. Chapelle, Joachims, Radlinski, and Yue found interleaved comparisons more sensitive than standard A/B metrics in a large-scale search study (&lt;a href="https://dl.acm.org/doi/10.1145/2094072.2094078" rel="noopener noreferrer"&gt;ACM Transactions on Information Systems, 2012&lt;/a&gt;). A/B tests are still needed for session length and retention.&lt;/p&gt;

&lt;p&gt;Define guardrail metrics before the test starts. For feeds, useful guardrails are the hide or report rate, the share of creators receiving impressions, and p99 latency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: How do you cut over safely?
&lt;/h2&gt;

&lt;p&gt;Cut over by ramping traffic in stages, for example 1%, 10%, 50%, then 100%, while keeping the rules ranker as an automatic fallback. If the new ranker errors or exceeds the latency budget, the service serves the rules-based order for that request.&lt;/p&gt;

&lt;p&gt;Keep the fallback path in production for at least one release cycle after reaching 100%.&lt;/p&gt;

&lt;h2&gt;
  
  
  When is this approach the wrong choice?
&lt;/h2&gt;

&lt;p&gt;Stage-by-stage replacement fails in four situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ranking happens inside retrieval.&lt;/strong&gt; A query that returns 20 sorted rows gives a new ranker nothing to reorder. Widen candidate generation to a few hundred items first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No interaction logging exists.&lt;/strong&gt; Every learned or calibrated ranker needs impression and click data. Add logging before Step 3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traffic is very low.&lt;/strong&gt; Online tests may not reach significance. Rely on shadow mode and manual list review instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The rules encode policy.&lt;/strong&gt; Those rules stay as constraints permanently, as Step 2 describes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where does a ranking API fit in this migration?
&lt;/h2&gt;

&lt;p&gt;A hosted ranking API replaces only the &lt;code&gt;new_ranker&lt;/code&gt; function. Steps 1, 2, 4, and 5 stay the same, which is why the shadow harness doubles as a vendor evaluation tool: each candidate vendor can run on real traffic before any contract.&lt;/p&gt;

&lt;p&gt;Gortex is one option for that slot, currently in invite-only beta. One request with a recipient, a surface, and a candidate set returns the ranked order and fills sponsored slots in the same response, at sub-200ms p99. Calibrated linear ranking and deterministic slot-filling ship today. A learned ranker is planned for mid-2027 and a real-time auction for Q1 2027. Details are on the Gortex &lt;a href="https://gortex.ai/feed-ranking-api" rel="noopener noreferrer"&gt;feed ranking API&lt;/a&gt; page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get a free feed teardown
&lt;/h2&gt;

&lt;p&gt;Send your current ranking rules and feed surface, and the Gortex team will map which rules become features, which stay as constraints, and what a shadow test should measure. Request access at &lt;a href="https://gortex.ai" rel="noopener noreferrer"&gt;gortex.ai&lt;/a&gt;, or book a demo with the founder at &lt;a href="mailto:h@gortex.ai"&gt;h@gortex.ai&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How long does it take to replace a rules-based ranker?&lt;/strong&gt;&lt;br&gt;
Isolation and rule mapping usually fit in one sprint. Shadow mode runs at least one full week, and the live test runs until it reaches significance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does replacing a rules-based ranker require a data science team?&lt;/strong&gt;&lt;br&gt;
No. Shadow mode, guardrails, and staged rollout are backend engineering tasks. A team needs modeling skills only if it trains its own ranker.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can some rules stay after the migration?&lt;/strong&gt;&lt;br&gt;
Yes. Policy rules stay as filters or placement constraints. Only preference rules move into the ranker as features.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>10 Ranking Algorithms Used in Production: From Rules to Learning-to-Rank</title>
      <dc:creator>Hemin Joshi</dc:creator>
      <pubDate>Tue, 13 Oct 2026 08:22:24 +0000</pubDate>
      <link>https://dev.to/heminjoshi/10-ranking-algorithms-used-in-production-from-rules-to-learning-to-rank-2fbe</link>
      <guid>https://dev.to/heminjoshi/10-ranking-algorithms-used-in-production-from-rules-to-learning-to-rank-2fbe</guid>
      <description>&lt;p&gt;Most ranking systems do not begin with machine learning.&lt;/p&gt;

&lt;p&gt;They begin with something like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;score = freshness + popularity&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then the product grows. Users behave differently. Inventory expands. Cold start matters. Business rules accumulate. Personalization becomes important. Monetization enters the same surface.&lt;/p&gt;

&lt;p&gt;Eventually, one hand-tuned score is no longer enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The short answer:&lt;/strong&gt; production ranking systems usually combine multiple approaches rather than rely on a single algorithm. A cheap model or heuristic narrows and scores candidates first, then more expensive models or reranking logic improve the final order.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://research.google/pubs/deep-neural-networks-for-youtube-recommendations/" rel="noopener noreferrer"&gt;Google's published YouTube architecture&lt;/a&gt; separates candidate generation from a dedicated deep ranking model. LinkedIn describes first-pass rankers feeding a second-pass ranker, followed by reranking. Vespa explicitly supports first-phase, second-phase, and global ranking so expensive models only run on the strongest candidates. &lt;/p&gt;

&lt;p&gt;That distinction matters if you are designing a feed, marketplace, recommendation system, job board, dating app, or discovery product.&lt;/p&gt;

&lt;p&gt;Here are 10 ranking algorithms and approaches commonly found in production systems, from deterministic rules to learned ranking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ranking algorithms at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Best suited for&lt;/th&gt;
&lt;th&gt;Personalization&lt;/th&gt;
&lt;th&gt;Complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rule-based ranking&lt;/td&gt;
&lt;td&gt;Policy and simple products&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weighted linear scoring&lt;/td&gt;
&lt;td&gt;Early production ranking&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Popularity and trending&lt;/td&gt;
&lt;td&gt;Cold start and discovery&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BM25&lt;/td&gt;
&lt;td&gt;Search relevance&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content-based ranking&lt;/td&gt;
&lt;td&gt;Sparse interaction data&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collaborative filtering&lt;/td&gt;
&lt;td&gt;Behavioral recommendations&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Matrix factorization&lt;/td&gt;
&lt;td&gt;User-item affinity&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gradient-boosted trees&lt;/td&gt;
&lt;td&gt;Structured ranking features&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deep neural ranking&lt;/td&gt;
&lt;td&gt;Large, complex datasets&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning-to-rank&lt;/td&gt;
&lt;td&gt;Optimizing ordered results&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The useful question is not which algorithm is "best."&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;which ranking architecture matches your data, latency budget, product maturity, and objective&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you are still separating retrieval from ranking, our &lt;a href="https://gortex.ai/blog/ranking-api-explained-how-to-choose-one" rel="noopener noreferrer"&gt;Ranking API buyer's guide&lt;/a&gt; explains where the ranking layer sits in the request path.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Rule-Based Ranking
&lt;/h2&gt;

&lt;p&gt;Rule-based ranking is the simplest production approach.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;newest items first&lt;/li&gt;
&lt;li&gt;exclude unavailable inventory&lt;/li&gt;
&lt;li&gt;boost verified sellers&lt;/li&gt;
&lt;li&gt;cap posts from the same creator&lt;/li&gt;
&lt;li&gt;prioritize items within a user's geography&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rules are deterministic and easy to explain. That makes them extremely useful for hard constraints, even inside sophisticated ML systems.&lt;/p&gt;

&lt;p&gt;The mistake is using rules for everything.&lt;/p&gt;

&lt;p&gt;As more requirements appear, teams often end up with dozens of interacting conditions:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;if premium_user &amp;amp;&amp;amp; recent_item &amp;amp;&amp;amp; category_match &amp;amp;&amp;amp; seller_score &amp;gt; X...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Eventually, nobody can confidently predict how changing one weight affects the whole system.&lt;/p&gt;

&lt;p&gt;Rules work best for eligibility, safety, compliance, and non-negotiable product constraints. Learned systems work better when the question becomes "which eligible item is most valuable to this user?"&lt;/p&gt;

&lt;p&gt;That distinction is also central to how a modern &lt;a href="https://gortex.ai/decision-engine" rel="noopener noreferrer"&gt;decision engine&lt;/a&gt; separates policy from downstream ranking.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Weighted Linear Scoring
&lt;/h2&gt;

&lt;p&gt;Weighted scoring is often the first real ranking engine a startup builds.&lt;/p&gt;

&lt;p&gt;A simplified score might look like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;score = 0.40 relevance + 0.25 freshness + 0.20 popularity + 0.15 quality&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Unlike a single sort field, this combines multiple signals while remaining fast and explainable.&lt;/p&gt;

&lt;p&gt;Production search systems use the same basic idea. Vespa's documentation, for example, shows ranking expressions combining BM25 relevance with popularity and supports arbitrary mathematical combinations of document, query, and computed ranking features. &lt;/p&gt;

&lt;p&gt;Weighted scoring works particularly well when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the number of signals is manageable&lt;/li&gt;
&lt;li&gt;engineers understand their relative importance&lt;/li&gt;
&lt;li&gt;training data is limited&lt;/li&gt;
&lt;li&gt;explainability matters&lt;/li&gt;
&lt;li&gt;latency needs to remain predictable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The weakness is manual calibration. As the product evolves, someone has to decide whether freshness should be 0.25 or 0.31.&lt;/p&gt;

&lt;p&gt;Gortex currently takes this transparent approach with &lt;a href="https://gortex.ai/ranking-api" rel="noopener noreferrer"&gt;calibrated linear ranking&lt;/a&gt; in its private beta. &lt;/p&gt;

&lt;h2&gt;
  
  
  3. Popularity and Trending Ranking
&lt;/h2&gt;

&lt;p&gt;Popularity ranking orders items using aggregate behavior such as views, clicks, purchases, saves, or interactions.&lt;/p&gt;

&lt;p&gt;It is simple, but extremely useful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/personalize/latest/dg/working-with-predefined-recipes.html" rel="noopener noreferrer"&gt;Amazon Personalize&lt;/a&gt; even exposes a Popular Items recipe alongside personalized recommendation and ranking recipes. &lt;/p&gt;

&lt;p&gt;Popularity is particularly valuable for &lt;strong&gt;cold start&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you know almost nothing about a new user, "what is currently popular among similar visitors?" can be a much better fallback than random ordering.&lt;/p&gt;

&lt;p&gt;Trending ranking adds a time component.&lt;/p&gt;

&lt;p&gt;Instead of lifetime interactions, a system might measure:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;recent engagement / expected engagement&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;or apply exponential decay so yesterday's activity matters more than last month's.&lt;/p&gt;

&lt;p&gt;The main risk is feedback loops. Items ranked highly receive more exposure, generating more interactions, which can make them rank even higher.&lt;/p&gt;

&lt;p&gt;Production systems therefore rarely use popularity alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. BM25 and Lexical Relevance
&lt;/h2&gt;

&lt;p&gt;For search surfaces, BM25 remains an important baseline.&lt;/p&gt;

&lt;p&gt;BM25 scores how well a document matches a query based largely on term frequency, document length, and how rare the matched terms are across the corpus.&lt;/p&gt;

&lt;p&gt;It is not personalized in the same way as a feed ranker, but it remains useful for candidate ranking because it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inexpensive&lt;/li&gt;
&lt;li&gt;interpretable&lt;/li&gt;
&lt;li&gt;strong for exact textual relevance&lt;/li&gt;
&lt;li&gt;mature at large scale&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Modern systems frequently combine lexical ranking with other signals rather than replace it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.vespa.ai/en/ranking/phased-ranking.html" rel="noopener noreferrer"&gt;Vespa&lt;/a&gt;, for example, demonstrates first-phase ranking that combines &lt;code&gt;bm25(title)&lt;/code&gt; with freshness before applying a more expensive XGBoost model during a later ranking phase. &lt;/p&gt;

&lt;p&gt;That is a good example of what production ranking usually looks like: &lt;strong&gt;multiple models doing different jobs at different stages&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Content-Based Ranking
&lt;/h2&gt;

&lt;p&gt;Content-based systems compare user preferences with item attributes.&lt;/p&gt;

&lt;p&gt;For a news app, those features might include topic, author, language, embedding, or publication time.&lt;/p&gt;

&lt;p&gt;For a marketplace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;category&lt;/li&gt;
&lt;li&gt;price&lt;/li&gt;
&lt;li&gt;brand&lt;/li&gt;
&lt;li&gt;seller attributes&lt;/li&gt;
&lt;li&gt;product embedding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a dating product:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;interests&lt;/li&gt;
&lt;li&gt;profile features&lt;/li&gt;
&lt;li&gt;location&lt;/li&gt;
&lt;li&gt;stated preferences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Content-based ranking is especially useful when interaction data is sparse.&lt;/p&gt;

&lt;p&gt;A new item may have zero clicks, but its content still tells you something about who might want it.&lt;/p&gt;

&lt;p&gt;Modern versions often replace manually selected categories with learned embeddings, allowing semantically similar items to sit near one another in vector space.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Collaborative Filtering
&lt;/h2&gt;

&lt;p&gt;Collaborative filtering learns from patterns across users rather than relying entirely on item attributes.&lt;/p&gt;

&lt;p&gt;The intuition is straightforward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Users who behaved similarly in the past may prefer similar items in the future.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This can be powerful when a platform has substantial interaction data because the system can discover relationships that were never explicitly encoded.&lt;/p&gt;

&lt;p&gt;The weakness is equally familiar: cold start.&lt;/p&gt;

&lt;p&gt;A new user has little behavioral history. A new item has few interactions.&lt;/p&gt;

&lt;p&gt;That is why collaborative filtering is commonly combined with content signals, popularity, or other fallback strategies rather than deployed alone.&lt;/p&gt;

&lt;p&gt;For teams deciding whether candidate generation and candidate ordering should live in the same system, it helps to distinguish broader recommendation infrastructure from a dedicated ranking API.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Matrix Factorization
&lt;/h2&gt;

&lt;p&gt;Matrix factorization is one of the classic approaches for estimating user-item affinity.&lt;/p&gt;

&lt;p&gt;Imagine a giant matrix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rows are users&lt;/li&gt;
&lt;li&gt;columns are items&lt;/li&gt;
&lt;li&gt;cells contain interactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most cells are empty.&lt;/p&gt;

&lt;p&gt;Matrix factorization learns smaller latent representations for users and items so the system can estimate preferences even where no direct interaction exists.&lt;/p&gt;

&lt;p&gt;Those hidden factors might indirectly represent concepts such as genre preference, price sensitivity, style, or content taste.&lt;/p&gt;

&lt;p&gt;Matrix factorization became particularly well known through recommender systems because it can capture behavioral relationships that simple popularity ranking misses.&lt;/p&gt;

&lt;p&gt;Its biggest limitation for modern real-time products is context.&lt;/p&gt;

&lt;p&gt;User A may generally prefer technology content, but what they want at 8 AM on a phone can differ from what they want Saturday evening on a TV.&lt;/p&gt;

&lt;p&gt;That pushed production systems toward models that can incorporate richer contextual features.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Gradient-Boosted Decision Trees
&lt;/h2&gt;

&lt;p&gt;Gradient-boosted trees, including XGBoost and LightGBM-style models, remain highly practical ranking tools.&lt;/p&gt;

&lt;p&gt;They perform especially well when you have structured features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;historical CTR&lt;/li&gt;
&lt;li&gt;creator quality&lt;/li&gt;
&lt;li&gt;price&lt;/li&gt;
&lt;li&gt;freshness&lt;/li&gt;
&lt;li&gt;user-item affinity&lt;/li&gt;
&lt;li&gt;query relevance&lt;/li&gt;
&lt;li&gt;inventory information&lt;/li&gt;
&lt;li&gt;session features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Their appeal is that they can model nonlinear interactions without the serving complexity of a very large neural network.&lt;/p&gt;

&lt;p&gt;Vespa supports XGBoost and LightGBM directly inside ranking pipelines, including using a cheap first-phase scorer and a boosted model only on the strongest candidates.&lt;/p&gt;

&lt;p&gt;This cascade is important.&lt;/p&gt;

&lt;p&gt;Running your most expensive model over 100 candidates is very different from running it over 100,000.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Deep Neural Ranking
&lt;/h2&gt;

&lt;p&gt;Deep ranking models become attractive when the relationships between users, items, and context are too complex for manually engineered interactions.&lt;/p&gt;

&lt;p&gt;Google's YouTube paper described separate deep neural models for candidate generation and ranking at one of the world's largest recommendation systems. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/pinterest-engineering/multi-task-learning-and-calibration-for-utility-based-home-feed-ranking-64087a7bcbad" rel="noopener noreferrer"&gt;Pinterest has also documented neural Homefeed ranking systems&lt;/a&gt; that consume user, Pin, and contextual signals to predict user actions. Pinterest later evolved its ranking toward multi-task learning so the system could model several engagement objectives rather than optimizing a single action. &lt;/p&gt;

&lt;p&gt;Deep models can capture sophisticated behavioral relationships, but the trade-offs are real:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;greater data requirements&lt;/li&gt;
&lt;li&gt;more difficult debugging&lt;/li&gt;
&lt;li&gt;higher inference costs&lt;/li&gt;
&lt;li&gt;more complex training infrastructure&lt;/li&gt;
&lt;li&gt;harder model observability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A neural ranker is not automatically better simply because it is more complex.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Learning-to-Rank
&lt;/h2&gt;

&lt;p&gt;Learning-to-rank, or LTR, is specifically designed for &lt;strong&gt;ordered results&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of simply predicting "will the user click?", an LTR model learns how candidates should be ordered relative to one another.&lt;/p&gt;

&lt;p&gt;There are three broad approaches:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pointwise:&lt;/strong&gt; predict a relevance score independently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pairwise:&lt;/strong&gt; learn whether item A should rank above item B.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Listwise:&lt;/strong&gt; optimize the ordering of an entire list.&lt;/p&gt;

&lt;p&gt;One of the b&lt;a href="https://www.microsoft.com/en-us/research/publication/from-ranknet-to-lambdarank-to-lambdamart-an-overview/" rel="noopener noreferrer"&gt;est-known approaches is LambdaMART&lt;/a&gt;, which combines LambdaRank with boosted decision trees. Microsoft's work describes LambdaMART as a successful real-world ranking method, while modern XGBoost implements ranking objectives for NDCG, MAP, and pairwise ranking. &lt;/p&gt;

&lt;p&gt;NDCG is especially useful because it cares about &lt;strong&gt;where&lt;/strong&gt; relevant results appear, not merely whether they appear.&lt;/p&gt;

&lt;p&gt;That matches the product problem much better than ordinary classification.&lt;/p&gt;

&lt;p&gt;There is one major caveat: logged interaction data contains position bias. Results already shown near the top receive more clicks simply because users see them first. XGBoost's current LTR implementation includes an unbiased LambdaMART option specifically to address position-dependent click data. &lt;/p&gt;

&lt;h2&gt;
  
  
  Production ranking systems usually combine algorithms
&lt;/h2&gt;

&lt;p&gt;The biggest misconception is that mature teams eventually discover one perfect ranker.&lt;/p&gt;

&lt;p&gt;They usually build a cascade.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://engineering.linkedin.com/teams/data/artificial-intelligence/feed" rel="noopener noreferrer"&gt;LinkedIn's published feed architecture&lt;/a&gt; includes multiple first-pass rankers, a second-pass ranker, and a final reranking layer. &lt;/p&gt;

&lt;p&gt;Pinterest describes retrieval, pre-ranking, ranking, and reranking in its Homefeed recommendation funnel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://research.atspotify.com/2025/9/calibrated-recommendations-with-contextual-bandits-on-spotify-homepage" rel="noopener noreferrer"&gt;Spotify recently deployed contextual-bandit-based calibration&lt;/a&gt; on its Home page to balance content types dynamically. In its reported online experiment, the approach improved overall impression-to-stream efficiency by 3.93%, consumption by 1.28%, and Home activity by 1.54% versus its baseline.&lt;/p&gt;

&lt;p&gt;That last step matters because a production ranker is rarely optimizing only relevance.&lt;/p&gt;

&lt;p&gt;It may also need to balance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;diversity&lt;/li&gt;
&lt;li&gt;freshness&lt;/li&gt;
&lt;li&gt;exploration&lt;/li&gt;
&lt;li&gt;creator fairness&lt;/li&gt;
&lt;li&gt;marketplace supply&lt;/li&gt;
&lt;li&gt;revenue&lt;/li&gt;
&lt;li&gt;long-term satisfaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once monetization enters the feed, the final ordering may also need to reconcile organic relevance with sponsored inventory. Our guides to &lt;a href="https://gortex.ai/blog/sponsored-listings-api-guide" rel="noopener noreferrer"&gt;sponsored listings architecture&lt;/a&gt; and &lt;a href="https://gortex.ai/blog/auction-api-explained" rel="noopener noreferrer"&gt;real-time ad auctions&lt;/a&gt; cover that layer separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which ranking algorithm should you use?
&lt;/h2&gt;

&lt;p&gt;For a small product, start simpler than you think.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Low interaction volume:&lt;/strong&gt; rules, popularity, and content signals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Growing feed or marketplace:&lt;/strong&gt; weighted scoring plus personalized features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strong structured behavioral data:&lt;/strong&gt; gradient-boosted models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Large-scale recommendation system:&lt;/strong&gt; deep models and multi-stage ranking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enough labeled or interaction data to optimize ordering directly:&lt;/strong&gt; learning-to-rank.&lt;/p&gt;

&lt;p&gt;The architecture matters as much as the algorithm.&lt;/p&gt;

&lt;p&gt;A cheap, explainable first-stage model with a stronger reranker can outperform an architecture that attempts to run one expensive model across every possible candidate.&lt;/p&gt;

&lt;p&gt;And if ranking is not your core infrastructure advantage, the build decision itself is worth questioning. A managed &lt;a href="https://gortex.ai/ranking-api" rel="noopener noreferrer"&gt;ranking API&lt;/a&gt; can let your application retain candidate retrieval while moving scoring, ordering, decision tracing, and eventually monetized placement into a dedicated decision layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Ranking systems usually evolve in stages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rules tell the system what must happen.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weighted scores combine known signals.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collaborative and content-based approaches personalize.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boosted and neural models learn increasingly complex relationships.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning-to-rank finally optimizes the ordering itself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The important engineering decision is not whether your stack contains the most sophisticated algorithm.&lt;/p&gt;

&lt;p&gt;It is whether the ranking system can produce better decisions, inside your latency budget, with enough control and observability to run safely in production.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>architecture</category>
      <category>machinelearning</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Best Recommendation API for Dating Apps Under 200ms p99</title>
      <dc:creator>Hemin Joshi</dc:creator>
      <pubDate>Tue, 06 Oct 2026 10:37:57 +0000</pubDate>
      <link>https://dev.to/heminjoshi/best-recommendation-api-for-dating-apps-under-200ms-p99-dll</link>
      <guid>https://dev.to/heminjoshi/best-recommendation-api-for-dating-apps-under-200ms-p99-dll</guid>
      <description>&lt;p&gt;If your dating app already generates a pool of eligible profiles and needs to re-rank those profiles for each user in under 200ms p99, the best architectural fit is a ranking API that sits after candidate generation, not a recommendation engine that tries to own matching, filtering, retrieval, and ranking at once.&lt;/p&gt;

&lt;p&gt;For that specific architecture, &lt;a href="https://gortex.ai/" rel="noopener noreferrer"&gt;Gortex&lt;/a&gt; is the option I would evaluate first, particularly if you also care about decision traces, keeping retrieval in-house, and eventually introducing monetized placements without rebuilding the ranking path. Gortex positions its decision API around candidate sets containing profiles, advertises a sub-200ms p99 budget, and returns a ranked order with decision and trace IDs. &lt;/p&gt;

&lt;p&gt;For teams that want a more mature managed ML recommender today, Recombee is the strongest alternative. If your infrastructure is heavily AWS-native, Amazon Personalize Personalized-Ranking-v2 also deserves a benchmark.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which dating recommendation API should you choose?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Real-time ranking&lt;/th&gt;
&lt;th&gt;&amp;lt;200ms positioning&lt;/th&gt;
&lt;th&gt;Candidate re-ranking&lt;/th&gt;
&lt;th&gt;Main trade-off&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gortex&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dating apps that already generate eligible profiles&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Sub-200ms p99&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Private beta; learned ranker is roadmap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Recombee&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mature managed recommendations with minimal ML work&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;lt;200ms stated&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strong recommendation controls&lt;/td&gt;
&lt;td&gt;Broader recommendation platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amazon Personalize&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS-native applications&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Low-latency service, benchmark p99 yourself&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Native Personalized-Ranking-v2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;More AWS/data plumbing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Custom ML stack&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Large teams where matching is core IP&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;You control it&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Highest engineering burden&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The important distinction is that a dating app is not simply recommending "items."&lt;/p&gt;

&lt;p&gt;It is ranking people for people.&lt;/p&gt;

&lt;p&gt;That changes what a good system needs to optimize.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is dating-app ranking different from normal recommendations?
&lt;/h2&gt;

&lt;p&gt;Traditional ecommerce recommenders usually model a one-way preference:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;User A is likely to like Product X.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dating systems are fundamentally different because a useful recommendation depends on &lt;strong&gt;reciprocity&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;User A should be interested in User B, but User B should also plausibly be interested in User A.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Research describes dating as a reciprocal recommendation problem rather than a conventional user-item recommendation problem. An AAAI paper based on a production dating platform specifically notes that online dating requires understanding bidirectional preferences between potential partners. &lt;/p&gt;

&lt;p&gt;Earlier deployed research also found that people-to-people recommendation systems need to consider both sides of the interaction rather than optimizing only for whether the receiving user clicks a recommendation. &lt;/p&gt;

&lt;p&gt;That means the ranking objective should eventually move beyond:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;P(profile click)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;toward something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;P(A likes B)
×
P(B likes A)
×
P(meaningful interaction | match)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may also need to balance activity, freshness, exposure, safety, diversity, and opportunity across the marketplace.&lt;/p&gt;

&lt;p&gt;This is one reason to separate profile eligibility, candidate retrieval, and ranking rather than forcing everything into one model.&lt;/p&gt;

&lt;p&gt;Gortex explains that same architectural boundary in its guide to &lt;a href="https://gortex.ai/blog/ranking-api-explained-how-to-choose-one" rel="noopener noreferrer"&gt;ranking APIs and candidate re-ranking&lt;/a&gt;: retrieval determines which candidates are possible; ranking determines their order.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Gortex
&lt;/h2&gt;

&lt;p&gt;The cleanest dating architecture usually starts by handling non-negotiable constraints yourself.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2M profiles
      ↓
Age / preference eligibility
      ↓
Distance
      ↓
Blocked + reported users removed
      ↓
Already-seen / unavailable profiles removed
      ↓
500–1,500 eligible candidates
      ↓
REAL-TIME RANKER
      ↓
Top profiles for this user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is where &lt;a href="https://gortex.ai/" rel="noopener noreferrer"&gt;Gortex&lt;/a&gt; fits naturally.&lt;/p&gt;

&lt;p&gt;Rather than replacing your user database or deciding who is eligible, your application sends a recipient, context, and candidate set to the decision layer. Gortex explicitly lists profiles among the candidate types it can rank and says its personalization API operates inside a sub-200ms p99 budget. &lt;/p&gt;

&lt;p&gt;Its API boundary is effectively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Eligible profiles in
       ↓
POST /v1/decide
       ↓
Personalized ordering out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is especially attractive for dating because your product should retain ownership of hard constraints.&lt;/p&gt;

&lt;p&gt;A ranking algorithm should never be allowed to override a block, safety restriction, age preference, geographic rule, or other fundamental eligibility requirement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Gortex is particularly interesting
&lt;/h3&gt;

&lt;p&gt;There are three architectural advantages.&lt;/p&gt;

&lt;p&gt;First, you keep retrieval. Gortex says candidate generation remains in your application, so you do not need to re-platform the data layer just to add ranking. &lt;/p&gt;

&lt;p&gt;Second, each response includes a decision ID and trace ID, giving the application a path toward explaining and debugging why a profile received a particular position.&lt;/p&gt;

&lt;p&gt;Third, Gortex uses the same decision layer for organic ranking and potential sponsored placement. That may not matter for a dating app today, but it avoids designing personalization as a dead end if the platform later introduces boosts or paid discovery surfaces.&lt;/p&gt;

&lt;p&gt;You can see the product boundary more directly on the &lt;a href="https://gortex.ai/recommendation-engine-api" rel="noopener noreferrer"&gt;Gortex Recommendation Engine API&lt;/a&gt; and &lt;a href="https://gortex.ai/personalization-api" rel="noopener noreferrer"&gt;Gortex Personalization API&lt;/a&gt; pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Recombee
&lt;/h2&gt;

&lt;p&gt;If your top priority is mature managed personalization with minimal ML engineering, Recombee is a serious contender.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.recombee.com/api" rel="noopener noreferrer"&gt;Recombee&lt;/a&gt; states that recommendations are updated immediately after user actions and returned in less than 200ms.&lt;/p&gt;

&lt;p&gt;Its API can also recommend users to users, which is particularly relevant for dating rather than traditional product recommendation. The platform supports interaction tracking, relevance controls, recommendation rotation and personalized ranking behavior. &lt;/p&gt;

&lt;p&gt;A dating app might send events such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;profile impression&lt;/li&gt;
&lt;li&gt;profile opened&lt;/li&gt;
&lt;li&gt;like&lt;/li&gt;
&lt;li&gt;pass&lt;/li&gt;
&lt;li&gt;match&lt;/li&gt;
&lt;li&gt;message sent&lt;/li&gt;
&lt;li&gt;message replied&lt;/li&gt;
&lt;li&gt;conversation continued&lt;/li&gt;
&lt;li&gt;hide&lt;/li&gt;
&lt;li&gt;block&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recombee's rotation controls are useful as well. A dating experience should not repeatedly return the same small set of highly ranked profiles simply because the model has high confidence in them. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose Recombee when:&lt;/strong&gt; you want a mature recommendation-as-a-service platform and want the vendor to handle more of the personalization machinery.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Amazon Personalize
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/personalize/latest/dg/native-recipe-personalized-ranking-v2.html" rel="noopener noreferrer"&gt;Amazon Personalize&lt;/a&gt; is particularly interesting when you already generate the profile candidates yourself.&lt;/p&gt;

&lt;p&gt;Its Personalized-Ranking-v2 recipe was built specifically to accept an input collection and reorder it for an individual user. AWS says it supports up to five million items and offers lower latency than its previous Personalized-Ranking recipe. &lt;/p&gt;

&lt;p&gt;The request pattern is almost exactly what a dating app needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user_123

candidate profiles:
profile_17
profile_42
profile_88
profile_103
...

         ↓

GetPersonalizedRanking

         ↓

profiles ordered by predicted interest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AWS also supports real-time personalization as new interaction data is recorded.&lt;/p&gt;

&lt;p&gt;However, I would not interpret "lower latency" as proof that your own workload will achieve &amp;lt;200ms p99.&lt;/p&gt;

&lt;p&gt;Benchmark it.&lt;/p&gt;

&lt;p&gt;Run requests from your production region with your actual candidate count, filters, context fields, concurrency and network path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose Amazon Personalize when:&lt;/strong&gt; AWS integration matters more than having an opinionated ranking product.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should happen before the ranking API?
&lt;/h2&gt;

&lt;p&gt;Do not send every dating profile into the ranker.&lt;/p&gt;

&lt;p&gt;Perform hard eligibility filtering first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Candidate generation
       ↓
Age / preference filters
       ↓
Geo constraints
       ↓
Safety / block lists
       ↓
Availability / activity
       ↓
Already seen / matched profiles
       ↓
Ranking API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then let the ranker answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Among eligible profiles, who should this person see first?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That architecture also makes your latency target much more realistic.&lt;/p&gt;

&lt;p&gt;A useful deeper explanation is &lt;a href="https://gortex.ai/blog/recommendation-engine-architecture" rel="noopener noreferrer"&gt;Gortex's recommendation-engine architecture guide&lt;/a&gt;, which separates retrieval, ranking, re-ranking, serving, and logging rather than treating recommendation as one black box. &lt;/p&gt;

&lt;h2&gt;
  
  
  What should a dating app optimize for?
&lt;/h2&gt;

&lt;p&gt;Do not optimize your recommendation API solely for profile clicks or likes.&lt;/p&gt;

&lt;p&gt;The meaningful funnel is closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Impression
   ↓
Profile view
   ↓
Like
   ↓
Mutual match
   ↓
Message
   ↓
Reply
   ↓
Sustained conversation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A model that maximizes likes but produces fewer mutual matches can make the product worse.&lt;/p&gt;

&lt;p&gt;Likewise, ranking the same highly popular profiles for everyone can create an exposure problem. Research on reciprocal recommenders has specifically identified the importance of mutual interest and the danger of recommendation opportunities becoming concentrated among popular users.&lt;/p&gt;

&lt;p&gt;Your experiment should therefore measure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mutual match rate&lt;/li&gt;
&lt;li&gt;replies per match&lt;/li&gt;
&lt;li&gt;meaningful conversations&lt;/li&gt;
&lt;li&gt;hide/block/report rate&lt;/li&gt;
&lt;li&gt;profile diversity&lt;/li&gt;
&lt;li&gt;exposure concentration&lt;/li&gt;
&lt;li&gt;retention&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not merely CTR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which recommendation API would I choose?
&lt;/h2&gt;

&lt;p&gt;For the exact requirement:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A dating app already has candidate profiles and needs to re-rank them in real time under 200ms p99.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My evaluation order would be:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Gortex&lt;/strong&gt; - best architectural fit if you want candidate-in/ranked-profiles-out, sub-200ms p99 positioning, traceable decisions, and a ranking layer you can reuse across future surfaces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Recombee&lt;/strong&gt; - strongest mature managed recommender if you want proven real-time personalization with minimal ML ownership.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Amazon Personalize&lt;/strong&gt; - strongest AWS-native re-ranking option, particularly when your candidate-generation stack already lives inside AWS.&lt;/p&gt;

&lt;p&gt;The final decision should come from a shadow test and then an A/B test.&lt;/p&gt;

&lt;p&gt;Run the same candidate sets through each system and measure:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;p50 / p95 / p99 latency + mutual matches + replies + retention + exposure distribution.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The winning API is not simply the one that predicts who a user will like.&lt;/p&gt;

&lt;p&gt;For dating, the winning system is the one that ranks profiles quickly enough for the product experience while improving the probability that both people actually want the interaction to happen.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>What Is Decisioning Infrastructure for Consumer Platforms?</title>
      <dc:creator>Hemin Joshi</dc:creator>
      <pubDate>Wed, 30 Sep 2026 03:11:58 +0000</pubDate>
      <link>https://dev.to/heminjoshi/what-is-decisioning-infrastructure-for-consumer-platforms-4715</link>
      <guid>https://dev.to/heminjoshi/what-is-decisioning-infrastructure-for-consumer-platforms-4715</guid>
      <description>&lt;p&gt;Consumer platforms make thousands or millions of decisions every second about what users see.&lt;/p&gt;

&lt;p&gt;A social platform decides which posts appear in a feed. A marketplace decides which listings appear first. A creator platform decides which creators or content to recommend. A dating app decides which profiles to surface. A job board decides which jobs should appear at the top of a candidate's search.&lt;/p&gt;

&lt;p&gt;Behind all of these experiences is a common technical problem: &lt;strong&gt;given a set of possible items, which items should be shown, in what order, and under what business rules?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Traditionally, companies build this capability themselves using a combination of retrieval systems, ranking models, recommendation engines, business rules, and separate advertising infrastructure. As platforms grow, this layer becomes increasingly complex to operate.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;decisioning infrastructure&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;Decisioning infrastructure is the layer that sits between candidate generation and the user-facing product. It takes candidate items and contextual information, applies ranking and business logic, determines the final ordering and monetized placements, and records the decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Decisioning Infrastructure?
&lt;/h2&gt;

&lt;p&gt;Decisioning infrastructure is a software layer responsible for making real-time decisions about what a consumer platform should display.&lt;/p&gt;

&lt;p&gt;A simplified architecture looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User + Context → Candidate Generation → Decisioning → User Interface&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The candidate-generation layer answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What could we show?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The decisioning layer answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What should we show, in what order, and where should monetized inventory appear?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The distinction is important.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/machine-learning/recommendation/overview/types" rel="noopener noreferrer"&gt;Modern recommendation systems&lt;/a&gt; commonly separate &lt;strong&gt;candidate generation, scoring, and re-ranking&lt;/strong&gt;. Google describes candidate generation as narrowing a potentially huge corpus into a smaller set, followed by scoring and re-ranking to determine what ultimately appears to the user.&lt;/p&gt;

&lt;p&gt;Decisioning infrastructure extends this final part of the architecture into an explicit production layer.&lt;/p&gt;

&lt;p&gt;It can incorporate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Relevance&lt;/li&gt;
&lt;li&gt;Personalization&lt;/li&gt;
&lt;li&gt;User context&lt;/li&gt;
&lt;li&gt;Freshness&lt;/li&gt;
&lt;li&gt;Business rules&lt;/li&gt;
&lt;li&gt;Inventory constraints&lt;/li&gt;
&lt;li&gt;Sponsored placements&lt;/li&gt;
&lt;li&gt;Monetization objectives&lt;/li&gt;
&lt;li&gt;Diversity requirements&lt;/li&gt;
&lt;li&gt;Eligibility rules&lt;/li&gt;
&lt;li&gt;Experimentation&lt;/li&gt;
&lt;li&gt;Decision logging and explanations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of embedding all of this logic directly into an application, a platform can expose it through a dedicated decisioning service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Consumer Platforms Need a Separate Decisioning Layer
&lt;/h2&gt;

&lt;p&gt;At first, ranking can look relatively simple.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://gortex.ai/marketplaces" rel="noopener noreferrer"&gt;marketplace&lt;/a&gt; might sort products by relevance. A social network might sort posts by predicted engagement. A job board might sort jobs by relevance to a candidate.&lt;/p&gt;

&lt;p&gt;As the product grows, however, ranking becomes a multi-objective problem.&lt;/p&gt;

&lt;p&gt;The platform may simultaneously need to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What the user is likely to find relevant&lt;/li&gt;
&lt;li&gt;What is available right now&lt;/li&gt;
&lt;li&gt;What content is fresh&lt;/li&gt;
&lt;li&gt;What the user has already seen&lt;/li&gt;
&lt;li&gt;Whether an item is eligible for the surface&lt;/li&gt;
&lt;li&gt;Whether a business rule should override the model&lt;/li&gt;
&lt;li&gt;Whether sponsored inventory should be displayed&lt;/li&gt;
&lt;li&gt;How much sponsored inventory the experience can tolerate&lt;/li&gt;
&lt;li&gt;How monetization affects the final ordering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates an infrastructure problem rather than simply a machine-learning problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/blog/engineering/recommendations/building-a-large-scale-recommendation-system-people-you-may-know" rel="noopener noreferrer"&gt;LinkedIn, for example, describes a multi-stage ranking architecture&lt;/a&gt; in which candidate generation first selects candidates from a very large inventory, followed by ranking stages that calibrate and reduce the candidate set against a common objective.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://engineering.fb.com/2018/10/02/ml-applications/under-the-hood-facebook-marketplace-powered-by-artificial-intelligence/" rel="noopener noreferrer"&gt;Facebook Marketplace&lt;/a&gt; similarly uses retrieval and ranking as separate stages because the system needs to narrow a massive product inventory before applying more computationally expensive ranking models. &lt;/p&gt;

&lt;p&gt;The difficult part is therefore not just creating a model.&lt;/p&gt;

&lt;p&gt;It is making the &lt;strong&gt;decision reliably, quickly, consistently, and observably in production&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decisioning Infrastructure vs Retrieval
&lt;/h2&gt;

&lt;p&gt;One of the most important distinctions is between retrieval and decisioning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieval asks: "What are the candidates?"
&lt;/h3&gt;

&lt;p&gt;Suppose a marketplace has 10 million listings.&lt;/p&gt;

&lt;p&gt;It would be impractical to run a sophisticated ranking model against every listing for every request.&lt;/p&gt;

&lt;p&gt;Instead, retrieval systems narrow the inventory to a manageable candidate set.&lt;/p&gt;

&lt;p&gt;Modern retrieval systems can use techniques such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vector embeddings&lt;/li&gt;
&lt;li&gt;Approximate nearest-neighbor search&lt;/li&gt;
&lt;li&gt;Collaborative filtering&lt;/li&gt;
&lt;li&gt;Content similarity&lt;/li&gt;
&lt;li&gt;User history&lt;/li&gt;
&lt;li&gt;Geographic signals&lt;/li&gt;
&lt;li&gt;Popularity&lt;/li&gt;
&lt;li&gt;Graph relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/machine-learning/recommendation/overview/candidate-generation" rel="noopener noreferrer"&gt;Google's recommendation architecture&lt;/a&gt; describes candidate generation as the first stage, where a huge corpus is reduced to a smaller group of potentially relevant candidates. &lt;/p&gt;

&lt;p&gt;Two-tower architectures are one example of how this can be implemented at scale. &lt;a href="https://docs.cloud.google.com/architecture/implement-two-tower-retrieval-large-scale-candidate-generation" rel="noopener noreferrer"&gt;Google Cloud describes using separate query and candidate representations&lt;/a&gt; so that candidate embeddings can be precomputed and retrieved efficiently at serving time. &lt;/p&gt;

&lt;h3&gt;
  
  
  Decisioning asks: "What should actually be shown?"
&lt;/h3&gt;

&lt;p&gt;Once the platform has, for example, 200 candidates, it can apply more sophisticated logic.&lt;/p&gt;

&lt;p&gt;The system might determine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Candidate A → organic position 1
Candidate B → organic position 2
Sponsored Candidate C → sponsored slot 3
Candidate D → organic position 4
Candidate E → organic position 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This final decision can involve many signals and constraints that don't belong in the retrieval layer.&lt;/p&gt;

&lt;p&gt;That distinction becomes especially important when monetization is involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ranking and Monetization Are Closely Connected
&lt;/h2&gt;

&lt;p&gt;Consumer platforms increasingly need to monetize the same surfaces that users depend on for discovery.&lt;/p&gt;

&lt;p&gt;A marketplace might sell sponsored product placements.&lt;/p&gt;

&lt;p&gt;A creator platform might offer promoted creator listings.&lt;/p&gt;

&lt;p&gt;A job board might sell sponsored job placements.&lt;/p&gt;

&lt;p&gt;A discovery platform might offer promoted products.&lt;/p&gt;

&lt;p&gt;This creates a fundamental tension.&lt;/p&gt;

&lt;p&gt;If sponsored content is handled by a completely separate advertising system, the application may effectively have two decision-makers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ranking engine&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which organic items should appear?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Ad system&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which sponsored items should appear?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application then has to merge the outputs.&lt;/p&gt;

&lt;p&gt;That architecture can become difficult to maintain because relevance, monetization, eligibility, and placement logic can evolve independently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://arxiv.org/abs/2607.14418" rel="noopener noreferrer"&gt;Research on sponsored-search systems&lt;/a&gt; also highlights why this is a genuine optimization problem. A 2026 field experiment on sponsored-search ad load found that increasing sponsored slots can increase revenue while also reducing conversions and engagement, with the trade-off varying by query and advertiser composition. &lt;/p&gt;

&lt;p&gt;The implication is straightforward: &lt;strong&gt;monetization cannot always be treated as an independent layer bolted onto ranking.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The platform needs a decision about the entire surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does a Decisioning API Look Like?
&lt;/h2&gt;

&lt;p&gt;A &lt;a href="https://gortex.ai/decision-engine" rel="noopener noreferrer"&gt;decisioning infrastructure&lt;/a&gt; provider can expose this capability through an API.&lt;/p&gt;

&lt;p&gt;For example, Gortex describes itself as a decisioning infrastructure layer for consumer platforms. Its API accepts a recipient, context, and candidate set through a single &lt;code&gt;POST /v1/decide&lt;/code&gt; endpoint and returns ranked items together with sponsored placements and decision identifiers. &lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /v1/decide

{
  "recipient": {
    "id": "user_8120"
  },
  "context": {
    "surface": "home_feed"
  },
  "items": [
    ...
  ]
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response can contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ranked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sponsored"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"decision_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important architectural idea is that the platform does not need to replace its existing retrieval infrastructure.&lt;/p&gt;

&lt;p&gt;It can continue generating candidates using its own systems and then send those candidates to a decisioning layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does Decisioning Infrastructure Actually Replace?
&lt;/h2&gt;

&lt;p&gt;It does not necessarily replace every component of a recommendation stack.&lt;/p&gt;

&lt;p&gt;A useful way to think about the architecture is:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Main responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data / events&lt;/td&gt;
&lt;td&gt;Collect behavioral and product signals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Candidate generation&lt;/td&gt;
&lt;td&gt;Find items that could be relevant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decisioning&lt;/td&gt;
&lt;td&gt;Determine what should actually be shown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monetization&lt;/td&gt;
&lt;td&gt;Determine eligible sponsored placements and commercial constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application&lt;/td&gt;
&lt;td&gt;Render the final experience&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The exact boundaries vary by company.&lt;/p&gt;

&lt;p&gt;Some platforms may already have excellent retrieval but lack a reusable ranking layer. Others may have ranking models but struggle to integrate sponsored inventory. Some may have separate systems for each surface.&lt;/p&gt;

&lt;p&gt;The value of decisioning infrastructure is therefore less about introducing "another recommender" and more about &lt;strong&gt;standardizing the final decision layer&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Latency Matters
&lt;/h2&gt;

&lt;p&gt;Decisioning happens directly on the user request path.&lt;/p&gt;

&lt;p&gt;If a consumer opens a feed and the decisioning service takes too long to respond, the latency becomes part of the product experience.&lt;/p&gt;

&lt;p&gt;This is why &lt;a href="https://docs.cloud.google.com/architecture/implement-two-tower-retrieval-large-scale-candidate-generation" rel="noopener noreferrer"&gt;production recommendation systems&lt;/a&gt; typically use multi-stage architectures. Retrieval reduces the candidate space first, allowing more sophisticated ranking to operate on a much smaller set. Google Cloud's reference architecture specifically describes reducing millions of candidates to hundreds before ranking them. &lt;/p&gt;

&lt;p&gt;A decisioning service therefore needs to operate within a predictable latency budget.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gortex.ai/" rel="noopener noreferrer"&gt;Gortex&lt;/a&gt; currently describes a target of &lt;strong&gt;under 200 ms p99 latency&lt;/strong&gt; and provides an API-based architecture designed to sit between candidate generation and the consumer-facing surface. Its website also lists seven SDKs and OpenAPI 3.1 as part of the integration architecture. &lt;/p&gt;

&lt;p&gt;For an engineering team, these characteristics matter because the decision layer needs to behave more like infrastructure than a dashboard or analytics tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explainability Becomes Part of the Infrastructure
&lt;/h2&gt;

&lt;p&gt;Ranking decisions can become difficult to debug.&lt;/p&gt;

&lt;p&gt;Suppose an item unexpectedly moves from position two to position twenty.&lt;/p&gt;

&lt;p&gt;An engineer may need to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which signals affected the ranking?&lt;/li&gt;
&lt;li&gt;Which rules were applied?&lt;/li&gt;
&lt;li&gt;Was the item eligible?&lt;/li&gt;
&lt;li&gt;Did a sponsored placement change the ordering?&lt;/li&gt;
&lt;li&gt;Which version of the decision logic produced the response?&lt;/li&gt;
&lt;li&gt;What happened during that particular request?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without decision-level logging, debugging can become difficult.&lt;/p&gt;

&lt;p&gt;Gortex includes a decision ID and trace ID in its API response and describes its architecture as providing a decision trace for understanding why items were placed where they were. &lt;/p&gt;

&lt;p&gt;This is particularly useful when ranking becomes part of the platform's core infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decisioning Infrastructure Is Not the Same as a Recommendation Engine
&lt;/h2&gt;

&lt;p&gt;The terms are related, but they are not identical.&lt;/p&gt;

&lt;p&gt;A recommendation engine primarily focuses on predicting which items a user may want.&lt;/p&gt;

&lt;p&gt;Decisioning infrastructure is broader.&lt;/p&gt;

&lt;p&gt;It can consume recommendations or retrieved candidates and then combine them with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context&lt;/li&gt;
&lt;li&gt;Rules&lt;/li&gt;
&lt;li&gt;Ranking&lt;/li&gt;
&lt;li&gt;Eligibility&lt;/li&gt;
&lt;li&gt;Monetization&lt;/li&gt;
&lt;li&gt;Placement constraints&lt;/li&gt;
&lt;li&gt;Business objectives&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/machine-learning/recommendation/dnn/scoring" rel="noopener noreferrer"&gt;Google's recommendation documentation&lt;/a&gt; itself emphasizes that scoring objectives matter because optimizing a single metric such as clicks can produce undesirable outcomes. &lt;/p&gt;

&lt;p&gt;A decisioning layer gives engineering teams a place to explicitly manage these competing objectives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Can Decisioning Infrastructure Be Used?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://gortex.ai/solutions" rel="noopener noreferrer"&gt;concept applies anywhere a platform&lt;/a&gt; has a set of candidates competing for limited user-facing slots.&lt;/p&gt;

&lt;h3&gt;
  
  
  Social feeds
&lt;/h3&gt;

&lt;p&gt;Rank posts, creators, communities, or other content while applying freshness, personalization, and placement rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Marketplaces
&lt;/h3&gt;

&lt;p&gt;Rank products or listings while considering relevance, seller quality, inventory, and monetized placements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creator platforms
&lt;/h3&gt;

&lt;p&gt;Rank creators, profiles, or content and optionally introduce sponsored discovery.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dating platforms
&lt;/h3&gt;

&lt;p&gt;Rank potential profiles using user and contextual signals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Job boards
&lt;/h3&gt;

&lt;p&gt;Rank jobs based on candidate relevance while supporting promoted or sponsored jobs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content platforms
&lt;/h3&gt;

&lt;p&gt;Rank articles, videos, podcasts, or user-generated content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Discovery products
&lt;/h3&gt;

&lt;p&gt;Rank products, services, locations, companies, or other entities where multiple candidates compete for visibility.&lt;/p&gt;

&lt;p&gt;Gortex explicitly positions its decisioning API for feeds, recommendations, marketplaces, content, personalization, and sponsored listings. &lt;/p&gt;

&lt;h2&gt;
  
  
  What Should Companies Look for in Decisioning Infrastructure?
&lt;/h2&gt;

&lt;p&gt;For teams evaluating this category, several capabilities are particularly important.&lt;/p&gt;

&lt;h3&gt;
  
  
  Low-latency serving
&lt;/h3&gt;

&lt;p&gt;The decision layer sits close to the user request, so predictable latency is critical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flexible candidate inputs
&lt;/h3&gt;

&lt;p&gt;Teams should be able to use their existing retrieval systems rather than rebuild their entire recommendation stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ranking flexibility
&lt;/h3&gt;

&lt;p&gt;Different surfaces need different objectives. A marketplace, feed, and job board should not necessarily use the same ranking strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monetization support
&lt;/h3&gt;

&lt;p&gt;If sponsored inventory is part of the product roadmap, the infrastructure should support it without forcing the application to maintain a completely separate decision path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Observability
&lt;/h3&gt;

&lt;p&gt;Decision IDs, traces, logs, and reproducibility make ranking systems significantly easier to debug.&lt;/p&gt;

&lt;h3&gt;
  
  
  API-first integration
&lt;/h3&gt;

&lt;p&gt;A decisioning layer should fit into existing engineering infrastructure rather than require a complete platform migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Versioning and experimentation
&lt;/h3&gt;

&lt;p&gt;Ranking logic changes frequently. Teams need to understand which version produced a particular decision and safely test changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gortex as a Decisioning Infrastructure Layer
&lt;/h2&gt;

&lt;p&gt;For consumer platforms that already have candidate-generation infrastructure but do not want to build and maintain another ranking and monetization layer internally, Gortex represents one approach to this architecture.&lt;/p&gt;

&lt;p&gt;Gortex describes is &lt;strong&gt;the layer between candidate generation and what users see&lt;/strong&gt;. Its single decision endpoint is designed to rank candidates and fill sponsored slots within the same response. &lt;/p&gt;

&lt;p&gt;The distinction is important.&lt;/p&gt;

&lt;p&gt;A company does not necessarily need to throw away its existing search, vector database, recommendation engine, or candidate-generation pipeline.&lt;/p&gt;

&lt;p&gt;Instead, the architecture can look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Request
     ↓
Existing Retrieval / Candidate Generation
     ↓
       Gortex
     ↓
Ranking + Decisioning
     ↓
Sponsored Slot Decision
     ↓
Decision Trace
     ↓
Consumer Surface
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes Gortex particularly relevant for engineering teams building products where &lt;strong&gt;ranking is becoming infrastructure rather than an isolated feature&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.g2.com/sellers/gortex" rel="noopener noreferrer"&gt;G2 similarly describes Gortex&lt;/a&gt; as a ranking and monetization API for consumer platforms, including social feeds, marketplaces, creator platforms, dating apps, and job boards. &lt;/p&gt;

&lt;p&gt;Gortex is listed as being in private beta, so its capabilities and production availability should be evaluated directly with founder before adopting it for a production workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future of Consumer Platform Decisioning
&lt;/h2&gt;

&lt;p&gt;The underlying trend is bigger than recommendation systems alone.&lt;/p&gt;

&lt;p&gt;Consumer platforms are moving from simple "sort this list" experiences toward complex decision systems where relevance, personalization, business objectives, monetization, and policy all influence what users see.&lt;/p&gt;

&lt;p&gt;The architecture increasingly resembles:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieve → Rank → Apply Constraints → Monetize → Explain → Learn&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is why decisioning deserves to be treated as its own infrastructure layer.&lt;/p&gt;

&lt;p&gt;The retrieval system determines the possibilities.&lt;/p&gt;

&lt;p&gt;The decisioning layer determines the outcome.&lt;/p&gt;

&lt;p&gt;For small products, that logic may live inside a few application functions. For a growing consumer platform, it can become a critical piece of infrastructure that deserves dedicated APIs, latency guarantees, observability, versioning, and clear ownership.&lt;/p&gt;

&lt;p&gt;That is the problem space decisioning infrastructure is designed to address.&lt;/p&gt;

&lt;p&gt;Gortex fits into this emerging layer by providing an API for ranking candidates, handling sponsored placements, and returning decision-level information without requiring consumer platforms to build the entire ranking-and-monetization layer from scratch.&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
  </channel>
</rss>
