Reach plc logo
Mobile

A reader community that unified 70+ titles into one feed and deepened engagement

Reach's comment threads were siloed per title and bouncing readers straight back out. I helped build a community app that pulled 70+ titles into one personalised feed with scalable moderation, turning scroll-and-bounce into readers who post, reply and return.

Reach plcFull-Stack DeveloperNov 2021 – Aug 2022
Download one-pager (PDF)
70+
titles unified
+41%
comments per user
2.3m
monthly active readers
4.6★
app-store rating

Problem

Reach plc, owner of the Daily Mirror, Daily Express, OK! and 70+ other titles, wanted a more engaged relationship with readers than scroll-and-bounce. Comment threads existed but were siloed per title and structured around the article, not the reader, so engagement leaked away the moment someone finished a piece.

The business goal was retention through participation: a forum-style app where readers discuss stories, follow other readers, and post their own takes across the whole portfolio in one feed, built for the volume and moderation reality of mass-market UK news.

Process

The pivotal decision was the feed. Early framing was a chronological cross-title stream; we reframed it around the reader, because a feed that surfaces what you care about across 70+ titles is what turns a one-off visit into a daily habit. That meant investing in a tunable ranking model rather than a simple merge: the difference between a novelty and something people return to.

I joined as a full-stack developer across the mobile (React Native) and web (React) builds on shared AWS services.

  1. The unified feed. Tens of thousands of daily items across 70+ titles ranked into a personalised ~50 per reader, by interests, topic and editorial weighting, with the API built tunable post-launch.
  2. The comment / post model. Richer than article comments, with replies, reactions, image uploads and mentions, but moderate-able at scale, via a hierarchical schema with moderation state on every record.
  3. Moderation workflow. Tabloid comment sections need aggressive moderation to survive. We built a queue with auto-flagging (profanity, link detection, repeat-offender heuristics) feeding a human review tool.

I also led a documentation-standards push: establishing a template, refactoring the top-traffic repos onto it, and writing the onboarding playbook to speed new developers up.

Outcome

Two production apps shipped, mobile (iOS + Android) and web, on one shared API, giving readers a place to engage that wasn't tied to a single article and turning passive scrolling into posting, replying and returning. The personalised cross-title feed unified 70+ titles into one experience. The documentation standards I set were carried forward across other Reach teams.

"It gave our readers somewhere to actually talk, across all our titles at once. Engagement stopped ending at the bottom of the article."

Product Manager, Reach

Architecture

Reach plc architecture diagram
For engineersTechnical Deep Dive
Expand

The unified feed

Each title publishes hundreds of articles a day. Across 70+ titles that's tens of thousands of items daily, from which a reader's feed needs ~50.

CMS publishes article → SNS (per-title topic) Lambda ingests → enriches with topic embeddings DynamoDB articles table (PK: ARTICLE#<id>, GSIs by topic, by title, by recency) Feed-builder Lambda runs per user request: ├─ Pull user's interests (topics, followed users, source titles) ├─ Score-and-rank candidate articles (recency × interest × editorial) └─ Cache result for 60s in DynamoDB (PK: USER#<id>#FEED, TTL)

Editorial weight was a per-article number set by Reach's curation team, a way to surface a breaking-news story in everyone's feed without polluting the personalisation logic.

Comments and posts

Both used the same hierarchical record:

type Post = { id: string; // POST#<uuid> parentId?: string; // POST#<uuid> for replies, ARTICLE#<id> for top-level authorId: string; body: string; attachments?: Attachment[]; reactions: Record<string, number>; state: "live" | "pending" | "hidden" | "removed"; createdAt: string; flaggedAt?: string; flaggedReason?: string; };

DynamoDB layout: PK = PARENT#<parentId>, SK = POST#<createdAt>#<id> for fast in-thread reads in chronological order. The same record served article comments and forum posts. The only difference was the parent type.

Moderation workflow

Auto-flag rules ran inline on submission and bumped state to "pending" when triggered. Human moderators worked a queue ordered by flag severity. Repeat offenders' future posts went straight to "pending" for a configurable cooldown.

We kept the auto-flag rules simple, heuristics rather than ML, because false positives in news comments carry an editorial cost. Moderation is a human-in-the-loop problem, not an automation problem.

Trade-offs

  • No real-time push. We polled rather than streamed comment updates. Real-time felt aspirational; the observed behaviour was that readers refreshed when they wanted new replies.
  • Single API for mobile and web. Tempting to fork and optimise per platform, but the shared API kept feature parity tight and let small front-end teams ship fast. The cost was occasional over-fetching that a field-selection layer would have avoided.