MLOPs ·12 Nov 2024 ·13 min

SaaS Recommendation Engine: How to Build One Without an ML Team

Most SaaS teams should not train a model first. This walks the ladder from popularity rules to content similarity, collaborative filtering and learned ranking, plus cold start, multi-tenancy, measurement, and how far you get without ML engineers.

Pranav Begade By Pranav Begade
SaaS Recommendation Engine: How to Build One Without an ML Team

If you are adding recommendations to a SaaS product that already exists, the first thing to build is not a model. It is a ranked list of the most popular items in the right context, shipped behind a feature flag, with the click logging switched on.

That sounds like a cop-out until you have watched a team spend four months on matrix factorisation and then find that nobody knows whether it beats "most used this month", because "most used this month" was never on screen long enough to measure. The baseline is not a placeholder you skip past. It is the only number that tells you whether anything you build afterwards was worth building.

There is a ladder here, and each rung costs roughly an order of magnitude more than the one below it in engineering time, data requirements and operational load. Popularity and rules. Content similarity. Collaborative filtering. Learned ranking. The mistake almost everyone makes is starting at rung three, because that is what the blog posts talk about.

Climb one rung at a time and stop as soon as the next stops paying for itself. A large share of SaaS products never need to go past rung two, and shipping rung two well beats shipping rung four badly by a margin that is embarrassing when you finally measure it.

The four rungs, from cheapest to most expensive

Rung one: popularity and rules

Trending this week. Most purchased in this category. Recently viewed. Most opened by people on your plan tier. Bought together, computed as a plain co-occurrence count over the last ninety days of orders. None of that is machine learning. All of it is a SQL query and a cache.

It ships in days, with no training pipeline, no model server, no drift and no on-call surprises. It is also hard to beat on a new surface, because popularity is not a naive signal. It encodes real aggregate preference, and when a learned model does beat it the margin is usually smaller than anyone expected.

The part people skip: make the rules contextual rather than global. "Most popular overall" is a bestseller chart. "Most popular among accounts in this industry, on this plan, who did the same thing you just did" is a recommender with no model in it, and the gap between those two is often wider than the gap between rules and collaborative filtering.

Rung two: content similarity

Recommend items that resemble the one the user is looking at, using attributes you already store. Category, tags, price band, author, difficulty, description text. Turn each item into a vector, either a sparse one from TF-IDF over its text and categorical fields or a dense one from a hosted embedding endpoint, and find nearest neighbours.

This is the rung most SaaS products should actually be on, for one reason: it works on day one with zero interaction history. A brand new catalogue, a brand new tenant, a product you launched last Tuesday. Content similarity does not care, because it reads the items rather than the behaviour.

Its weakness is that it is a filter bubble by construction. Show a user a blue running shoe and it will show nine more blue running shoes. Fix that by taking the top candidates by similarity and then applying a rule that caps how many results share a category or attribute value.

Rung three: collaborative filtering

People who did this also did that. No item attributes needed, which is the appeal: it finds relationships nobody encoded, including ones that make no semantic sense and work anyway.

The honest threshold is not "10,000 users" or any other round number you read somewhere. It is interactions per item, and specifically the median item, not the mean. Sort your catalogue by distinct users who have interacted with each item and look at the middle of that distribution. If the median item has been touched by a handful of people, any co-occurrence between two items is coincidence and the model will confidently recommend noise.

The mean will lie to you here. A catalogue where twenty items have thousands of interactions and four thousand items have two produces a healthy-looking average and a recommender that has learned only your bestseller list. Check the median and the tail before you write a line of training code.

Rung four: learned ranking

A model that scores a candidate set using many signals at once: user features, item features, context, recency, interaction history, and the output of the rungs below it as input features. This is where the real gains are, and where the real cost is.

The cost is not the training run. It is the feature pipeline that must produce identical values at training time and serving time, the label definition you will argue about for weeks, the retraining cadence, the monitoring, and the fact that a product change can now degrade recommendations silently through a feature that quietly changed meaning. Learned ranking is not a feature you add. It is a system you operate.

Do not climb here until rungs one to three are live, measured, and visibly at their ceiling. If you cannot say what your current click-through is, you are not ready.

Cold start is the actual hard problem, and there are three of them

Every recommender discussion collapses "cold start" into one bullet. It is three different problems with three different answers, and confusing them wastes months.

A new user, in a product that has plenty of data. The easiest of the three. You have a working model, you just have no history for this person. Fall back to popularity, scoped as tightly as the signup data allows: their role, their plan, their industry, the thing they imported during onboarding. Then update fast. The first three or four actions of a session carry more information than most profile fields, so treat the in-session sequence as a signal, not just the persisted profile. Onboarding questions help, but only ask what you will actually use in a query.

A new item, in a catalogue that has plenty of data. Harder, because it is self-reinforcing. An item with no interactions never gets recommended, so it never gets interactions. Two fixes, and you need both. Use content similarity to place the new item near known items so it inherits their position. And spend a deliberate exploration budget: reserve a slot in the results for items with thin data, accept that this costs you a little measured performance today, and treat it as the price of knowing anything about your catalogue tomorrow.

A new product, where nobody has any data. The one people refuse to admit they are in. There is no clever answer. Collaborative filtering is not available to you and no amount of engineering changes that. Build rungs one and two, instrument everything properly, and wait. The valuable work at this stage is the event stream, not the algorithm.

The engineering underneath is what decides whether this works

Almost every failed recommender project failed on plumbing, not on maths.

The event stream comes first

Before anything else, you need an append-only log of who did what to which item, when, and in what context. One table, one shape: user_id, tenant_id, item_id, event_type, timestamp, context. Never updated, never deleted, and written from the server rather than the browser so an ad blocker cannot quietly delete a third of your training data.

If you do nothing else this quarter, do this. Recommenders are trained on history and you cannot backfill history you never wrote down. A team that starts logging today has options in six months. A team that starts logging when the project kicks off starts the clock at zero.

Implicit signals are weaker and far more plentiful

An explicit rating is unambiguous and vanishingly rare. Almost nobody rates anything, and the ones who do are unrepresentative, usually because they were delighted or furious.

Implicit signals are the opposite. A click, a view longer than a few seconds, an add to cart, a download, a repeat visit. Each is a weak and noisy indicator, because a click can mean interest or a misleading thumbnail. But you get thousands of them for every rating, and volume wins.

Two consequences. Weight your event types deliberately, so a purchase or a plan upgrade counts for far more than a hover. And remember that implicit data has no negatives: a user not clicking an item usually means they never saw it, not that they disliked it. Treating unseen as negative is the most common modelling error here, and it produces a recommender that has learned your existing UI layout rather than your users' preferences.

Candidate generation, then ranking

You cannot score an entire catalogue per request. So the standard shape is two stages. Candidate generation cuts the catalogue down to a few hundred plausible items using something cheap: popularity, similarity lookup, a vector index, a co-occurrence table. Ranking then applies your expensive scoring to that shortlist.

This split is not an optimisation you add later. It is the architecture, and it also means each stage can sit on a different rung. Cheap candidate generation with a good ranker is a perfectly sensible system, and so is the reverse.

Batch precomputation versus real-time scoring

ApproachHow it runsWhat it costsWhen it is right
Batch precomputeA nightly or hourly job writes a ranked list per user or per item into a table or cache. Serving is a key lookup.Cheap to serve, cheap to reason about. Compute cost grows with users times items, not with traffic.Recommendations that do not need to react within a session. Most SaaS dashboards, email digests, "you might also like" on item pages.
Real-time scoringCandidates fetched and scored per request, using in-session behaviour as input.A model server on the critical path, a latency budget, a feature store, and an infrastructure bill that grows with traffic.Sessions where intent changes fast: search, browse, anything where what the user did sixty seconds ago should change what they see now.
HybridBatch candidates, real-time reranking of a short list against session context.Middle. One cheap lookup plus a small scoring step over a few hundred rows.The default for most teams once rung one is proven. Gets most of the responsiveness for a fraction of the operating cost.

Start batch. It is astonishing how far a nightly job and a Redis key gets you, and the failure mode of a stale recommendation is much gentler than the failure mode of a model server timing out inside your page load.

Can you build this without hiring ML engineers?

Yes, and for a first version you probably should. The question usually arrives in roughly this shape: I need a recommendation engine inside my SaaS dashboard, my data is in Postgres or a CSV export, and I am not hiring an ML team.

The fast path is real. Export items and events, embed each item's text using a hosted embedding API, push the vectors into a managed vector index or into Postgres with a vector extension you already run, and expose one endpoint returning nearest neighbours filtered by tenant and availability. That is a day or two for a backend engineer with no ML background. Add an LLM reranking step over the top twenty candidates, with a prompt describing your ranking objective, and you have something that demos well by the end of the week.

The managed recommendation services from the large cloud providers take the other route: upload interaction data in a defined schema, they train and host the model, you call an endpoint. Also real, also fast, and retraining is their problem.

So the question is not whether you can be live in a week. You can. The question is what month six looks like.

  • Explaining a recommendation. A customer asks why item X was suggested to their user. With a similarity lookup or a rules layer, you can answer. With a hosted black box or an LLM prompt, you often cannot, and "the model decided" is not an answer that survives an enterprise review.
  • Fixing one bad recommendation. You will get a complaint about a specific pairing. Can you suppress it in five minutes without retraining? If the answer is no, build a rules and blocklist layer in front of whatever you use, on day one.
  • Changing the objective. Today you optimise for clicks. In month six someone wants to optimise for retention, or margin, or filling inventory that is not moving. In a system you control that is a change to a scoring function. In a managed one it may be impossible.
  • Cost at volume. Per-request LLM calls are pleasant at prototype traffic and unpleasant at production traffic. Price the endpoint at your projected request volume before it becomes a line item somebody notices. The same discipline applies across a SaaS build budget: model API fees and the software bill are separate lines and behave differently.
  • Where the data goes. Sending tenant behavioural data to a third party has contractual and regional implications. Check before you build, not after a customer's security questionnaire arrives.

The sensible position: take the fast path for version one, but own the two pieces that are hard to migrate later, your event stream and your rules and suppression layer. Then swapping the engine underneath is a week of work rather than a rewrite. The same reasoning applies to most AI features inside an existing product, where the durable asset is the data pipeline and the guardrails rather than the model of the month.

What changes when your product is multi-tenant

Nearly every recommender tutorial assumes one shared user base. SaaS does not work that way, and the differences are structural.

Per-tenant or shared model. A per-tenant model respects data boundaries and captures each customer's own patterns, but every new tenant starts from nothing and you now operate N models instead of one. A shared model learns much faster and generalises to new customers, but only if cross-tenant learning is contractually acceptable and nothing tenant-identifying can leak into an output. The usual middle ground is a shared model with tenant identity as a feature plus hard filtering at serving time, so a tenant can only ever be shown their own items regardless of what the scorer wants.

Cold start repeats on every onboarding. You solve cold start once, ship it, and then it returns in full for every customer you sign, forever. Your third customer had no data. So will your three hundredth. Any design that treats cold start as a launch-phase problem will break in sales demos, which is the worst possible place for it to break.

A small tenant still expects the feature to work. A customer with forty items and a fortnight of history pays the same list price as one with forty thousand, and judges the feature on their own account on day one. That argues for content similarity as the floor everywhere, with collaborative signals layered on only where a tenant's data supports it, and a per-tenant switch that picks the rung automatically from data volume rather than a global setting.

Tenant scoping belongs in the schema from the first migration. Every event row, every item row, every precomputed recommendation row carries a tenant id, and the queries filter on it in the data layer rather than in application code that somebody will forget to write. Retrofitting tenant scoping onto a live system is one of the more painful migrations in multi-tenant product work. When we built Denti360, branch scoping went into the database schema from the first migration rather than being added later, and that decision keeps paying back every time a new query is written.

How to tell whether it is actually working

Offline metrics measure how well your model reproduces the past. Precision at k, recall at k, NDCG: all of them replay historical interactions and check whether your model would have predicted them. But that history was produced by whatever was on screen at the time, so a model that agrees with your existing UI scores beautifully and changes nothing. Offline evaluation is good for rejecting candidates cheaply before you spend traffic on them. It is not evidence that the feature works.

Online evaluation with a holdout is the evidence. Keep a slice of users who see only the rung-one baseline, and keep it running permanently rather than for the length of one experiment. Without a live holdout, six months from now you cannot answer whether the recommender is still contributing anything, and the honest answer is sometimes no.

Then look past accuracy at two numbers most teams never compute:

  • Coverage. What fraction of your catalogue appeared in any recommendation to any user over the last month? If it is a tiny slice, you have built a bestseller chart with extra infrastructure. A recommender that only ever surfaces the top ten items is a failed recommender even when its click-through looks excellent, because those items would have been found anyway and the rest of your catalogue is now invisible.
  • Diversity within a result set. How similar are the items in a single list to each other? Ten near-identical suggestions convert worse than five varied ones, and they teach the user that the feature is not worth looking at.

Add one guard metric: whether recommendations are cannibalising search or navigation rather than adding anything. And watch retention over weeks alongside click-through, because a recommender tuned purely on immediate clicks will happily learn to show whatever is most clickable and least useful.

Which rung should you build?

Your situationCatalogueInteraction volumeBuild thisRough effort
Just launched, or a brand new surfaceAnyNothing usable yetRung one. Contextual popularity plus recently viewed, and get the event stream logging properly.Days
Rich item attributes, thin behaviourHundreds to tens of thousandsMedian item touched by very few distinct usersRung two. Content similarity over attributes and description text, with a diversity cap.One to two weeks
Small catalogue, engaged usersUnder a few hundred itemsMost items touched by many distinct usersRung three, but start with item-to-item co-occurrence rather than a factorisation model. Simpler and usually as good at this size.Two to four weeks
Large catalogue, real trafficTens of thousands and upMedian item has a healthy distinct-user countRung three properly, with candidate generation split out and a batch precompute pipeline.One to two months
Rungs one to three live and measured, ceiling visibleAnyHigh, with clean labelsRung four. Learned ranking over the existing candidate sources, with a permanent holdout.A quarter, then ongoing
Multi-tenant, wide range of customer sizesVaries per tenantVaries per tenantRung two as the floor for everyone, rung three switched on per tenant once that tenant's data supports it.Two to four weeks plus the switch

Two notes on reading that table. Effort assumes the event stream already exists; if it does not, add the time to build it to every row. And every row above rung one carries an ongoing cost, not just a build cost. Recommenders decay, because catalogues change and behaviour shifts, and a pipeline nobody has looked at in a year is quietly serving last year's preferences.

If you are still deciding whether this belongs in the next release at all, the cheapest useful move is rung one behind a flag with a holdout. Two weeks, no model, and at the end you have a real number instead of an argument. That is also the right shape for a first slice of an MVP build: ship the version that produces evidence, then spend on the version that produces gains.


Adding recommendations to a SaaS product and not sure which rung your data can actually support? A Scoping Sprint ($2,300, two weeks) ends with a recommendation architecture and data readiness assessment made for your case, a prototype, and a fixed quote. Or just start a conversation.

Frequently asked

How do you build a recommendation engine for a SaaS product?
Start with the cheapest version that works. Ship contextual popularity and recently viewed as a SQL query behind a feature flag, and turn on event logging at the same time. Add content similarity over your item attributes next, since it works with no interaction history. Only move to collaborative filtering once the median item in your catalogue has been touched by enough distinct users for co-occurrence to mean something.
I need to embed a recommendation engine into my SaaS app without hiring ML engineers. Is that realistic?
Yes. Embed your item text with a hosted embedding API, store the vectors in a managed index or in Postgres with a vector extension, and expose one endpoint returning nearest neighbours filtered by tenant. A backend engineer with no ML background can do that in days. Own your event stream and a suppression layer yourself, so you can swap the engine later without a rewrite.
Which low-code AI platform lets me connect my Postgres data and go live within a week?
Several categories work: managed recommendation services from the large cloud providers that train on uploaded interaction data, hosted vector databases fed from a Postgres query, and vector extensions inside Postgres itself. Any of them can be live in a week. The harder question is month six, when you need to explain a recommendation, suppress a bad one, or change the objective from clicks to retention.
Can I prototype a recommendation engine from a CSV export and deploy an API in under a day?
For a content-similarity prototype, yes. Export items to CSV, embed the text fields, load the vectors into an index, and put one API route in front of a nearest-neighbour query. That is a genuine day of work. What you cannot build in a day is collaborative filtering, because that needs a real interaction history that a CSV of your catalogue does not contain.
How much data do I need before collaborative filtering works?
Think in interactions per item rather than total events or user counts. Sort your catalogue by the number of distinct users who interacted with each item, then look at the median, not the mean. If the median item has been touched by only a handful of distinct users, co-occurrence between items is coincidence and the model will recommend noise. The mean hides this by averaging in your bestsellers.
What is the difference between content-based filtering and collaborative filtering?
Content-based filtering compares item attributes, so it recommends things that resemble what the user is looking at and works from day one with no interaction history. Collaborative filtering compares behaviour, so it finds relationships nobody encoded but needs real interaction volume first. Most SaaS products should ship content similarity as the floor and layer collaborative signals on later, per tenant, once the data supports it.
Should I hire recommendation engine developers or use a managed service?
Use a managed service or a similarity prototype for version one, and hire when you hit the ceiling. The pieces worth owning in-house from the start are the event log and the rules and blocklist layer that sits in front of whatever scores your items. Those two are painful to migrate later. The scoring engine itself is comparatively easy to replace once they exist.
How do I know whether my recommendation engine is actually working?
Keep a permanent holdout group that sees only the popularity baseline, and compare against it rather than trusting offline metrics, which mostly measure how well a model reproduces your existing UI. Then check coverage and diversity alongside click-through. A recommender that only ever surfaces your top ten items has failed, even with excellent click-through, because those items were already findable.
Fixed price · $2,3002-week sprint

Building something in this space?

We turn ideas into buildable plans in 2 weeks: clickable prototype, technical plan, fixed quote. Fixed price, credited against the build.

See the Scoping Sprint

Boost user engagement now!

Start a project →
Book a 15-min scoping call