Building an Algorithmic Recommendation Feed for Mobile Apps
We develop algorithmic recommendation feeds for mobile apps that rank content in real time for each user. Unlike a chronological feed, our system uses a predicted engagement score and collects signals directly from the app—from video_completion_rate to swipe velocity. This increases engagement by 20–40% (based on our project experience). Over 5+ years, we have implemented such feeds for news, video, and e-commerce apps, accumulating engineering practices we share below. We guarantee stable operation even under peak loads.
How the Recommendation Feed Architecture Works
An algorithmic feed operates in two stages, and the mobile app critically depends on both.
Candidate generation—from millions of content items, we select a few hundred candidates for a specific user. This is usually a lightweight model (Approximate Nearest Neighbor on user embedding) or a set of rules: followed users, trending in geo, topic affinity. This stage must complete within 50–100ms.
Ranking—candidates are ranked by a heavy model that predicts interaction probability (like, share, comment, completion rate). Gradient boosted trees (XGBoost, LightGBM), two-tower neural networks, or DLRM. The result is an ordered list with scores.
Serving—the mobile app requests the next N feed items, receiving them with a precomputed order. Prefetch the next page before the user reaches the end of the current one.
Why Signal Quality Matters Most
The quality of the algorithm is determined by the quality of signals. The mobile app is the primary source. We collect explicit signals (like, share, comment) and implicit signals (completion rate, dwell time, swipe away velocity). Device context (time of day, network type, battery level) also improves ranking.
| Signal Type | Examples | Weight in Ranking |
|---|---|---|
| Explicit | like, share, comment | High |
| Implicit | completion rate, dwell time | Medium |
| Contextual | time of day, network type | Low (but improves) |
| Negative | skip < 0.5s, report | Very high |
Tracking video completion on iOS:
Example implementation on iOS
class VideoProgressTracker { private var timeObserver: Any? private let player: AVPlayer private let itemId: String private var maxProgress: Float = 0 init(player: AVPlayer, itemId: String) { self.player = player self.itemId = itemId setupObserver() } private func setupObserver() { let interval = CMTime(seconds: 0.5, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) timeObserver = player.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in guard let self, let duration = self.player.currentItem?.duration.seconds, duration > 0 else { return } let progress = Float(time.seconds / duration) if progress > self.maxProgress { self.maxProgress = progress } } } func reportCompletion() { Analytics.track(.videoProgress(itemId: itemId, completionRate: maxProgress, source: .algorithmicFeed)) } } On Android—use ExoPlayer with AnalyticsListener.onPlaybackStateChanged() and Player.Listener.onPositionDiscontinuity().
How to Implement Infinite Scroll Without Losing UX
The user should never see a loader while scrolling. The standard approach: preload the next page when the user reaches the second-to-last item on the current page. Prefetch with a threshold of 7–10 items for a video feed reduces loading time by a factor of 2–3 compared to no prefetch.
// Android, RecyclerView + ViewModel recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { val layoutManager = recyclerView.layoutManager as LinearLayoutManager val lastVisible = layoutManager.findLastVisibleItemPosition() val total = layoutManager.itemCount if (total - lastVisible <= PREFETCH_THRESHOLD) { viewModel.loadNextPage() } } }) PREFETCH_THRESHOLD is usually 3–5 items. For a video feed, we increase it to 7–10 because video loading takes longer. Deduplication: the server may return the same item in two paginated responses. The client stores a Set<String> of displayed IDs and filters out duplicates before adding to the list.
How We Test and Deploy
A new ranking model version is not rolled out to everyone at once. A typical scheme: 5% traffic → 20% → 50% → 100%, with monitoring of session metrics (retention D1/D7, average time in app, engagement rate) at each step. Feature flags are managed via Firebase Remote Config or an in-house system. The client sends experiment_variant in each feed API request—this allows the server to select the appropriate ranker.
What’s Included in the Work
- Audit of current tracking and analytics
- Designing event schema and feed architecture
- Developing the client side (iOS/Android) with prefetch, deduplication, tracking
- Integration with feed API (REST/GraphQL)
- Implementation of explanation labels and UI controls
- Setting up A/B testing and monitoring
- Documentation and code review
- Post-launch support (2 months)
Timeline Estimates
| Stage | Duration |
|---|---|
| Audit and design | 1–2 weeks |
| Client development | 2–3 weeks |
| Server ranker (optional) | 4–6 weeks |
| A/B testing | 2–3 weeks |
Cost is calculated individually, depending on content volume, required latency, and the presence of an existing analytics infrastructure.
Why Choose Us
We have 5+ years of experience in developing recommendation systems and have delivered over 30 projects in this area. Our engineers are proficient in the full stack: Swift, Kotlin, Flutter, Python, ML. We guarantee stable feed operation under high load and transparent reporting at every stage.
Contact us to discuss your task and get a preliminary budget estimate.







