DEV Community

Cover image for Building a Recommendation Engine
Derek mwale
Derek mwale

Posted on

Building a Recommendation Engine

Building a Recommendation Engine

One of my favorite moments as a software engineer wasn't when I deployed a new API or optimized a database query.

It was the first time I built a system that made a decision on its own.

Not a complicated AI model.

Not a large language model.

Just a simple recommendation engine.

A user opened an application, and instead of showing random content, the system suggested something they were actually interested in.

It wasn't magic.

It was software engineering.

The more I learned about recommendation systems, the more I realized they aren't just about recommending movies or products.

They're about solving a much bigger problem.

Helping users discover value without searching endlessly.

Whether you're building Netflix, Spotify, Amazon, YouTube, or even a small travel website, the challenge is remarkably similar.

You have thousands of possible choices.

The user only wants a few.

How do you decide which ones to show?

Let's build a simple recommendation engine from scratch and explore the system design, algorithms, data structures, design patterns, and implementation that make it work.


The Problem

Imagine you're building an online bookstore.

The store has:

  • 500,000 books
  • 200,000 users
  • Thousands of new books every month

When a user logs in, showing every book is impossible.

Sorting by newest isn't useful.

Sorting alphabetically isn't useful.

Showing random books certainly isn't useful.

The application needs to answer one question.

What should this particular user see first?

That's the recommendation problem.


Step One: Understanding the User

Every recommendation engine begins with data.

Without data, recommendations become guesses.

Suppose we collect:

  • Books viewed
  • Books purchased
  • Categories explored
  • Ratings
  • Time spent reading descriptions
  • Search history
  • Wishlist items

Instead of storing random events, we organize them.

User
 ├── Purchases
 ├── Views
 ├── Ratings
 ├── Searches
 └── Favorite Genres
Enter fullscreen mode Exit fullscreen mode

Every interaction teaches the system something.

Viewed ten programming books?

Probably interested in programming.

Purchased three Rust books?

Recommend another Rust book.

Skipped every romance novel?

Maybe stop recommending romance.

Recommendations begin by listening.


Designing the System

A recommendation engine should not be mixed into the product service.

Instead, separate responsibilities.

                API Gateway
                     │
      ┌──────────────┴─────────────┐
      │                            │
 Product Service        Recommendation Service
      │                            │
      │                     User Behavior DB
      │                            │
 Product Database          Recommendation Cache
Enter fullscreen mode Exit fullscreen mode

The Product Service stores products.

The Recommendation Service decides what users should see.

This separation keeps both services simple.


Choosing the Right Design Pattern

One recommendation strategy rarely fits everyone.

Sometimes popularity matters.

Sometimes user history matters.

Sometimes promotions matter.

Instead of hardcoding everything, use the Strategy Pattern.

RecommendationStrategy

PopularStrategy

SimilarityStrategy

CategoryStrategy

TrendingStrategy
Enter fullscreen mode Exit fullscreen mode

Now changing recommendation logic becomes easy.

The engine simply swaps strategies.


Data Structures Matter

Efficient recommendations depend on efficient storage.

For user history:

HashMap<UserID, UserProfile>
Enter fullscreen mode Exit fullscreen mode

Fast lookups.

For product categories:

HashMap<Category, List<Product>>
Enter fullscreen mode Exit fullscreen mode

Finding related books becomes instant.

For rankings:

Use a Priority Queue (Heap).

The highest score stays at the top.

Perfect for "Top Recommendations."


Our Simple Recommendation Algorithm

Let's avoid machine learning.

Instead, build a scoring engine.

Every product receives points.

Suppose:

Viewed category before

+20

Purchased similar books

+30

Recently searched keyword

+15

Highly rated

+10

Trending

+5

Already purchased

−100

Now calculate:

Score =
Category +
Purchase +
Search +
Rating +
Popularity
Enter fullscreen mode Exit fullscreen mode

The highest score wins.

Simple.

Understandable.

Fast.


Example

User:

Purchased

  • Rust Programming

Viewed

  • Backend Engineering

Searched

  • APIs

Available books

Book Score
Advanced Rust 60
API Design 55
Cooking Basics 2
Gardening 1

The recommendation engine immediately understands the user's interests.


Implementation in Rust

Create a product.

struct Product {
    id: u32,
    title: String,
    category: String,
}
Enter fullscreen mode Exit fullscreen mode

User profile.

struct UserProfile {
    favorite_categories: Vec<String>,
}
Enter fullscreen mode Exit fullscreen mode

Recommendation function.

fn score(product: &Product, user: &UserProfile) -> i32 {

    if user.favorite_categories.contains(&product.category) {
        20
    } else {
        0
    }

}
Enter fullscreen mode Exit fullscreen mode

Now rank products.

products.sort_by_key(|p| -score(p, &user));
Enter fullscreen mode Exit fullscreen mode

Simple.

Readable.

Maintainable.


But There's a Problem

Every user receives identical category recommendations.

Suppose someone likes Rust.

Eventually they purchase every Rust book.

Continuing to recommend Rust books forever becomes repetitive.

We need diversity.


Introducing Recommendation Fatigue

Humans enjoy familiarity.

They also enjoy discovery.

Too much familiarity becomes boring.

Too much randomness becomes confusing.

The recommendation engine should balance both.

Instead of returning:

Programming

Programming

Programming

Programming

Return:

Programming

Backend

Cloud Computing

Algorithms

The user remains engaged.


Diversity Algorithm

After selecting one recommendation from a category,

reduce that category's score slightly.

Programming

40

↓

30

↓

20

↓

10
Enter fullscreen mode Exit fullscreen mode

Now other categories naturally rise.

This produces healthier recommendations.


Cold Start Problem

What about new users?

No history.

No purchases.

No ratings.

No searches.

Traditional recommendation engines struggle here.

A simple solution:

Show

Trending

Popular

Recently added

Editor's picks

Once the user begins interacting,

gradually personalize recommendations.

Learning begins immediately.


Caching Recommendations

Computing recommendations every request wastes CPU.

Instead:

User logs in.

Generate recommendations.

Store in Redis.

recommendations:user:120
Enter fullscreen mode Exit fullscreen mode

Next request?

Retrieve instantly.

Fast.

Scalable.


Event-Driven Updates

Suppose the user purchases another Rust book.

The cache becomes outdated.

Instead of rebuilding continuously,

publish an event.

Book Purchased

↓

Message Queue

↓

Recommendation Service

↓

Recalculate
Enter fullscreen mode Exit fullscreen mode

Now recommendations stay fresh without slowing purchases.


Scaling to Millions of Users

Eventually:

20 million users.

100 million products.

Recommendations cannot be computed sequentially.

Split work.

Worker 1

Users 1–100,000

Worker 2

Users 100,001–200,000

Worker 3

Users 200,001–300,000
Enter fullscreen mode Exit fullscreen mode

Each worker computes independently.

Horizontal scaling becomes easy.


Monitoring Quality

Fast recommendations aren't enough.

They must also be useful.

Track:

Click-through rate.

Purchase rate.

Average recommendation score.

Ignored recommendations.

Repeated recommendations.

If nobody clicks,

the algorithm needs improvement.

Metrics guide evolution.


Improving the Algorithm

Eventually scoring becomes insufficient.

We can introduce:

Weighted history.

Recent activity matters more.

Collaborative filtering.

People with similar interests recommend to one another.

Content similarity.

Books sharing authors, tags, or topics.

Hybrid recommendations.

Combine multiple strategies.

The architecture doesn't change.

Only the strategy changes.

Good design welcomes improvement.


Time Complexity

Suppose:

100,000 products.

Scoring:

O(n)

Sorting:

O(n log n)

Need only top 10?

Use a heap.

Complexity becomes:

O(n log k)

Where

k = 10

Much faster.

Algorithm selection matters.


Reliability

Recommendation engines should never stop the application.

If the recommendation service fails,

show:

Popular products.

Recently added.

Trending.

Graceful degradation keeps users happy.


Lessons Learned

Building a recommendation engine taught me something that extends far beyond personalized suggestions.

The best software doesn't simply respond.

It anticipates.

It learns.

It adapts.

The recommendation engine isn't replacing human choice.

It's reducing unnecessary effort.

Helping users discover something valuable sooner.

That's the essence of good software engineering.


Final Thoughts

When most people hear the phrase recommendation engine, they imagine artificial intelligence, neural networks, or massive data science teams.

While those technologies certainly have their place, every great recommendation system starts with something much simpler: understanding the problem.

Who is the user?

What do they like?

What have they interacted with?

What patterns exist?

What information can help them make a better decision?

From there, everything becomes an engineering exercise.

Designing clean services.

Choosing efficient data structures.

Selecting appropriate algorithms.

Keeping responsibilities separated.

Planning for scalability.

Preparing for failure.

Measuring outcomes.

Iterating continuously.

Over time, the recommendation engine grows from a simple scoring function into an intelligent platform capable of serving millions of users.

But its foundation rarely changes.

Collect meaningful data.

Organize it well.

Score candidates intelligently.

Continuously learn from user behavior.

Improve without adding unnecessary complexity.

The longer I build backend systems, the more I appreciate projects like recommendation engines because they sit at the intersection of software engineering and human behavior.

You're not just optimizing algorithms.

You're helping people discover books they'll enjoy, songs they'll replay, places they'll visit, or products they'll genuinely need.

And perhaps that's what makes recommendation engines so fascinating.

Behind every suggestion is not just an algorithm.

Behind every suggestion is a carefully designed system making thousands of small engineering decisions—all working together to answer one deceptively simple question:

"What should this user see next?"

Top comments (0)