SevyDevy

Guide · AI and GenAI

The Cold Start Problem in Recommendation Systems

24 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • The cold start problem is when a recommender has too little history to make good picks. It hits new users, new items such as a just-listed restaurant, and whole new cities.
  • Many recommenders lean on collaborative filtering: they suggest what people with similar past taps and orders liked. With no history to compare, their picks for that user or item are close to guesses.
  • For new users, start with what is popular near them at that hour, ask one or two quick questions, and learn fast from their first taps.
  • For new items, guess from content features, facts like cuisine and price, then give them a small, capped share of views. For a new city, reuse a model built on features that work anywhere.
  • Beginners can stop after the new-city section. Engineers get a runnable check of a bandit, a picker that learns from each result. They also see why a plain one lets a brand-new restaurant beat a proven 5% one 95% of the time, and how to test fairly on old logs.

What goes wrong when a recommender meets a new user, item or city?

It guesses. That is the cold start problem: a recommender learns from past taps and orders, and a new user, item or city has none. The feed falls back to the same big names for everyone.

Picture a common setup. A food delivery app runs in Pune, Indore and Nagpur and opens in Bhubaneswar with 420 restaurants. The setup is made up, but the problem is real.

The home feed is the list of restaurants you see when you open the app. In the older cities, 6 of every 100 feed visits end in an order. In Bhubaneswar's first two weeks, only 2 do.

The team reads the logs and finds three separate problems hiding inside that one bad number. Each one is a different kind of missing history.

  • New users. About 18,000 people signed up in two weeks. Most have zero orders, so the model knows nothing personal about them.
  • New restaurants. A small Odia place in Saheed Nagar makes excellent dalma, a lentil and vegetable stew, and chhena poda, a baked cheese dessert. It has never been ordered, so the feed never shows it, so it is never ordered.
  • A new city. The model learned that Indore orders poha, flattened rice, at breakfast. It has no idea what Bhubaneswar wants at 8 in the morning.

The second problem is the cruellest. The feed hides the restaurant because it has no orders, and it has no orders because the feed hides it. Breaking that loop is a big part of this guide.

Each problem has its own cause and its own fixes. The guide walks through all three on this one app, with the code the team runs and the numbers it watches.

Which words do you need before fixing cold start?

You need four words: interaction, collaborative filtering, embedding and content features. Each gets a plain meaning and an example from the app.

Interaction. An interaction is any action a user takes on an item: a view, a tap, an order or a rating. Example: a user in Patia opens a biryani restaurant's page and orders from it.

Collaborative filtering. Collaborative filtering recommends what similar users liked, using only interactions. If many people who order from restaurant A also order from restaurant B, a new fan of A will probably like B.

Embedding. An embedding is a list of numbers that stands for a user or an item, usually learned from interactions. Users and restaurants with similar tastes end up with similar numbers. Without interactions, there is nothing to learn the numbers from.

Content features. Content features describe a user or item without needing any interactions. For a restaurant, they are things like cuisine, price per plate, veg or non-veg, menu text and area.

What is the cold start problem in recommendation systems?

The cold start problem is when a recommender system has too little interaction data about a user, an item or a whole market to make useful picks. Cold means little or no history. Warm means plenty of it.

A 2025 survey of cold-start research describes the task as modelling new or interaction-limited users or items. The second part matters. A user with two orders is not fully cold, but the model still knows very little.

The same survey separates strict cold start, with zero interactions, from normal cold start, with fewer than a set number. It also names system cold start, where a whole new platform has sparse data.

Bhubaneswar has all three kinds at once: new users, new restaurants and a new market. The diagram shows how each reaches the same model.

Three kinds of cold start in a new city

Notice that all three gaps feed the same model. Each starves it of a different kind of history, so each needs its own fix.

Diagram connections:

  • New user to Home feed model (No personal history)
  • New restaurant to Home feed model (No order history)
  • New city to Home feed model (No local history)
  • Home feed model to Feed of near-guesses
  • New user: Signed up today, zero orders
  • New restaurant: Joined last week, never ordered from
  • New city: No local order history at all
  • Home feed model: Learned from orders in other cities
  • Feed of near-guesses: Same big chains for everyone

The three kinds need different fixes. A new user can tell you things in seconds. A new restaurant cannot, but its menu can. A new city needs both, plus patience.

Why can't collaborative filtering recommend for new users and new items?

Collaborative filtering cannot help new users or items because it learns only from interactions, and they have none. With no interactions, the model has no learned embedding for them, so its scores for them mean almost nothing.

Think about how the app's model was trained. It saw millions of orders from Pune, Indore and Nagpur and learned an embedding for each user and restaurant there. A Bhubaneswar restaurant never appeared, so it has no embedding or only a random starting one.

There is a trap inside this too. Items that already have orders get shown more, so they get more orders. New items get fewer views, so they stay new.

Researchers call the wider pattern popularity bias. A 2019 paper on it describes popular items being recommended frequently while niche items are recommended rarely or not at all. Without a deliberate fix, new restaurants can stay locked out.

One picture for the whole playbook

Picture a Kathputli troupe, a family of string-puppet performers from Rajasthan, arriving in a village they have never played. Nobody there has watched them before. A sensible troupe does not freeze.

Each part of what the troupe does maps to one fix in this guide:

  • It opens with crowd favourites, the tales nearly every audience enjoys. That is showing what is popular nearby at that hour.
  • It watches which scenes get cheers and adjusts. That is learning from a new user's first taps.
  • It slips one new tale between two favourites. That is a capped exploration slot for a new restaurant.
  • It chats with the village elders before the first show. That is building starter lists from local restaurant partners and riders.

Where the picture breaks: a troupe plays to one crowd and sees every face. A feed serves thousands of people at once, sees only taps and orders, and orders arrive minutes after the view.

How do you recommend to a new user with no history?

You recommend to a new user by starting from what you do know: where they are, what time it is and what is popular nearby. Then you ask a little and learn quickly from their first taps.

Before a new user has ordered anything, the team already has these signals:

  • Delivery area. Patia's office crowd orders differently from Old Town's families, so popularity is counted per area.
  • Time of day. Breakfast, lunch, evening snacks and dinner each get their own popular list.
  • Onboarding answers. Two taps at signup: veg or non-veg, and up to three favourite cuisines.
  • First actions. The first search, the first restaurant opened, even the first scroll past a category.

Notice what is missing from that list. The team does not guess a user's religion, caste or income from their name or phone. Those guesses are invasive, often wrong, and a privacy risk.

The next idea is blending. A user with zero orders should see mostly area favourites, and a user with fifty orders mostly personal picks. In between, the feed should slide smoothly from one to the other.

Here is one simple blend. The helpers are stand-ins: model_score is the personal model's score, and area_popularity is the restaurant's order share in that area at that hour.

python
def blended_score(user, restaurant, hour, k=5):
    n = user.order_count
    trust = n / (n + k)            # 0 when new, close to 1 later
    popular = area_popularity(restaurant, user.area, hour)
    if n == 0:
        return popular
    personal = model_score(user, restaurant)
    return trust * personal + (1 - trust) * popular

The line that matters is trust = n / (n + k), where n is the user's order count. With k set to 5, a new user gets 0 trust in the personal model. The constant k is the order count at which personal and popular get equal weight.

Within a single session, things can move even faster. If a new user opens two biryani places in a row, the next screen can lean towards biryani right away. You do not need to wait for an order to learn something.

How do you recommend a new item that has no ratings?

You recommend a new item by predicting its score from its content features. Then you give it a small, capped share of views so real feedback can replace the prediction.

A new restaurant cannot tell you its taste, but its listing can. When the Saheed Nagar place joins, its owner fills in cuisine, price per plate, veg or non-veg, opening hours and a full menu.

The team trains a small content model on the warm restaurants in older cities. Its input is a restaurant's content features, and its target is that restaurant's learned embedding. It might learn, say, that Odia and Bengali home-style menus sit close together.

For a new restaurant, the content model predicts an embedding from the listing alone. The ranking model can then score it like any other restaurant. The guess is rough, but it beats a random starting point.

Research takes a related route. DropoutNet, a 2017 paper, feeds one network both the embedding from an existing model and content features. In training it sets the embedding input to zero for some users and items, so the network learns to score from content alone.

The diagram shows the path a new restaurant takes. That loop is how a predicted embedding slowly becomes a learned one.

How a new restaurant gets into the feed

Follow the loop from the listing back to the ranker. Content gives the first guess; orders from capped exploration slots replace it.

Diagram connections:

  • Restaurant listing to Content model
  • Content model to Ranking model (First guess)
  • Ranking model to Exploration slots
  • Exploration slots to Views and orders log (Real feedback)
  • Views and orders log to Learned embedding
  • Learned embedding to Ranking model (Replaces the guess)
  • Restaurant listing: Cuisine, price, menu, photos, area
  • Content model: Predicts an embedding from the listing
  • Ranking model: Scores every restaurant for a user
  • Exploration slots: At most 1 in 10 feed positions
  • Views and orders log: Who saw it, who tapped, who ordered
  • Learned embedding: Takes over as real orders arrive

Content has limits. Two restaurants with the same menu can differ hugely in taste, portion size and delivery time. Only orders, repeat orders and ratings reveal that.

How does exploration help with the cold start problem?

Exploration helps because it deliberately shows items the system is unsure about, so it can learn how good they are. Without it, new items never collect the feedback they need to stop being new.

Every recommender faces the explore-exploit trade-off. Exploit means showing what you already believe is best. Explore means trying something uncertain to learn about it. Pure exploiting keeps the feed safe today but blind tomorrow.

A method that keeps choosing among options and learns from each result is called a bandit. The simplest one is epsilon-greedy: show the best-scoring item most of the time, and a random one, say 1 time in 10.

Epsilon-greedy works, but it explores blindly. In our simulation with 10% random picks and three made-up restaurants, it gave a clearly bad 0.5% restaurant about 670 of 20,000 views. Thompson sampling gave it about 100.

Thompson sampling is a smarter bandit. A 2017 tutorial frames the task as balancing exploiting what is known against investing in information that may improve future performance. Thompson sampling keeps a range of plausible order rates per item, then picks with a random draw from each.

An item with few views has a wide range, so it sometimes draws a high number and gets shown. An item with many views has a narrow range near its true rate. Uncertain items get tried, and proven ones get trusted.

Here is how the team fills one exploration slot among new restaurants. Each candidate has two counts: shown, how many times it appeared in a feed, and orders, how many of those views became orders.

python
import random

def pick_for_exploration_slot(candidates):
    best, best_draw = None, -1.0
    for r in candidates:
        misses = r.shown - r.orders
        draw = random.betavariate(1 + r.orders, 1 + misses)
        if draw > best_draw:
            best, best_draw = r, draw
    return best

The key line is random.betavariate(1 + r.orders, 1 + misses). A beta distribution says how likely each value between 0 and 1 is, here each possible order rate. A restaurant with 0 views draws anywhere from 0 to 1.

One with 400 views and 20 orders lands between about 0.03 and 0.08 in 95 of 100 draws. Its range has narrowed around its observed rate of 5%.

Real systems often use context too. A 2010 paper treated the story slot of the Today Module on the Yahoo! front page as a contextual bandit, one that also reads user and article features. It reported a 12.5% click lift over a context-free epsilon-greedy bandit, measured offline on logged random traffic with the replay method explained below.

How do you solve cold start when you launch in a new city?

You solve a new-city cold start by reusing a model trained on similar cities and seeding the feed with local favourites. Then you explore more than usual, and shift weight to local data as orders arrive.

The first trick is to make the model portable. A model that leans on restaurant IDs from Pune cannot say anything about Bhubaneswar. A model that leans on cuisine, price band, distance, delivery time and hour of day can.

The team rebuilt the ranker this way months before launch. The question it learns becomes general: do people who like cheap South Indian breakfasts within 3 km also like this place? The 2025 survey covers this as transferring knowledge from a warm source domain to a cold target domain.

The second trick is local knowledge, the troupe chatting with the elders. The city team talks to restaurant partners and delivery riders before launch. They might hear that pakhala, a fermented rice dish, sells in summer, and that Patia's offices want lunches under Rs 150.

These answers become hand-made starting lists for each area and hour. The diagram shows where each source of knowledge enters the Bhubaneswar feed.

How the Bhubaneswar feed starts

Notice that the local order log feeds both the starter lists and the shared model. Borrowed patterns fill the gap until local orders take over.

Diagram connections:

  • Orders from older cities to Shared ranking model (Trains)
  • Shared ranking model to Bhubaneswar feed builder
  • Local starter lists to Bhubaneswar feed builder (Area and hour lists)
  • Bhubaneswar feed builder to Bhubaneswar users
  • Bhubaneswar users to Local order log (Taps and orders)
  • Local order log to Shared ranking model (Retrains weekly)
  • Local order log to Local starter lists (Replaces hand-made lists)
  • Orders from older cities: Pune, Indore and Nagpur history
  • Shared ranking model: Uses cuisine, price, distance, hour
  • Local starter lists: From restaurant partners and riders
  • Bhubaneswar feed builder: Blends model, lists and exploration
  • Bhubaneswar users: New users, new restaurants
  • Local order log: Grows every day after launch

Here is the launch plan the team wrote for the next city, based on what Bhubaneswar taught it.

  1. Before launch: make the model portable

    Check that the ranker scores well on an older city it never trained on. If it fails there, it will fail in the new city too.

  2. Before launch: build starter lists

    Ask restaurant partners and riders what each area eats at each hour. Turn their answers into short ranked lists.

  3. Week 1: explore more than usual

    Raise the exploration share for new restaurants, but keep the cap. Watch ratings and cancellations daily.

  4. Weeks 2 to 6: retrain on local orders

    Add the city's own orders to training every week. Let blended scores shift users from area lists to personal picks.

  5. After that: treat it like any city

    Lower exploration to the normal level, retire the hand-made lists, and keep tracking new users and new restaurants separately.

The table compares every fix in this guide. Most teams use several at once, because each covers a different gap.

Cold start fixes compared on the food delivery feed
FixHelps withMain costMain risk
Popular by area and hourNew users, new cityCheap counting jobSame feed for everyone nearby
Onboarding questionsNew usersA few seconds of the user's timePeople quit before ordering
Blending by order countUsers with little historyOne tuning constantTrusting the model too early or late
Content modelNew restaurantsTraining a second modelSame menu, very different food
Exploration slotsNew restaurantsSome views spent on unknownsUsers see a bad new place
Shared model across citiesNew cityRebuilding features to be portableHabits that do not transfer

How do you check that Thompson sampling is learning?

Run the exploration picker on made-up restaurants whose true order rates you know, and count where the views go. The best restaurant should collect most of them.

This script uses the same picker as above, standard library only. Restaurant A converts 8% of views, B 4% and C 1%. The picker never sees those numbers, only the counts.

python
import random
from types import SimpleNamespace

def pick_for_exploration_slot(candidates):
    best, best_draw = None, -1.0
    for r in candidates:
        misses = r.shown - r.orders
        draw = random.betavariate(1 + r.orders, 1 + misses)
        if draw > best_draw:
            best, best_draw = r, draw
    return best

random.seed(7)
true_rate = {"A": 0.08, "B": 0.04, "C": 0.01}   # made up, hidden from the picker
places = [SimpleNamespace(name=n, shown=0, orders=0) for n in true_rate]

for _ in range(3000):                            # 3,000 exploration slots
    r = pick_for_exploration_slot(places)
    r.shown += 1
    if random.random() < true_rate[r.name]:
        r.orders += 1

for r in places:
    print(r.name, "shown", r.shown, "orders", r.orders)

On Python 3.11, 3.12 and 3.13 it printed the same three lines when we checked on 19 October 2026. A was shown 2599 times with 197 orders, B 315 times with 14, and C 86 times with 1.

Change the seed and the counts move. Across 200 seeds we tried, A got more than half the views in 199 runs. In the worst run it got 1,014.

Why can a flat start let a brand-new item beat a proven one in Thompson sampling?

A brand-new item wins because the picker above starts every item at Beta(1, 1): any order rate from 0 to 1 is equally likely. Real order rates sit near a few percent.

This matters when one picker compares new and proven restaurants together. The exploration slot above only compares new places, but a bandit over the whole feed would face exactly this.

Q wins about 95% of the time. The exact chance is 1 minus 21/402, or 0.948, and our 200,000-draw simulation gave 0.948. A flat start lands above 0.085 in about 91% of draws, while P lands there in about 1 draw in 200.

For pure exploration among new places, that is roughly what you want. But it means each new restaurant soaks up views until its range falls. With zero orders, it takes 19 views before its average estimate drops under 5%.

Start from the content model's guess

One fix joins the two ideas in this guide. A prior is the starting range a bandit uses before any data, and Beta(1, 1) is the flat one. Use the content model's predicted order rate as the prior instead.

Give the guess a weight, counted in views. A guess of 4% worth 25 views becomes 1 pretend order and 24 pretend misses. The tutorial's section on priors says a careful choice can improve learning.

Here is the picker with that change. Each candidate now also carries guess, the content model's predicted order rate.

python
import random

def pick_with_prior(candidates, weight=25):
    best, best_draw = None, -1.0
    for r in candidates:
        a0 = r.guess * weight            # r.guess: content model's order rate
        b0 = (1 - r.guess) * weight
        misses = r.shown - r.orders
        draw = random.betavariate(a0 + r.orders, b0 + misses)
        if draw > best_draw:
            best, best_draw = r, draw
    return best

With both restaurants on that start, Q beats P in about 31% of draws instead of 95%. Q still gets tried, but it no longer crowds out proven places.

We gave 20 new restaurants the same 4% guess, worth 25 views, with made-up true rates of 2%, 4% and 8%, over 2,000 slots and 200 runs. That shared guess matched their average rate, so this tests a light start, not per-restaurant predictions. It averaged about 117 orders against 107 for the flat start. The rates were made up, so treat the gap as a direction.

A wrong guess cuts the other way. The tutorial reports that a misspecified prior delays learning. Keep the weight small, so a few dozen real views can overrule the content model.

How do you measure whether your cold start fixes are working?

You measure cold start fixes by splitting every metric into groups: new versus returning users, new versus established restaurants, and new versus older cities. Overall averages hide cold start, because warm users usually far outnumber cold ones.

The team tracks a small set of numbers, each for a clear group:

  • First-order rate: the share of new users who order within 7 days of signing up.
  • Time to first order: how long a new user takes to place that first order.
  • Second-order rate: whether that first order was good enough to bring them back.
  • New restaurant coverage: the share of restaurants that joined this month and got at least 200 views in their first two weeks.
  • New restaurant quality: ratings and cancellations on orders that came through exploration slots.

Every change goes through an A/B test, where one group of users gets the change and a similar group does not. The test reports results for new users separately. A change can lift old users and quietly hurt new ones.

After six weeks, Bhubaneswar's numbers look much better. Feed visits ending in an order rose from 2 to 5 in 100. The Saheed Nagar restaurant got its first 40 orders through exploration slots and now ranks on its own merit.

Fair offline tests need a random slice

Offline tests on old logs are tricky here. The logs only show what the old feed chose to show, so a new restaurant it hid has no data at all.

A follow-up paper by largely the same authors gives a fix called replay. Fill one slot uniformly at random from the candidates for a small slice of traffic. Then walk through that log with the new ranker.

Keep an event only when the new ranker picks the same item the log shows, and ignore the rest. When logged picks are uniformly random and events independent, the paper proves the kept events match a live run in distribution. That proof assumes an endless log. For a fixed ranker and a finite log, the paper bounds the error, and the bound shrinks as the log grows.

The cost is data. With K candidates, only about 1 event in K matches, so the paper needs about K times T logged events to score T slots. Our test with 10 candidates kept 1,931 of 20,000 events.

How do you explain the cold start problem in a system design interview?

Explain it as three separate gaps, new users, new items and new markets, and give each one its own fix and its own metric.

  1. Name the three kinds

    Say which kinds of cold start this product has. A food app has all three, and a news site gets new items all day.

  2. Explain why the main model fails

    Collaborative filtering learns from interactions, so items and users without any have no useful embedding.

  3. Fix new users

    Popular by area and hour, a short onboarding, quick learning within the session, and a blend that shifts with history.

  4. Fix new items

    A content model for a first guess, plus a capped share of exploration chosen by a bandit that starts from that guess.

  5. Fix new markets

    Portable features, a shared model, local starter lists and a planned handover to local data.

  6. Measure by group

    Report first-order rate and new item coverage separately, and keep a small random slice for fair offline tests.

Questions people ask

What are the three types of cold start?
The usual split is three types: user cold start, item cold start and system cold start. User cold start is a new user with little or no history. Item cold start is a new product, restaurant or article with few or no interactions. System cold start is a new app or market where almost everyone and everything is new. Some surveys also count user-item cold start, where the user and the item are both new.
Is cold start in recommender systems the same as a serverless cold start?
No, they share a name but mean different things. A serverless cold start is the extra delay when the platform must set up a fresh environment for a function, for example on its first request. A recommender cold start is about missing data on users or items, not startup time.
Can popular items alone solve the cold start problem?
No, popular items are a good first answer but not a full one. They give new users safe picks, yet everyone nearby sees the same feed. They also feed popularity bias, because new items rarely or never get shown. Pair them with fast personal learning and exploration.
How many interactions does a user need before personal recommendations work?
There is no fixed number, so measure it on your own data. Plot recommendation quality against how many orders users have, and look for where personal picks start beating popular ones. Then set your blend so trust grows around that point, instead of a hard switch.
Do two-tower models help with the cold start problem?
Yes, when the towers use content features and not just IDs. A two-tower model turns users and items into embeddings with two separate networks. If the item tower reads cuisine, price and menu text, a brand-new item still gets an embedding, as good as those features allow.
Is exploration bad for users?
It can be, if it is uncapped or blind. Some users will see a new restaurant that turns out to be poor. Keep exploration to a small share of slots, prefer a bandit over random picks, and pull back quickly on bad ratings or cancellations.
Does cold start apply outside shopping and food apps?
Yes, any system that learns from behaviour faces it. Music apps get new songs, job sites get new postings, and news sites get new articles all day. The 2010 Yahoo! paper notes that news content changes often and many visitors arrive with no history.

Sources and further reading

Practise this with Lala

Practise